pax_global_header00006660000000000000000000000064136110477040014515gustar00rootroot0000000000000052 comment=2d60cb19a29084ac350194c38753cb1963749ecb swami-2.2.0/000077500000000000000000000000001361104770400126365ustar00rootroot00000000000000swami-2.2.0/ABOUT-NLS000066400000000000000000001131131361104770400140650ustar00rootroot00000000000000Notes on the Free Translation Project ************************************* Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do _not_ need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work at translations should contact the appropriate team. When reporting bugs in the `intl/' directory or bugs which may be related to internationalization, you should tell about the version of `gettext' which is used. The information can be found in the `intl/VERSION' file, in internationalized packages. Quick configuration advice ========================== If you want to exploit the full power of internationalization, you should configure it using ./configure --with-included-gettext to force usage of internationalizing routines provided within this package, despite the existence of internationalizing capabilities in the operating system where this package is being installed. So far, only the `gettext' implementation in the GNU C library version 2 provides as many features (such as locale alias, message inheritance, automatic charset conversion or plural form handling) as the implementation here. It is also not possible to offer this additional functionality on top of a `catgets' implementation. Future versions of GNU `gettext' will very likely convey even more functionality. So it might be a good idea to change to GNU `gettext' as soon as possible. So you need _not_ provide this option if you are using GNU libc 2 or you have installed a recent copy of the GNU gettext package with the included `libintl'. INSTALL Matters =============== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system already provides the GNU `gettext' functions. If not, the GNU `gettext' own library will be used. This library is wholly contained within this package, usually in the `intl/' subdirectory, so prior installation of the GNU `gettext' package is _not_ required. Installers may use special options at configuration time for changing the default behaviour. The commands: ./configure --with-included-gettext ./configure --disable-nls will respectively bypass any pre-existing `gettext' to use the internationalizing routines provided within this package, or else, _totally_ disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl.a' file and will decide to use this. This might be not what is desirable. You should use the more recent version of the GNU `gettext' library. I.e. if the file `intl/VERSION' shows that the library which comes with this package is more recent, you should use ./configure --with-included-gettext to prevent auto-detection. The configuration process will not test for the `catgets' function and therefore it will not be used. The reason is that even an emulation of `gettext' on top of `catgets' could not provide all the extensions of the GNU `gettext' library. Internationalized packages have usually many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. Using This Package ================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate `LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, and `CC' is an ISO 3166 two-letter country code. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute `setenv LANG de_DE' (in `csh'), `export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The country code serves to distinguish the dialects. The locale naming convention of `LL_CC', with `LL' denoting the language and `CC' denoting the country, is the one use on systems based on GNU libc. On other systems, some variations of this scheme are used, such as `LL' or `LL_CC.ENCODING'. You can get the list of locales supported by your system for your country by running the command `locale -a | grep '^LL''. Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called `LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' for the purpose of message handling, but you still need to have `LANG' set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. In the `LANGUAGE' environment variable, but not in the `LANG' environment variable, `LL_CC' combinations can be abbreviated as `LL' to denote the language's main dialect. For example, `de' is equivalent to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' (Portuguese as spoken in Portugal) in this context. Translating Teams ================= For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list. The up-to-date list of teams can be found at the Free Translation Project's homepage, `http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams" area. If you'd like to volunteer to _work_ at translating messages, you should become a member of the translating team for your own language. The subscribing address is _not_ the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate _actively_ in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `translation@iro.umontreal.ca' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skill are praised more than programming skill, here. Available Packages ================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of May 2003. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination, with a translation percentage of at least 50%. Ready PO files am az be bg ca cs da de el en en_GB eo es +-------------------------------------------+ a2ps | [] [] [] [] | aegis | () | anubis | | ap-utils | | bash | [] [] [] | batchelor | | bfd | [] [] | binutils | [] [] | bison | [] [] [] | bluez-pin | [] [] | clisp | | clisp | [] [] [] | coreutils | [] [] [] [] | cpio | [] [] [] | darkstat | () [] | diffutils | [] [] [] [] [] [] [] | e2fsprogs | [] [] | enscript | [] [] [] [] | error | [] [] [] [] [] | fetchmail | [] () [] [] [] [] | fileutils | [] [] [] | findutils | [] [] [] [] [] [] | flex | [] [] [] [] | gas | [] | gawk | [] [] [] [] | gcal | [] | gcc | [] [] | gettext | [] [] [] [] [] | gettext-runtime | [] [] [] [] [] | gettext-tools | [] [] | gimp-print | [] [] [] [] [] | gliv | | glunarclock | [] [] [] | gnucash | () [] | gnucash-glossary | [] () [] | gnupg | [] () [] [] [] [] | gpe-calendar | [] | gpe-conf | [] | gpe-contacts | [] | gpe-edit | | gpe-login | [] | gpe-ownerinfo | [] | gpe-sketchbook | [] | gpe-timesheet | | gpe-today | [] | gpe-todo | [] | gphoto2 | [] [] [] [] | gprof | [] [] | gpsdrive | () () () | grep | [] [] [] [] [] | gretl | [] | hello | [] [] [] [] [] [] | id-utils | [] [] | indent | [] [] [] [] | jpilot | [] [] [] [] | jwhois | [] | kbd | [] [] [] [] [] | ld | [] [] | libc | [] [] [] [] [] [] | libgpewidget | [] | libiconv | [] [] [] [] [] | lifelines | [] () | lilypond | [] | lingoteach | | lingoteach_lessons | () () | lynx | [] [] [] [] | m4 | [] [] [] [] | mailutils | [] [] | make | [] [] [] | man-db | [] () [] [] () | mysecretdiary | [] [] [] | nano | [] () [] [] [] | nano_1_0 | [] () [] [] [] | opcodes | [] [] | parted | [] [] [] [] [] | ptx | [] [] [] [] [] | python | | radius | | recode | [] [] [] [] [] [] | screem | | sed | [] [] [] [] [] | sh-utils | [] [] [] | sharutils | [] [] [] [] [] [] | sketch | [] () [] | soundtracker | [] [] [] | sp | [] | tar | [] [] [] [] | texinfo | [] [] [] [] | textutils | [] [] [] [] | tin | () () | util-linux | [] [] [] [] [] | vorbis-tools | [] [] [] | wastesedge | () | wdiff | [] [] [] [] | wget | [] [] [] [] [] [] [] | xchat | [] [] [] | xpad | | +-------------------------------------------+ am az be bg ca cs da de el en en_GB eo es 0 1 4 2 31 17 54 60 14 1 4 12 56 et fa fi fr ga gl he hr hu id it ja ko +----------------------------------------+ a2ps | [] [] [] () () | aegis | | anubis | [] | ap-utils | [] | bash | [] [] | batchelor | [] | bfd | [] [] | binutils | [] [] | bison | [] [] [] [] | bluez-pin | [] [] [] [] | clisp | | clisp | [] | coreutils | [] [] [] [] | cpio | [] [] [] [] | darkstat | () [] [] [] | diffutils | [] [] [] [] [] [] [] | e2fsprogs | | enscript | [] [] | error | [] [] [] [] | fetchmail | [] | fileutils | [] [] [] [] [] | findutils | [] [] [] [] [] [] [] [] [] [] [] | flex | [] [] | gas | [] | gawk | [] [] | gcal | [] | gcc | [] | gettext | [] [] [] | gettext-runtime | [] [] [] [] | gettext-tools | [] | gimp-print | [] [] | gliv | () | glunarclock | [] [] [] [] | gnucash | [] | gnucash-glossary | [] | gnupg | [] [] [] [] [] [] [] | gpe-calendar | [] | gpe-conf | | gpe-contacts | [] | gpe-edit | [] [] | gpe-login | [] | gpe-ownerinfo | [] [] [] | gpe-sketchbook | [] | gpe-timesheet | [] [] [] | gpe-today | [] [] | gpe-todo | [] [] | gphoto2 | [] [] [] | gprof | [] [] | gpsdrive | () [] () () | grep | [] [] [] [] [] [] [] [] [] [] [] | gretl | [] | hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | id-utils | [] [] [] | indent | [] [] [] [] [] [] [] [] | jpilot | [] () | jwhois | [] [] [] [] | kbd | [] | ld | [] | libc | [] [] [] [] [] [] | libgpewidget | [] [] [] | libiconv | [] [] [] [] [] [] [] [] | lifelines | () | lilypond | [] | lingoteach | [] [] | lingoteach_lessons | | lynx | [] [] [] [] | m4 | [] [] [] [] | mailutils | | make | [] [] [] [] [] [] | man-db | [] () () | mysecretdiary | [] [] | nano | [] [] [] [] | nano_1_0 | [] [] [] [] | opcodes | [] [] | parted | [] [] [] | ptx | [] [] [] [] [] [] [] | python | | radius | | recode | [] [] [] [] [] [] | screem | | sed | [] [] [] [] [] [] [] [] | sh-utils | [] [] [] [] [] [] | sharutils | [] [] [] [] [] | sketch | [] | soundtracker | [] [] [] | sp | [] () | tar | [] [] [] [] [] [] [] [] [] | texinfo | [] [] [] [] | textutils | [] [] [] [] [] | tin | [] () | util-linux | [] [] [] [] () [] | vorbis-tools | [] | wastesedge | () | wdiff | [] [] [] [] [] | wget | [] [] [] [] [] [] [] [] | xchat | [] [] [] | xpad | | +----------------------------------------+ et fa fi fr ga gl he hr hu id it ja ko 20 1 15 73 14 24 8 10 30 31 19 31 9 lg lt lv ms nb nl nn no pl pt pt_BR ro +----------------------------------------+ a2ps | [] [] () () () [] [] | aegis | () | anubis | [] [] | ap-utils | () | bash | [] | batchelor | | bfd | | binutils | | bison | [] [] [] [] | bluez-pin | [] | clisp | | clisp | [] | coreutils | [] | cpio | [] [] [] | darkstat | [] [] [] [] | diffutils | [] [] [] | e2fsprogs | | enscript | [] [] | error | [] [] | fetchmail | () () | fileutils | [] | findutils | [] [] [] [] | flex | [] | gas | | gawk | [] | gcal | | gcc | | gettext | [] | gettext-runtime | [] | gettext-tools | | gimp-print | [] | gliv | [] | glunarclock | [] | gnucash | | gnucash-glossary | [] [] | gnupg | | gpe-calendar | [] [] | gpe-conf | [] [] | gpe-contacts | [] | gpe-edit | [] [] | gpe-login | [] [] | gpe-ownerinfo | [] [] | gpe-sketchbook | [] [] | gpe-timesheet | [] [] | gpe-today | [] [] | gpe-todo | [] [] | gphoto2 | | gprof | [] | gpsdrive | () () () | grep | [] [] [] [] | gretl | | hello | [] [] [] [] [] [] [] [] [] | id-utils | [] [] [] | indent | [] [] [] | jpilot | () () | jwhois | [] [] [] | kbd | | ld | | libc | [] [] [] [] | libgpewidget | [] [] | libiconv | [] [] | lifelines | | lilypond | [] | lingoteach | | lingoteach_lessons | | lynx | [] [] | m4 | [] [] [] [] | mailutils | | make | [] [] | man-db | [] | mysecretdiary | [] | nano | [] [] [] [] | nano_1_0 | [] [] [] [] | opcodes | [] [] [] | parted | [] [] [] | ptx | [] [] [] [] [] [] [] | python | | radius | | recode | [] [] [] | screem | | sed | [] [] | sh-utils | [] | sharutils | [] | sketch | [] | soundtracker | | sp | | tar | [] [] [] [] [] [] | texinfo | [] | textutils | [] | tin | | util-linux | [] [] | vorbis-tools | [] [] | wastesedge | | wdiff | [] [] [] [] | wget | [] [] [] | xchat | [] [] | xpad | [] | +----------------------------------------+ lg lt lv ms nb nl nn no pl pt pt_BR ro 0 0 2 11 7 26 3 4 18 15 34 34 ru sk sl sr sv ta tr uk vi wa zh_CN zh_TW +-------------------------------------------+ a2ps | [] [] [] [] [] | 16 aegis | () | 0 anubis | [] [] | 5 ap-utils | () | 1 bash | [] | 7 batchelor | | 1 bfd | [] [] [] | 7 binutils | [] [] [] | 7 bison | [] [] | 13 bluez-pin | | 7 clisp | | 0 clisp | | 5 coreutils | [] [] [] [] [] | 14 cpio | [] [] [] | 13 darkstat | [] () () | 9 diffutils | [] [] [] [] | 21 e2fsprogs | [] | 3 enscript | [] [] [] | 11 error | [] [] [] | 14 fetchmail | [] | 7 fileutils | [] [] [] [] [] [] | 15 findutils | [] [] [] [] [] [] | 27 flex | [] [] [] | 10 gas | [] | 3 gawk | [] [] | 9 gcal | [] [] | 4 gcc | [] | 4 gettext | [] [] [] [] [] [] | 15 gettext-runtime | [] [] [] [] [] [] | 16 gettext-tools | [] [] | 5 gimp-print | [] [] | 10 gliv | | 1 glunarclock | [] [] [] | 11 gnucash | [] [] | 4 gnucash-glossary | [] [] [] | 8 gnupg | [] [] [] [] | 16 gpe-calendar | [] | 5 gpe-conf | | 3 gpe-contacts | [] | 4 gpe-edit | [] | 5 gpe-login | [] | 5 gpe-ownerinfo | [] | 7 gpe-sketchbook | [] | 5 gpe-timesheet | [] | 6 gpe-today | [] | 6 gpe-todo | [] | 6 gphoto2 | [] [] | 9 gprof | [] [] | 7 gpsdrive | [] [] | 3 grep | [] [] [] [] | 24 gretl | | 2 hello | [] [] [] [] [] | 33 id-utils | [] [] [] | 11 indent | [] [] [] [] | 19 jpilot | [] [] [] [] [] | 10 jwhois | () () [] [] | 10 kbd | [] [] | 8 ld | [] [] | 5 libc | [] [] [] [] | 20 libgpewidget | | 6 libiconv | [] [] [] [] [] [] | 21 lifelines | [] | 2 lilypond | [] | 4 lingoteach | | 2 lingoteach_lessons | () | 0 lynx | [] [] [] [] | 14 m4 | [] [] [] | 15 mailutils | | 2 make | [] [] [] [] | 15 man-db | [] | 6 mysecretdiary | [] [] | 8 nano | [] [] [] | 15 nano_1_0 | [] [] [] | 15 opcodes | [] [] | 9 parted | [] [] | 13 ptx | [] [] [] | 22 python | | 0 radius | | 0 recode | [] [] [] [] | 19 screem | [] | 1 sed | [] [] [] [] [] | 20 sh-utils | [] [] [] | 13 sharutils | [] [] [] [] | 16 sketch | [] | 5 soundtracker | [] | 7 sp | [] | 3 tar | [] [] [] [] [] | 24 texinfo | [] [] [] [] | 13 textutils | [] [] [] [] [] | 15 tin | | 1 util-linux | [] [] | 14 vorbis-tools | [] | 7 wastesedge | | 0 wdiff | [] [] [] [] | 17 wget | [] [] [] [] [] [] [] | 25 xchat | [] [] [] | 11 xpad | | 1 +-------------------------------------------+ 50 teams ru sk sl sr sv ta tr uk vi wa zh_CN zh_TW 97 domains 32 19 16 0 56 0 48 10 1 1 12 23 913 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If May 2003 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix with full percentage details can be found at `http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'. Using `gettext' in new packages =============================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course you have to respect the GNU Library General Public License which covers the use of the GNU `gettext' library. This means in particular that even non-free programs can use `libintl' as a shared library, whereas only free software can use `libintl' as a static library or use modified versions of `libintl'. Once the sources are changed appropriately and the setup can handle the use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `translation@iro.umontreal.ca' to make the `.pot' files available to the translation teams. swami-2.2.0/AUTHORS000066400000000000000000000006171361104770400137120ustar00rootroot00000000000000Element Green Contributors: BALATON Zoltan for Spectralis support, Mac OS X integration and MAC builds. Luis Garrido for the first auto loop finder algorithm (was completely rewritten since then). Ebrahim Mayat for Mac OS X testing, support and build HOWTO documentation Keishi Suenaga for Win32 patches and build HOWTO documentation swami-2.2.0/CMakeLists.txt000066400000000000000000000210341361104770400153760ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # project ( Swami C ) cmake_minimum_required ( VERSION 2.6.3 ) set ( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ) # Swami package name set ( PACKAGE "swami" ) # Swami package version set ( SWAMI_VERSION_MAJOR 2 ) set ( SWAMI_VERSION_MINOR 2 ) set ( SWAMI_VERSION_MICRO 0 ) set ( VERSION "${SWAMI_VERSION_MAJOR}.${SWAMI_VERSION_MINOR}.${SWAMI_VERSION_MICRO}" ) set ( SWAMI_VERSION "\"${VERSION}\"" ) # libswami/libswamigui - Library versions # *** NOTICE *** # Update library version upon each release (follow these steps in order) # if any source code changes: REVISION++ # if any interfaces added/removed/changed: REVISION=0 # if any interfaces removed/changed (compatibility broken): CURRENT++ # if any interfaces have been added: AGE++ # if any interfaces have been removed/changed (compatibility broken): AGE=0 # This is not exactly the same algorithm as the libtool one, but the results are the same. set ( LIB_VERSION_CURRENT 1 ) set ( LIB_VERSION_AGE 0 ) set ( LIB_VERSION_REVISION 0 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) # Options disabled by default option ( enable-debug "enable debugging (default=no)" off ) option ( enable-source-build "enable source build - load resources from source dir (default=no)" off ) option ( GTKDOC_ENABLED "Create Gtk-Doc API reference (default=no)" off ) # Options enabled by default option ( BUILD_SHARED_LIBS "Build a shared object or DLL (default=yes)" on ) option ( enable-fluidsynth "enable FluidSynth plugin - needed for sound (if it is available)" on ) option ( enable-fftw "enable fftw support for the FFTune plugin (if it is available)" on ) # Default install directory names include ( DefaultDirs ) include ( GNUInstallDirs ) # Basic C library checks include ( CheckSTDC ) include ( CheckIncludeFile ) check_include_file ( string.h HAVE_STRING_H ) check_include_file ( stdlib.h HAVE_STDLIB_H ) check_include_file ( stdio.h HAVE_STDIO_H ) check_include_file ( math.h HAVE_MATH_H ) check_include_file ( errno.h HAVE_ERRNO_H ) check_include_file ( stdarg.h HAVE_STDARG_H ) check_include_file ( unistd.h HAVE_UNISTD_H ) unset ( SWAMI_LIBS CACHE ) # Options for the GNU C compiler only if ( CMAKE_COMPILER_IS_GNUCC ) if ( NOT APPLE ) set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed" ) set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" ) endif ( NOT APPLE ) set ( GNUCC_WARNING_FLAGS "-Wall") set ( CMAKE_C_FLAGS_DEBUG "-g -DDEBUG ${GNUCC_WARNING_FLAGS}" ) set ( CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG ${GNUCC_WARNING_FLAGS}" ) set ( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG ${GNUCC_WARNING_FLAGS}" ) endif ( CMAKE_COMPILER_IS_GNUCC ) if ( enable-debug ) set ( CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo" FORCE ) else ( enable-debug ) set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo" FORCE ) endif ( enable-debug ) unset ( MINGW32 CACHE ) if ( WIN32 ) # MinGW compiler (a Windows GCC port) if ( MINGW ) set ( MINGW32 1 ) add_definitions ( -mms-bitfields ) endif ( MINGW ) else ( WIN32 ) set ( SWAMI_LIBS "m" ) endif ( WIN32 ) unset ( DEBUG CACHE ) if ( CMAKE_BUILD_TYPE MATCHES "Debug" ) set ( DEBUG 1 ) endif ( CMAKE_BUILD_TYPE MATCHES "Debug" ) unset ( SOURCE_BUILD CACHE ) unset ( SOURCE_DIR CACHE ) if ( enable-source-build ) set ( SOURCE_BUILD 1 ) set ( SOURCE_DIR ${CMAKE_SOURCE_DIR} ) set ( PLUGINS_DIR ${CMAKE_BINARY_DIR}/src/plugins ) endif ( enable-source-build ) # Mandatory tool: pkg-config find_package ( PkgConfig REQUIRED ) # Mandatory library libinstpatch pkg_check_modules ( LIBINSTPATCH REQUIRED libinstpatch-1.0>=1.1 ) # Mandatory libraries: GTK+ and libgnomecanvas pkg_check_modules ( GUI REQUIRED gtk+-2.0>=2.12 libgnomecanvas-2.0>=2.0 ) # Mandatory libraries: gobject, glib, gmodule and gthread pkg_check_modules ( GOBJECT REQUIRED gobject-2.0>=2.12 glib-2.0>=2.12 gmodule-2.0>=2.12 gthread-2.0>=2.12 ) # Disable deprecation warnings for now (fixed in master) add_definitions ( -DGLIB_DISABLE_DEPRECATION_WARNINGS ) # Needed on OS X if (APPLE) find_library ( COREFOUNDATION NAMES CoreFoundation ) mark_as_advanced ( COREFOUNDATION ) endif (APPLE) include ( UnsetPkgConfig ) # Optional library FluidSynth unset ( FLUIDSYNTH_SUPPORT CACHE ) if ( enable-fluidsynth ) pkg_check_modules ( FLUIDSYNTH fluidsynth>=2.0 ) set ( FLUIDSYNTH_SUPPORT ${FLUIDSYNTH_FOUND} ) else ( enable-fluidsynth ) unset_pkg_config ( FLUIDSYNTH ) endif ( enable-fluidsynth ) # Optional library fftw3 unset ( FFTW_SUPPORT CACHE ) if ( enable-fftw ) pkg_check_modules ( FFTW fftw3f>=3.0 ) set ( FFTW_SUPPORT ${FFTW_FOUND} ) else ( enable-fftw ) unset_pkg_config ( FFTW ) endif ( enable-fftw ) # Check for Gtk-Doc if (GTKDOC_ENABLED) include (FindGtkDoc) endif () # General configuration file configure_file ( ${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) add_definitions ( -DHAVE_CONFIG_H ) # Process subdirectories add_subdirectory ( src ) add_subdirectory ( docs ) # Extra targets for Unix build environments if ( UNIX ) # uninstall custom target configure_file ( "${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target ( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") # Install XDG mime type, application icon and .desktop file install ( FILES swami.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications ) install ( FILES swami.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages ) install ( FILES swami.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps ) install ( FILES swami.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps ) endif ( UNIX ) # Extra targets for Windows if ( WIN32 ) install ( FILES swami.ico DESTINATION ${CMAKE_INSTALL_BINDIR} ) endif ( WIN32 ) message( "\n**************************************************************\n" ) if ( FLUIDSYNTH_SUPPORT ) message ( "FluidSynth: yes" ) else ( FLUIDSYNTH_SUPPORT ) message ( "FluidSynth: no (there will be no sound!)" ) endif ( FLUIDSYNTH_SUPPORT ) if ( FFTW_SUPPORT ) message ( "FFTW: yes" ) else ( FFTW_SUPPORT ) message ( "FFTW: no (there will be no FFTune plugin!)" ) endif ( FFTW_SUPPORT ) if (GTKDOC_FOUND) message ( "Gtk-Doc API reference: yes" ) else (GTKDOC_FOUND) message ( "Gtk-Doc API reference: no" ) endif(GTKDOC_FOUND) if ( DEBUG ) message ( "Debug: yes" ) else ( DEBUG ) message ( "Debug: no" ) endif ( DEBUG ) if ( SOURCE_BUILD ) message ( "Source build: yes (resources loaded from source dir)" ) else ( SOURCE_BUILD ) message ( "Source build: no" ) endif ( SOURCE_BUILD ) message ( "**************************************************************\n\n" ) # CPack support set ( CPACK_PACKAGE_DESCRIPTION_SUMMARY "Swami instrument editor" ) set ( CPACK_PACKAGE_VENDOR "swami.sourceforge.net" ) set ( CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md" ) set ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING" ) set ( CPACK_PACKAGE_VERSION_MAJOR ${SWAMI_VERSION_MAJOR} ) set ( CPACK_PACKAGE_VERSION_MINOR ${SWAMI_VERSION_MINOR} ) set ( CPACK_PACKAGE_VERSION_PATCH ${SWAMI_VERSION_MICRO} ) # source packages set ( CPACK_SOURCE_GENERATOR TGZ;TBZ2;ZIP ) set ( CPACK_SOURCE_IGNORE_FILES "/.svn/;~$;.cproject;.project;/.settings/;${CPACK_SOURCE_IGNORE_FILES}" ) set ( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" ) set ( CPACK_SOURCE_STRIP_FILES OFF ) # binary packages include ( InstallRequiredSystemLibraries ) set ( CPACK_GENERATOR STGZ;TGZ;TBZ2;ZIP ) set ( CPACK_PACKAGE_NAME ${PACKAGE} ) set ( CPACK_STRIP_FILES ON ) include ( CPack ) file(GLOB_RECURSE ALL_SOURCE_FILES LIST_DIRECTORIES false ${CMAKE_SOURCE_DIR}/*.[chi] ${CMAKE_SOURCE_DIR}/*.[chi]pp ${CMAKE_SOURCE_DIR}/*.[chi]xx ${CMAKE_SOURCE_DIR}/*.cc ${CMAKE_SOURCE_DIR}/*.hh ${CMAKE_SOURCE_DIR}/*.ii ${CMAKE_SOURCE_DIR}/*.[CHI] ) find_program ( ASTYLE "astyle" ) if ( ASTYLE ) add_custom_target( format COMMAND ${ASTYLE} -A1 -xb -j -k3 -p -f -n -U ${ALL_SOURCE_FILES} ) endif(ASTYLE) swami-2.2.0/COPYING000066400000000000000000000355251361104770400137030ustar00rootroot00000000000000NOTE: This software is restricted to version 2 of the GPL only. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS swami-2.2.0/ChangeLog000066400000000000000000002200501361104770400144070ustar00rootroot000000000000002008-10-13 Josh Green * src/python/swami.defs: Fixed libswami Python binding. * src/swamigui/SwamiguiSplits.c: Fixed crash bug with span Middle click move operation (thanks to Ebrahim Mayat for reporting this). Fixed Middle click move status bar updates. 2008-10-11 Josh Green * README.OSX: Update from Ebrahim Mayat. * src/swamigui/swami-2.glade: Re-saved swami-2.glade using glade 3.4.5. 2008-10-05 Josh Green * src/swamigui/SwamiguiSplits.c: Added support for moving multiple selected spans and root notes (depending on move mode). 2008-09-26 Josh Green * src/swamigui/SwamiguiSplits.c: Added ALT click drag functionality for setting low span handle, root note or upper span handle depending on mouse button pressed. Improved drag logic. 2008-09-25 Josh Green * po/POTFILES.in: Patch from Henry Kroll to remove a couple of files which were recently deleted. * src/libswami/SwamiObject.c: Removed some unused functions. * src/swamigui/SwamiguiSplits.c: Now using GTK for widget construction, added root note indicators, now using anti-aliased canvases, combined several instances of width/height item resize code into a single function called swamigui_splits_update_entries(), now using buttons for Note/Velocity mode selection instead of a notebook, placed a "Shift Mode" drop down widget for future selection of what items get shifted with the middle mouse button (spans, root notes or both), SwamiguiSplitsEntry replaced SplitBag and is now part of the public API (although private), span and root note controls now only created when the appropriate API function is called to get its control, likely some other improvements. * src/swamigui/swami-2.glade: Added SwamiguiSplits widget. 2008-08-18 Josh Green * Removed SwamiguiHPaned.[ch], SwamiguiVPaned.[ch], SwamiguiLayout.[ch], SwamiguiSelector.[ch], SwamiguiSwitcher.[ch], SwamiguiTreeStorePref.[ch] as they weren't providing necessary functionality. * configure.ac: Required glib and gobject version now set to 2.12. * src/glade/Makefile.am: Removed libgladeswamigui (just uses libswamigui now). * src/glade/swamigui.xml: Set library to "swamigui-2.0" other minor changes. * src/libswami/SwamiControl.c: (swami_control_sync_spec): Now returns boolean value to indicate if conversion was successful. * src/libswami/SwamiParam.c: (swami_param_type_has_limits): New function. (swami_param_convert_new): No longer prints conversion errors. (swami_param_type_transformable): New function. (swami_param_type_transformable_value): New function. * src/libswami/swami_priv.h: Removed macros related to older glib support. * src/libswami/value_transform.c: New file which contains additional GValue conversion functions (string to int and string to double currently). * src/plugins/fluidsynth.c: Removed "reverb-enable" and "chorus-enable" properties, since they are now dynamically created as FluidSynth settings properties. Enumeration strings are introspected and a "-options" property is installed. String enumeration types "yes/no" are created as standard boolean properties. * src/plugins/fluidsynth_gui.c: Added FluidSynth preferences. Updated "reverb-enable" and "chorus-enable" to new property names "synth-reverb-active" and "synth-chorus-active". * src/swamigui/SwamiguiControl.c: (swamigui_control_new_for_widget_full): Flags and enum types are reduced to G_TYPE_FLAGS and G_TYPE_ENUM during widget handler comparison. Improved widget handler comparison to find best match. (swamigui_control_glade_prop_connect): Allow for ":blah" postfix to auto glade property names to work around duplicate glade names. * src/swamigui/SwamiguiControl_widgets.c: Added GtkComboBoxEntry handler, GtkFileChooserButton handler and GtkComboBox handlers (string, enum and gtype). (adjustment_control_handler): "digits" now assigned and "spec-changed" watched under the property conditions. (entry_control_handler): Control parameter spec conversion now performed. * src/swamigui/SwamiguiMenu.c: Added Python script editor to Tools menu. Some i18n updates. Added FluidSynth restart item until it is managed by the FluidSynth plugin. * src/swamigui/SwamiguiModEdit.c: Removed "item" property, now using only "item-selection" parameter and supports generic IpatchSF2ModItem interface. * src/swamigui/SwamiguiMultiList.c: Renamed swamigui_multi_list_set_items() to swamigui_multi_list_set_selection(). * src/swamigui/SwamiguiPanelSelector.c: Renamed swamigui_panel_selector_set_items() and swamigui_panel_selector_get_items() to swamigui_panel_selector_set_selection() and swamigui_panel_selector_get_selection(). * src/swamigui/SwamiguiPref.[ch]: New preferences registration and widget. * src/swamigui/SwamiguiRoot.c: Added default piano key mapping strings, Now using a prop handler function for IpatchSF2 types to provide custom creation date functionality (push button for current date). Added glade_module_register_widgets() function, so libswamigui can be used as a glade module. Removed "layout" parameter. Added main_window field to SwamiguiRoot. Updated swamigui_root_init() code to create wavetable device, create main window, display splash image if enabled and swami tips. (swamigui_root_create_main_window): Code in swamigui_create_default_session() moved here. Removed pref_store field from SwamiguiRoot object and added main_window, tree, splits, panel_selector and wavetbl. * src/swamigui/SwamiguiSampleEditor.c: Renamed swamigui_sample_editor_set_items() to swamigui_sample_editor_set_selection() and swamigui_sample_editor_get_items() to swamigui_sample_editor_get_selection(). * src/swamigui/SwamiguiSplits.c: Renamed swamigui_splits_set_items() to swamigui_splits_set_selection(), swamigui_splits_get_items() to swamigui_splits_get_selection() and swamigui_splits_set_selection() to swamigui_splits_select_items(). * src/swamigui/util.c: Added swamigui_util_glade_lookup_nowarn() to not warn if a Glade widget can't be found. * src/swamigui/swami-2.glade: Lots of GUI changes. New preferences interfaces, more interfaces using PROP:: style widget/property auto connection. 2008-03-31 Josh Green * autogen.sh: Added gtkdocize. * configure.ac: Now requiring librsvg-2.0 >= 2.8 and GTK_DOC_CHECK(1.9). * Makefile.am: Added glade sub directory (Swami widgets in glade!). * plugins/Makefile.am: Now building fluidsynth_gui.c. * plugins/fftune_gui.c: Now implements SwamiguiPanel interface. * plugins/fluidsynth.c: Removed left over disabled fixed modulators, reverb and chorus names are now character arrays, reverb and chorus now reverb_params and chorus_params, added reverb_presets and chorus_presets arrays and default presets for both, added reverb-enable and reverb-preset properties (removed reverb-mode), added chorus-enable and chorus-preset (removed chorus-mode), lots of other changes to reverb/chorus parameters and presets. * plugins/fluidsynth_gui.c: New FluidSynth GUI with knobs. * src/swamigui/SwamiguiControl.c: Removed swamigui_control_object_create_widgets(), swamigui_control_object_connect_widgets(), swamigui_control_object_disconnect_widgets() and related functions. Added swamigui_control_glade_prop_connect() which basically replaces above functions, but allows custom glade interfaces whose widgets are automatically connected to object properties. * src/swamigui/SwamiguiKnob.[ch]: New SVG based cairo knob widget. * src/swamigui/SwamiguiMenu.c: Added place holder for preferences. * src/swamigui/SwamiguiModEdit.c: Now implements SwamiguiPanel interface, updated to GtkComboBox widgets. * src/swamigui/SwamiguiPanel.c: Renamed swamigui_panel_get_info() to swamigui_panel_type_get_info(), renamed swamigui_panel_test() to swamigui_panel_type_check_selection(), added swamigui_panel_get_types_in_selection() helper function. * src/swamigui/SwamiguiPanel.h: Added SwamiguiPanelCheckFunc method function definition. * src/swamigui/SwamiguiPanelSelector.[ch]: New panel selector notebook widget. Yeeeeee haaa! * src/swamigui/SwamiguiProp.[ch]: More useful now as it has a registry for Glade widgets or handler functions to create interfaces for specific object types. * src/swamigui/SwamiguiRoot.c: New FluidSynth interface loaded, some layout changes of main window. * src/swamigui/SwamiguiSampleEditor.[ch]: Implements SwamiguiPanel interface now. swamigui_sample_editor_register_handler() now takes a SwamiguiPanelCheckFunc() for determining when an item selection is valid for a given sample editor handler. * src/swamigui/SwamiguiSplits.[ch]: Updated to support any item type with a "note-range" or "velocity-range" property. * src/swamigui/swami-2.glade: Lots of GUI changes. * src/swamigui/images/knob.svg: New Knob SVG image. * src/glade: New directory for Swami glade catalog widgets (use Swami widgets directly in Glade). * src/glade/glade-swamigui.c: Init function of libswamigui. * src/glade/swamigui.xml: Glade catalog description file (only SwamiguiKnob is defined for the moment). 2007-12-02 Josh Green * configure.ac: Removed detection for libxml (will use glib XML functions), removed --enable-build-dir option and added --enable-developer as a replacement which will also include other developer functionality. * swami.anjuta: New Anjuta project file. * swami.prj: Removed old Anjuta project file. * src/libswami/SwamiXml.[ch]: Removed, will be replaced by libinstpatch XML object pickling. Also removed all XML interfaces from other objects. * src/libswami/SwamiXmlObject.[ch]: Removed and probably not needed. * src/libswami/SwamiLog.h: Added SWAMI_ERROR_IO. * src/libswami/SwamiRoot.c (swami_root_patch_load): Added err parameter. (swami_root_patch_save): Added err parameter. * src/python/Makefile.am: Added separate targets for swami.c and swamigui.c and include appropriate PyGtk .def dependencies. * src/python/swamigui.override: Added additional import dependencies. * src/swamigui/SwamiguiMultiSave.[ch]: Added new multi-file save dialog. * src/swamigui/help.c (swamigui_help_about): Ticket #38 - Can now close the about dialog. * src/swamigui/patch_funcs.c: Using new multi file save dialog for saving patch files, added _() i18n macro to some strings. 2007-05-10 Josh Green * Default value for GType properties is now G_TYPE_NONE. * src/libswami/swami_priv.h: Added GTYPE_PARAM_SUPPORT for checking existence of GType GParamSpec support (glib 2.10.0). * src/swamigui/SwamiguiRoot.c: Fixed issue with storage of GType in "default-patch-type" property of SwamiguiRoot, which would affect 64 bit platforms. Now using GType GParamSpec if available (Glib 2.10.0) with fallback to gulong (instead of guint). * src/swamigui/SwamiguiTree.c: Added conditional for use of GTK 2.10.0 only function gdk_atom_intern_static_string(). 2007-04-20 Josh Green * configure.ac: Added --disable-python configure option. * swami-2.png: Updated with new blurry feature of Inkscape, to give some software edge effects. * src/swamigui/SwamiguiDnd.h: Changed SWAMIGUI_DND_WIDGET_INFO and SWAMIGUI_DND_WIDGET_NAME to SWAMIGUI_DND_OBJECT_INFO and SWAMIGUI_DND_OBJECT_NAME respectively, since they will be used for passing arbitrary GObject's within Swami. * src/swamigui/SwamiguiSampleEditor.c: Added buttons for sample cut, crop and new from selection. Added selection support using marker 0. Initial function stubs are there for sample operation buttons, but don't do anything yet. Added swamigui_sample_editor_set_marker() function. * src/swamigui/SwamiguiStatusbar.c: Added "default-timeout" property which is used for messages which use the SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT value. Added swamigui_statusbar_printf() for added convenience in sending executed operation status messages to status bar. * src/swamigui/SwamiguiTree.c: Added support for paste operations using drag and drop and high level tree DnD API. Unfortunately this doesn't currently support multiple item drag and drop, so only 1 item at a time can be pasted using this method (plan to hack in code to implement custom multi-item drag and drop, or wait for GTK support). 2007-03-21 Josh Green * src/swamigui/SwamiguiSampleEditor.c: Moved zoom/scroll canvas functionality to a new separate object SwamiguiCanvasMod. Loop cross section viewer is now also zoomable. Added swamigui_sample_editor_loop_zoom() function to set zoom of loop view. * src/swamigui/SwamiguiTree.c: When item selection is assigned to tree it now expands all parents of selected items and makes the first item in view. * src/swamigui/patch_funcs.c: swamigui_new_item() now sets new item as current selection. swamigui_cb_load_samples_response() now sets current item selection to added samples. * src/swamigui/SwamiguiItemMenu_actions.c: "copy" action now excluded for virtual container types. "load-samples" not available for virtual containers containing samples. "new" available only for instrument and program categories now. * src/swamigui/SwamiguiRoot.c: Tree "selection" now connected bi-directionally to swamigui_root "selection". * src/swamigui/marshals.list: New file, marshals for GUI. * src/swamigui/SwamiguiCanvasMod.[ch]: New canvas zoom/scroll modulator object, so canvas zoom/scroll can be managed in a centralized fashion. New features include: mouse wheel scrolling, acceleration curve equations for wheel and snap zoom/scrolling, smooth wheel operation (scrolling continues for a short time after last wheel event). * src/libswami/SwamiPropTree.c: Properties in prop tree now connected bi-directionally when supported. 2007-02-18 Josh Green * src/swamigui/icons.c: Fixed setting of window default icon name. * src/swamigui/SwamiguiRoot.c: Making sure that we are calling all GUI get_type functions, adding of selector options is handled better for optional components, added global enable variables for Python and plugins so command line arguments can disable them. * src/swamigui/SwamiguiItemMenu.c: Moved one time init stuff to an internal init function instead of the class initializer. * src/swamigui/swami_python.c: Added swamigui_python_is_initialized() to check if Python binding is active. * src/swamigui/main.c: Added -y switch to disable Python binding, plugin disable switch -p should now be working. 2007-02-15 Josh Green * Modified many version sensitive file names to include 2.0 version indicator so future major revisions can be parallel installable (including swami-0.9.xx). * src/plugins/fluidsynth.c: Added initial real time effect control support (currently only works with IpatchSF2Sample items), locking of wavetable object was moved to FluidSynth driver instead of SwamiWavetbl for finer control. * src/swamigui/SwamiguiSampleEditor.c: New range based markers in a bar above sample view, sample loop finder now integrated with sample viewer, started sample selection support (not yet finished). * src/swamigui/SwamiguiLoopFinder.[ch]: Now only the GUI portion of the finder. The smarts have been moved to libswami. * src/swamigui/icons.c: Added loop none/standard/release icons, removed swami icon (moved to top source folder). * src/swamigui/util.c: Added swamigui_util_unit_rgba_color_get_type() to create a new unit type for guint properties to indicate they are RGBA color values (SWAMIGUI_UNIT_RGBA_COLOR). * src/swamigui/SwamiguiSampleCanvas.c: Properties for color values of sample canvas. Modified swamigui_sample_canvas_xpos_to_sample() and swamigui_sample_canvas_sample_to_xpos() to still return a value but also return info on whether the calculated value is valid. * src/swamigui/SwamiguiPiano.c: Added properties for colors. * src/swamigui/SwamiguiControl.c: Simplified parameters of swamigui_control_new_for_widget() and created swamigui_control_new_for_widget_full() with the previous set. Added new swamigui_control_prop_connect_widget() function. * src/swamigui/swami_python.c: Now calling PySys_SetArgv() in _swamigui_python_init() function. * src/libswami/SwamiWavetbl.c: Locking of SwamiWavetbl object removed from most class functions. Its up to the derived class to do its own locking (more flexible). * src/libswami/SwamiControlProp.c: Added new swami_control_prop_connect_to_control() and swami_control_prop_connect_from_control() for added convenience. * src/libswami/SwamiLoopFinder.[ch]: Separated from GUI and moved to its own stand alone object. 2007-01-09 Josh Green * src/swamigui/SwamiguiSampleEditor.c: Speeded up zoom/scroll quite a bit, should be controllable via properties/prefs, fixed problem where zoom value of sample view was quite screwed up on initial display, removed "goto loop end point" buttons (will replace with a canvas loop scrolly thingy). * src/swamigui/SwamiguiTree.c: Added way cool search feature, not yet finished though. * src/swamigui/SwamiguiMenu.c: Setting recent chooser limit to unlimited since the list gets truncated before being filtered (bug in GTK?). * src/swamigui/SwamiguiStatusbar.[ch]: A new statusbar! * src/swamigui/SwamiguiProp.c: Now displays "Item not selected" if property interface is being used but no item is active, fixed an issue with the GtkAdjustment related SwamiControl in SwamiguiControl_widgets.c which broke ranges of scale widgets. * src/swamigui/SwamiguiPiano.c: Re-worked drawing routines and such so that each key has an equal amount of active space (so that the spans don't look so funky), now sends statusbar info to indicate what note is being played or the mouse is over (including velocity). * src/swamigui/SwamiguiSpans.c: Spans are now equal distant for each note, sending statusbar messages to show current span range (on mouse over and when setting/moving). * src/swamigui/SwamiguiItemMenu_actions.c: Added Find and Find Next item menu options and hot keys. * src/swamigui/SwamiguiRoot.c: A little hack to get root "selection" property to always be set to a IpatchList selection (even if its empty) so that the "origin" object is availabe (SwamiguiTree for example), statusbar getting visiously packed. * src/swamigui/SwamiguiRoot.h: Added "statusbar" to SwamiguiRoot instance. * src/swamigui/SwamiguiControl_widgets.c: GtkAdjustment GParamSpec wasn't being handled correctly, breaking SwamiguiProp scale widgets. * src/libswami/util.c: Changed swami_util_midi_note_to_str() to use octave -2 for the first C (as Rosegarden does). Forgot to update swami_util_midi_str_to_note though, added to TODO list. 2006-12-28 Josh Green * src/swamigui/SwamiguiMenu.c: Patch icons are now being used for New.. menu items. * src/swamigui/SwamiguiTreeStorePatch.c: Implemented a work around for SwamiContainer, since it can contain children of different item types. * src/swamigui/SwamiguiPiano.c: Fixed bug which caused mouse grab to not be released when playing the piano with the mouse off the right hand side. * src/swamigui/SwamiguiSplits.c: Velocity mode of the splits widget appears to be working, fixed bug which caused default handler to not initialize correctly in some cases. 2006-12-22 Josh Green * Lots of envelope icons added in src/swamigui/images. * src/swamigui/SwamiguiPanelSF2Gen.[ch]: New - SoundFont generator control interface (oooo puuurrrtty! ;) * src/swamigui/SwamiguiGenGraph.[ch]: Killed, it will be replaced with a generic envelope graph editor. * src/swamigui/SwamiguiGenView.[ch]: Murdered, will replace with a generic property view. * src/swamigui/SwamiguiGenCtrl.[ch]: Replaced by SwamiguiPanelSF2Gen.[ch]. * src/libswami/SwamiStateItem.[ch]: Moving to libInstPatch. * src/libswami/SwamiStateGroup.[ch]: Moving to libInstPatch. * src/libswami/SwamiState_types.[ch]: Moving to libInstPatch. * src/libswami/SwamiState.[ch]: Moving to libInstPatch. * src/libswami/SwamiControlProp.c: New and improved! Updates for new IpatchItem notify system. * src/swamigui/SwamiguiPanel.c: Actually does something now! * src/swamigui/icons.[ch]: New envelope icons! * src/swamigui/SwamiguiTreeStorePatch.c: Various bug fixes that were causing items to not be inserted correctly for container sorted items. * src/swamigui/SwamiguiSplits.c: Beginning of tabbed interface for note and velocity range switching (not done). * src/swamigui/SwamiguiRoot.c: Updated for changes to property/container notify system (moved to libinstpatch). * src/swamigui/SwamiguiSpinScale.c: Some improvements. * src/libswami/SwamiControl.c: Lots of new goodies to make things more complicated and fun including new value transform functions for converting values for a specific connection, swami_control_connect_item_prop() for ultra convenient property connections (with unit conversion!), lots of other crazy crap, I sure hope SwamiControl doesn't become more of a nightmare than it is! Still, it does kick some ass. * Tons of other cool improvements and changes which I don't feel like writing about.. 2006-08-31 Josh Green * src/swamigui/SwamiguiTree.c: Added external file drag and drop support TO the instrument tree. * src/swamigui/SwamiguiMenu.c: Changes to recent manager list in menu (not sure what exactly, but seems to be working well ;). * src/swamigui/icons.h: Reminded by Ebrahim Mayat that OS X does not like variables declared in headers without extern (causes duplicate symbols error during link). * src/swamigui/SwamiguiDnd.h: Added new SWAMIGUI_DND_URI_INFO type for use in external file drag and drop. 2006-07-10 Josh Green * src/plugins/fluidsynth.c: Reverb and Chorus parameters now update synth. * src/swamigui/SwamiguiSampleEditor.c: Removed vertical scaling range control and related non-functioning code. * src/swamigui/SwamiguiTree.c: Right click menu can now be popped via keyboard (popup_menu GtkWidget method hooked), private function swamigui_tree_real_set_store() added to assign active tree store, right click on item not part of list causes right clicked item to become selected, swamigui_tree_get_selection_single() no longer adds a reference to the returned item (the single threaded GUI policy), removed rclick_item field and corresponding functions, tree now assigns itself as the origin (swami_object_set_origin) on its selection property. * src/swamigui/SwamiguiMenu.c: Recent file chooser sub menu added, removed global Edit menu, removed global "Save" menu item, added "New " menu item and the New menu item creates the default or last patch type created, added "Save All" menu item, removed other inactive code. * src/swamigui/icons.c: Icons got severely WORKED over! New icons for DLS, Gig, SoundFont, FFTune GUI and Modulator junction, also a new Swami desktop icon which is now assigned to the main window (gtk_window_set_default_icon). Most other icons replaced by newer sexier versions. Starting to look fucking awesome now! Swami, if you weren't a computer program I'd.. ;) * src/swamigui/SwamiguiTreeStore.c: Renamed swamigui_tree_store_node_peek_item() to swamigui_tree_store_node_get_item() since the default GUI policy is to not reference returned objects. * src/swamigui/patch_funcs.c: Opened files are now added to recent files manager (Gtk+ 2.10.0+ support only currently). * src/swamigui/SwamiguiItemMenu_actions.c: Added key bindings to menu actions, for right click item menu actions which require the active tree the origin is fetched with swami_object_get_origin() and other updates to conform to new SwamiguiItemMenu changes. * src/swamigui/SwamiguiRoot.c: Added "selection-origin" property, icons in SwamiguiSelector re-arranged, "icon" IpatchTypeProp set for supported patch types, item menu accelerators assigned to main window. * src/swamigui/SwamiguiItemMenu.c: Keyboard accelerator code updated, right click item and menu no longer passed to action handlers to make item menu more generic (to be used by splits view for example), and also since the actions can be initiated by key presses. * src/swamigui/main.c: Swami can now except URI style file names (added for support for recent files subsystem). * src/libswami/SwamiRoot.c (swami_root_patch_load): Item is now returned via a pointer instead of the return value, to make it optional and remove the necessity of unreferencing it by the caller. * src/libswami/SwamiObject.c: Added swami_object_set_origin()/get to be able to assign an "origin" object to objects such as IpatchList item selections. 2006-05-08 Josh Green * Functions with 'ref' in the name renamed. * src/plugins/fluidsynth.c: Added "modulators" property for assigning session modulators to FluidSynth instance which get applied to voice caches. If an invalid temporary item is selected, the previous sounding item remains. * Improved Python binding generation (pulls in ipatch.defs). * src/swamigui/SwamiguiGenGraph.c: Fixes/improvements to envelope graph, including MVC updates. * src/swamigui/SwamiguiTree.c: Right click on a non-selected item now clears selection before selecting new item. * src/swamigui/SwamiguiPythonView.c: Added loading of scripts from "scripts" directory in Swami config directory (currently hard coded path: FIXME!). * src/swamigui/patch_funcs.c (swamigui_new_item): Items can now also be created using a IpatchVirtualContainer parent hint and will also use any "virtual-child-conform-func" type property to make new item conform to virtual container. * src/swamigui/SwamiguiMenu_actions.c: "New item" menu action updated to handle virtual containers also. * src/swamigui/SwamiguiModEdit.c: Updated to handle editing of any object which contains a "modulators" property and do so in a MVC fashion. * src/swamigui/main.c: Added "-r" option to run Python scripts on startup. * src/libswami/SwamiControlProp.c: Added swami_control_prop_connect_objects for connecting two object properties together. * src/libswami/SwamiRoot.c: Added "patch-root" property. Renamed swami_patch_load_ref to swami_root_patch_load and swami_patch_save to swami_root_patch_save. 2006-04-15 Josh Green * gtk-doc updates. * Removed builtin_enums.[ch] and marshals.[ch] which are now being auto generated always. 2006-04-12 Josh Green * src/python/swami.defs: No longer auto generated. * src/python/swamigui.defs: No longer auto generated. * src/swamigui/SwamiguiBar.[ch]: New canvas item bar for pointers and ranges (not yet functional). * src/swamigui/SwamiguiBarPtr.[ch]: New canvas item which defines a pointer or range to be added to a SwamiguiBar canvas item (not yet functional). * src/libswami/SwamiWavetbl.[ch]: Added check_update_item, update_item, and realtime_effect methods and related C functions. * src/libswami/libswami.c: Changed patch property change callback system: properties are now specified by GParamSpec instead of property name, wild card callbacks can be added for item and or property. * src/libswami/libswami.h: Changed SwamiPatchPropCallback to include SwamiEventPropChange parameter to save one from having to retrieve it. * src/plugins/fluidsynth.c: Added patch property change monitoring and check_update_item and update_item methods for refreshing voice cache of active instruments. * src/swamigui/SwamiguiPythonView.[ch]: Widget is now generated with glade, scripts can now be execute in full, toggle for line by line execution. * src/swamigui/swami_python.[ch] (swamigui_python_set_root): Function called by SwamiguiRoot to assign to the "swamigui.root" variable so it is availabe to Python scripts. 2006-03-15 Josh Green * Updated gtk-doc build files and now disabled by default. * src/swamigui/SwamiguiControl.c (swamigui_control_object_create_widgets): Added hack for read only label controls to make them left justified. * src/swamigui/SwamiguiLoopFinder.c: Added a revert button to revert loop settings to the original values. * src/swamigui/SwamiguiRoot.c: Root "selection" property now connected to "item-selection" property of SwamiguiSwitcher objects. * src/swamigui/SwamiguiSampleEditor.c: Start and end loop spin buttons now functional. * src/swamigui/patch_funcs.c (swamigui_new_item): Fixed creation of new IpatchBase types. 2006-03-11 Luis Garrido * Autolooper: much more optimization and even better handling of local maxima. 2006-03-10 Josh Green * src/swamigui/SwamiguiLoopFinder.c: Some minor bug fixes and optimizations. 2006-03-10 Luis Garrido * Autolooper: some optimization and better handling of local maxima. 2006-03-09 Josh Green * src/libswami/SwamiContainer.[ch]: Created new object type for IpatchItem container root. * src/libswami/SwamiRoot.c: No longer derived from IpatchItem. Patch tree root is now a separate object. * src/libswami/SwamiControl.c: More detailed control debug messages. * src/libswami/SwamiControlProp.c: Now re-transmitting all set value events to connected outputs. * src/swamigui/SwamiguiControlMidiKey.c: Using key snooper functionality of GTK main loop, instead of a widget. * src/swamigui/SwamiguiLoopFinder.c: Algorithm run in a separate thread making the GUI much more responsive, many other GUI improvements including min loop size parameter. * src/swamigui/SwamiguiRoot.c: Added "selection-single" property and sending notify events for "selection" and visa-versa, swamigui_root "selection" and "selection-single" are now being used to connect other interfaces via controls. * src/swamigui/SwamiguiSampleEditor.c: Better loop finder button. 2006-03-08 Luis Garrido * Added first version of functional autolooper code. 2006-03-07 Josh Green * src/swamigui/SwamiguiLoopFinder.[ch]: New loop finder dialog widget. Awaiting loop finder algorithm code from Luis Garrido. * src/swamigui/SwamiguiSampleEditor.c: Removed recently added loop finder widgets and added Find button for loop finder dialog. * src/swamigui/help.c: Now using GtkAboutDialog for about. * src/swamigui/util.c: Removed call to gtk_window_set_wmclass as its not recommended anymore. 2006-03-04 Josh Green * src/plugins/fluidsynth.c: Added FluidSynth MIDI router callback to pass events to FluidSynth (from MIDI driver) to Swami control network, now tracking channel bank and preset numbers and restoring when Synth is restarted, temporary item is not cleared when closed and is restored when restarted, bug fixes to close and finalize. * src/swamigui/SwamiguiItemMenu_actions.c: Added a "Restart Driver" option for SwamiWavetbl devices. 2006-03-03 Josh Green * src/swamigui/SwamiguiSampleEditor.c: Started adding loop finder GUI components and loop start/end goto and spin buttons. * src/swamigui/SwamiguiMenu.c: Forgot to add edit menu action again. 2006-03-03 Josh Green * src/swamigui/SwamiguiPanel.c: New interface "panel" registry, not yet being used. * src/swamigui/SwamiguiTreeStorePatch.c: Split off instrument tree store specific code to a sub class. * src/swamigui/SwamiguiTreeStorePref.c: New preferences tree store. * src/libswami/SwamiControlProp.c: Added swami_ref_prop_control_from_name and swami_ref_prop_control_from_pspec to allow for SwamiControlProp sharing for a given object property. * src/libswami/SwamiPropTree.c: Converted GMemChunk stuff to new GSlice with backwards compatible defines. * src/libswami/SwamiRoot.h: Changed add_object signal to object_add. * src/libswami/swami_priv.h: Added GSlice backward compatible defines and removed PREALLOC defines since they are likely useless with new GSlice memory allocator. * src/libswami/util.c: Converted GMemChunk related code to GSlice. * src/plugins/fluidsynth.c: All FluidSynth properties now dynamically created, removed static properties now being handled dynamically. * src/swamigui/SwamiguiControl.c (swamigui_control_object_create_widgets): Checking for IPATCH_PARAM_HIDE flag in property GParamSpecs to not show certain properties in user interfaces. * src/swamigui/SwamiguiMenu.c: Now using swamigui_root "selection" property for edit menu actions. * src/swamigui/SwamiguiRoot.c: Changed tree-store property to tree-store-list which is now a list of tree stores. Removed tree-focus property and added a "selection" property. Now creating the preferences tree store. * src/swamigui/SwamiguiTree.c: Changed to a notebook to allow for multiple tree interfaces (instrument and preferences currently). * src/swamigui/SwamiguiTreeStore.c: All instrument tree specific code moved to SwamiguiTreeStorePatch.c, now an abstract base class. 2006-01-20 Josh Green * src/swamigui/SwamiguiSampleEditor.c: Fixes to object finalization. * src/swamigui/patch_funcs.c: Removed old rotten mass of smelly yucky stuff that was the paste system and put a nice new shiney one in its place. Ohh, did I mention it kicks ass? Probably still many bugs to fix.. "But we don't care, we still want the money Lebowski!" 2005-11-25 Josh Green * src/libswami/SwamiRoot.c: Modified swami_patch_load_ref and swami_patch_save to use new IpatchConverter convenience functions. * src/plugins/fluidsynth.c: Modified for new IpatchConverter system. 2005-11-05 Josh Green * src/swamigui/SwamiguiNoteSelector.[ch]: New MIDI note selector widget. * src/plugins/fftune_gui.[ch]: GUI frontend for FFTune plugin. * intl: removed. * src/libswami/SwamiControl.c: Don't allow more connections than can be handled to a control at swami_control_connect() time. * src/libswami/SwamiControlProp.c: Removed direct calling of get_property and set_property functions, since it probably isn't supported. * src/libswami/SwamiEvent_ipatch.c: Fixed some finalize bugs with prop change event, and allow empty value now. * src/libswami/SwamiParam.c: Removed extra parameters, now superseded by IpatchParamProp. * src/libswami/SwamiRoot.c: Added a "sample-format" parameter to set the default sample export format. * src/libswami/util.c: Added swami_util_midi_note_to_str() and swami_util_midi_str_to_note() for converting between MIDI note numbers and ASCII strings. * src/plugins/fftune.[ch]: Now kicks ass, and actually works to some extent :) Interface is accomplished completely through properties and signals. * src/plugins/fftune_gui.[ch]: Yes, there is now a GUI for FFTune! * src/swamigui/SwamiguiControl.c: new swamigui_control_object_disconnect_widgets() function, now doing some crude aliasing to make more property types be controllable by GUI widgets. * src/swamigui/SwamiguiProp.c: Updates to the properties control widget, works "mo' betta now". * src/swamigui/SwamiguiRoot.c: Added a "main-window" property and property changes are now handled to some extent. * src/swamigui/SwamiguiSpectrumCanvas.[ch]: Lots of bug fixes, i.e., actually works now! * src/swamigui/SwamiguiTreeStore.c: Property changes handled somewhat, no auto-re-sorting though, yet. * src/swamigui/patch_funcs.c: Removed swamigui_item_properties() because it was lame and no longer needed. Added sample export function. 2005-10-15 gettextize * Makefile.am (SUBDIRS): Remove intl. 2005-09-18 Josh Green Applied Win32 build patch from Keishi Suenaga. Fixed a build error with python binding. 2005-08-17 Josh Green src/swamigui/SwamiguiControl_widgets.c: Fixed some bugs where IPATCH_ITEM_WLOCK/UNLOCK was being used instead of SWAMI_LOCK_WRITE/UNLOCK. src/swamigui/SwamiguiRoot.c: Added "icon" and "open-icon" type properties. src/swamigui/SwamiguiTree.c: Fixes to right click menu handling which was causing item reference leaks. src/swamigui/ifaces/SwamiguiItem_DLS2.c: Modified to handle new DLS "percussion" property. configure.ac: Removed src/swamish/Makefile from AC_OUTPUT, thanks to Pieter Penninckx for reporting this. 2005-06-24 Josh Green src/libswami/SwamiControl.c: Controls now added to a global list for periodic expired event cleanup, swami_control_do_event_expiration added for this purpose. src/libswami/SwamiControlProp.c: Fixes to finalization and referencing. src/libswami/SwamiPropTree.c: Fixes to finalization and referencing. src/libswami/SwamiRoot.c: Fixes to finalization. src/libswami/libswami.c: Timeout added for expiring inactive control events. src/plugins/fluidsynth.c: Converted to IpatchSF2VoiceCache system. src/swamigui/SwamiguiControl_widgets.c: Fixes to finalization and referencing. src/swamigui/SwamiguiLayout.c: Fixes to finalization and referencing. src/swamigui/SwamiguiMenu.c: Now using gtk_ui_manager for menus. src/swamigui/SwamiguiRoot.c: Fixes to finalization and referencing. src/swamigui/SwamiguiSplits.c: "item-selection" property now readable. src/swamigui/SwamiguiTree.c: Disabled new interactive search feature, fixes to finalization. src/swamigui/SwamiguiTreeStore.c: Fixes to finalization. src/swamigui/main.c: Updated command line text output. src/swamigui/ifaces/SwamiguiSplits_DLS2.c: Some bug fixes and added custom tests for Gig files. src/swamigui/ifaces/SwamiguiSplits_SF2.c: Some bug fixes. 2005-05-06 Josh Green Removed dependency on popt. Updated copyright year and license to indicate only Version 2 of the GPL can be used (to avoid any possible future corruptions of the GPL). Added an "object-add" signal to SwamiRoot which is emitted when an object is added. Splash is now conditionally built. Renamed swami_control_new_for_object to swami_control_new_for_widget and swami_control_create_object to swami_control_create_widget. Added swamigui_control_object_create_widgets and swamigui_control_object_connect_widgets for creating and connecting, respectively, GUI controls to a GObject's properties. Improved reference handling and item locking for SwamiguiControlAdj and SwamiguiControl_widgets. SwamiguiMenu now has a "New .." menu item which allows for selection of what type of object to create. SwamiguiProp will now automatically create a property editor if no SwamiguiItem prop method exists and now allows GObject items. Added a "default-patch-type" property for setting the default File->New patch type. SwamiguiTree now responds to "selection" set property. Sample importing now works and in a generic way. Started Copy/Paste clipboard style item paste (not done yet). Continuing work on swamish (not wroking yet). SwamiguiSplits syncing to tree item selection, but not the other way yet. Added a center horizontal line to SwamiguiSampleEditor. Probably other stuff too :) 2004-12-14 Josh Green Some documentation updates, other stuffs.. * src/plugins/fftune.[ch]: Updated for fftw3 and new IpatchSample interface. * src/plugins/FFTuneGui.[ch]: Starting to implement the FFTune GUI yeeeha! * src/swamigui/SwamiguiSpectrumCanvas.[ch]: New fequency spectrum canvas, completely untested :) * src/swamigui/SwamiguiControl.[ch]: GUI object can now be configured without creating the SwamiControl and GParamSpecs are used for configuring GUI objects. * src/swamigui/SwamiguiControl_widgets.c: Updated for changes in SwamiControl.c. * src/swamigui/SwamiguiSplits.c: Now sizes to an absolute minimum or the width of the view whatever is largest. 2004-11-19 Josh Green * src/plugins/fluidsynth.c: Samples are now converted to 16 bit mono and native host endian if necessary. 2004-11-08 Josh Green * configure.ac: PyGtk codegen dir now fetched with pkg-config. * src/python/Makefile.am: PyGtk codegen dir now fetched with pkg-config. 2004-11-07 Josh Green * src/swamigui/Makefile.am: Added swami.glade to EXTRA_DIST. * src/swamigui/SwamiguiSampleCanvas.c: Updated for new sample format channel routing. * src/swamigui/ifaces/SwamiguiSampleEditor_DLS2.c: Loop points are now working for DLS and GigaSampler and stereo support added. * src/swamigui/ifaces/SwamiguiSplits_DLS2.c: Key split controls added for DLS2 regions. 2004-11-03 Josh Green * configure.ac: Micro versions (*.*.M) removed from PKG_CONFIG tests, thanks to Ebrahim Mayat for pointing out that this breaks the fftw test on Mac OS X. * src/swamigui/SwamiguiRoot.c: ALSA sequencer plugin is now tested for before being created. * src/swamigui/Makefile.am: Swamigui is now built as an installed shared library, swami.glade and header files are now installed. * src/libswami/Makefile.am: Header files now get installed. * ac_python_devel.m4: Uses -lSystem on Darwin instead of -lutil, thanks to Ebrahim Mayat for reporting this. 2004-10-28 Josh Green Massive rename of SwamiUi to swamigui to comply with mixed case naming conventions used by PyGtk, etc. * src/libswami/SwamiControl.c: Added a new function swami_control_get_connections(), added event debugging code, queue test_func virtual functions are now called to determine if an event should be queued or not. * src/libswami/SwamiControlProp.c: Fixed a signal disconnect bug. * src/libswami/SwamiControlQueue.c: Added swami_control_queue_set_test_func() for setting a queue test function. * src/libswami/SwamiWavetbl.c: Renamed swami_wavetbl_init_driver to swami_wavetbl_open, renamed swami_wavetbl_close_driver to swami_wavetbl_close including method names. * src/swamigui/patch_funcs.c: Updated file open dialog to use new GTK 2.4 file dialog. * src/swamigui/SwamiguiSampleEditor.c: Many improvements including loop viewer is now working, sample view markers functional, multiple tracks can now be added, many bug fixes. * src/swamigui/SwamiguiSplits.c: Re-wrote splits canvas widget to use rectangles for the span areas and include vertical lines for the end points. * src/swamigui/SwamiguiSpan.[ch]: Removed, no longer needed. * src/swamigui/ifaces/SwamiguiSampleEditor_SF2.c: Updated for latest changes to sample editor. * src/swamigui/ifaces/SwamiguiSplits_SF2.c: Updated for latest changes to splits widget. * src/plugins/alsaseq.c: New ALSA sequencer MIDI source/sink. 2004-09-17 Josh Green Release: 1.0.0beta-20040917 Massive rename of SwamiUi -> Swamigui. Cleaned up Python checks in configure.ac (hopefully). API documentation updates. Python binding now functional for libswami and swamigui! Renaming of some widgets to fit case naming conventions. * src/swamigui/SwamiguiSampleCanvas.c: CTRL-key zooming and SHIFT-key scrolling in sample view. * src/swamigui/SwamiguiPythonView.[ch]: New Python editor/shell widget, in other words, yeeeeeeha! 2004-09-04 Josh Green * src/libswami/SwamiRoot.c (swami_patch_save): Updated to use new patch object conversion system, so any patch format that has an object to file converter should save now. * src/swamigui/SwamiUiSampleCanvas.c: Re-wrote draw routines to use new IpatchSampleTransform object system and other improvements (code not as ugly and perhaps faster and less buggy?). * src/swamigui/SwamiUiSampleEditor.c: Sample zoom is now clamped to sane values. 2004-07-14 Josh Green Removed unused plugins and src/include files (each src directory now has its own i18n.h). SwamiMidiEvent now structure boxed type rather than an object. Updated patch object load routine to use new converter system. Updated i18n support in all src/ directories so each one now has its own gettext domain. More fixes to SwamiUiSplits in regards to destroying and finalization. 2004-07-06 Josh Green Fixes to po and intl gettext stuff and other build problems. Some changes to python binding, although its probably not working yet. gtk-doc documentation now pretty nice - doc updates throughout code. * src/libswami/SwamiControl.c: Some changes to handling of GValue GBoxed and GObject based GParamSpecs. * src/libswami/SwamiControlProp.c: Changes to handling of GValue GBoxed and GObject based GParamSpecs. * src/libswami/SwamiObject.c: Moved some more functions from SwamiRoot.c. * src/libswami/SwamiParam.c (swami_param_type_from_value_type): Now handles GBoxed and GValue derived value types. * src/libswami/SwamiRoot.c: Moved some functions to SwamiObject.c. * src/swamigui/SwamiUiControl.c: Renamed swamiui_control_create to swamiui_control_new_for_type and added a swamiui_control_new function for ultra convenience. * src/swamigui/SwamiUiPiano.c: Moved code in "destroy" handler to "finalize" handler, since it was causing a crash (destroy called multiple times). * src/swamigui/SwamiUiRoot.c: Added more items to the switcher object including tree and splits editor. Also added some new icons for these. A "store" value is now set at the root of the property tree to assign itself to new tree objects (a HACK?) * src/swamigui/SwamiUiSplits.c: SwamiControls are now being created for splits and SF2 interface is now using them. * src/swamigui/SwamiUiTree.c: Added a "store" property for tree store which automatically gets set for new tree objects by SwamiUiRoot property tree. 2004-07-02 Josh Green Release: 1.0.0beta-20040703 Lots of changes, I'll do my best to include them here. Lots of changes to SwamiControl and friends; SwamiRoot split out into SwamiRoot, SwamiObject and SwamiParam; new controls including PC MIDI keyboard, event hub, and GTK control widgets; new undo/redo state types based on control events; FluidSynth plugin is working again; a vertical and horizontal pane splitting widget; properties widget updates to patch interfaces. Lots of cool things are starting to work and come together. 2003-08-06 Josh Green GigaSampler loading and synthesis support. FluidSynth plugin now working again. Modified IpatchSF2Voices interface to abstract samples away from IpatchSF2Sample. Created SwamiControlMidi for MIDI controls and added interfaces to FluidSynth plugin and SwamiUiPiano. Fixed some bugs in IpatchRiffParser related to odd chunk sizes. SwamiRoot object lookup functions now search recursively through tree. 2003-07-28 Josh Green Removed old SoundFont loader and implemented new IpatchRiffParser based one. Many updates to IpatchRiffParser. Added IpatchFileBuf functionality for parsing data in an endian safe fashion. Added SwamiEvent as a base object for events in the SwamiControl network. SwamiEventValue for value update propagations. SwamiMidiEvent for MIDI events. SwamiMidiDevice to take the place of removed SwamiMidi. SwamiUiControlAdj to connect GtkAdjustment into the SwamiControl network. SwamiUiSample is a new GnomeCanvas item sample display. SwamiUiSampleEditor takes the place of the removed SwamiUiSampleViewer. SwamiUiSpan is a GnomeCanvas item span widget which replaces widgets/keyspan.[ch]. SwamiUiSplits replaces the SwamiUiSpanWin widget. ifaces/SwamiUiDLS2Dummy.[ch] for DLS dummy tree items. ifaces/SwamiUiSampleEditor* for interfaces to new sample editor. ifaces/SwamiUiSplits* for interfaces for new splits widget. Lots of other junk. 2003-06-24 gettextize * Makefile.am (SUBDIRS): Add m4. (ACLOCAL_AMFLAGS): New variable. * configure.ac (AC_OUTPUT): Add m4/Makefile. 2003-06-24 Josh Green Can't remember, its been way too long since last commit. Lots of stuff though, re-writing GUI widgets and making them more pluggable, etc. Whos checking the ChangeLog anyways :) 2003-03-05 Josh Green *: DLS objects are now almost complete, DLS loader is compiling, memchunks in libinstpatch now being properly locked, a GValue control interface and a few objects were added to libswami as well as the beginning of a property tree system and GValue stack based virtual machine, "Preset number" changed to "program number" to coincide with terminology with other patch formats. 2003-02-24 Josh Green * : DLS loader is taking shape, changes and additions to RIFF parser, IpatchDLS2 object in the works, SwamiXmlObject created to store raw XML state data, GUI moved from src/gui/ to src/swamigui/ to help global GUI header install, now using libglade although not fully migrated yet, ipatch_sf2_find_free_preset annexed in favor of a ipatch_base_find_unused_midi_locale method, SoundFont item make unique and add unique functionality moved to IpatchContainer, added a remove method to IpatchItem to handle removes of item and all dependencies (IpatchSF2Sample and IpatchSF2Inst methods implemented), fixed some bugs in IpatchSF2 duplicate method, added "rate" property to IpatchSampleData, removed SwamiConfig system entirely (config vars are now handled by object properties), added type and instance ranking to SwamiRoot, added a "create_midi" method to SwamiWavetbl to allow a wavetable object to create MIDI objects to control itself, many additions and changes to XML state system, GUI layout is now attempting to save/restore itself (not working yet), probably many other things. 2003-01-24 Josh Green * : Extreme rapeage! New RIFF parser object in libInstPatch, started DLS2 objects and loader, a SwamiUiItem interface created to ease adaption of new patch formats (handles labels and pixmaps, adding, removing and updating GUI tree store, action menus, paste routines, property create, verify and commit), all SoundFont SwamiUiItem interface methods put in SwamiUiItem_SF2.c with paste routines in SwamiUiPaste_SF2.c, tree store abstracted from GUI tree, SwamiItem renamed to SwamiLock to reflect its real purpose, FluidSynth plugin updated for recent CVS changes, a new paste object created to handle paste operations, many things still broken :) 2002-12-18 Josh Green * : libInstPatch API doc updates, split IpatchSampleStore types out to individual files, split IpatchSF2File to its own file, all for documentation purposes. Changed many functions related to modulator lists. Added a SF2 voices interface to convert patch items to SF2 voices for synthesis (and created these interfaces for standard SF2 items). FluidSynth plugin is now working again and making noises :) Moved IpatchSampleStore flags into IpatchItem flags field. 2002-12-12 Josh Green * : Lots of API renaming including changing SoundFont based objects with SF in the name to SF2 (to be able to distinguish from other versions of SoundFont should they be added), IPatch->Ipatch, and SwamiUI->SwamiUi. Also SwamiObject was renamed to SwamiRoot and SwamiUIObject to SwamiUiRoot. Python support being worked on (its now functioning but not complete). SwamiUiGenView and SwamiUiGenCtrl are now smarter in how they update themselves to improve performance. Also these objects now listen to generator change events. Piano to keyboard key mapping preferences converted to GtkTreeView. Fixed SwamiUiGenGraph object. SwamiXml.[ch] interface added for new XML based state save/restore system. Ported some changes from GTK1.2 branch including changes to Piano and Keyspan widget. Removed improper use of depricated gtk_widget_set_usize (now using gtk_entry_set_width_chars where appropriate). 2002-12-03 Josh Green * : Devel branch checked in. TONS of changes including GUI is now GTK2 based, libInstPatch has been completely re-written using GObject, Swami undo/redo state history and queues, gtk-docs being built and many other things. Lots of bugs, so lets get this show on the road! 2002-07-17 Josh Green * : FLAC plugin compressor is working (not enabled or tested yet) and configure option added, separated iiwusynth GUI stuff to wavetbl_iiwusynth_gui.c, GenGraph object is now functioning and usable, added 'patch-load' signal to SwamiUIObject so loading of files can be hooked, added a macro and routine to run a separate GUI init function in plugins and fixed some stuff with libinstpatch lowlevel chunk routines and reporting of file position. 2002-07-08 Josh Green * : Gettext support re-enabled, converting to gtk-doc for API docs, plugin system is now working, a new gtk-canvas based SwamiUIGenGraph object for graphing generator controls, libInstPatch now using glib, new functionality for SoundFont lowlevel chunk routines, a FLAC based plugin is in the works, some fixes to passing of modulators to iiwusynth, blah blah blah blah.. Too long since last update. 2002-06-08 Josh Green * : Many new routines dealing with modulator lists in libInstPatch and also added modulator layering to sfont_item_foreach_voice. Modulator editor now works and modulators are loaded into iiwusynth (not tested). Chorus parameter controls added to iiwusynth control dialog and enabling/disabling chorus/reverb doesn't require restart of drivers any more. Fixed keyspan widget grabbing for SwamiUISpanWin. 2002-06-06 Josh Green * : Updated documentation and added 2002 to copyright string. Moved libsoundfont into src/libinstpatch and renamed soundfont.h header to instpatch.h. Also split view menu radio buttons out into individual callbacks again due to a problem with Glade. 2002-06-04 Josh Green * Makefile.am: Disabled gettext support (temporary) * README: Updated for Swami (was the Smurf README). * configure.in: Removed gettext support (temporary) * src/gui/Makefile.am: Removed gettext support (temporary) * src/gui/SwamiUIModEdit.c: Its actually somewhat working now. Will display available modulators and load controls. Doesn't edit anything yet though. A pixmap combo box was added for selecting source control transform functions. Two option menus are used for selecting destination generator, one being the group the generator is part of and the other is filled with the generators for the selected group, which can then be selected. * src/gui/SwamiUIObject.c: Moved lack of plugin support hackery to SwamiUIObject instead of SwamiObject. "Plugins" are now being statically linked into swami binary. * src/gui/pixmap.c: Pixmaps added for modulator transform functions. * src/gui/pixmap.h: Pixmaps added for modulator transform functions. * src/gui/pixmaps/Makefile.am: More pixmaps. * src/gui/widgets/Makefile.am: combo-box.[ch] and pixmap-combo.[ch] widgets added and modified from libgal. * src/gui/widgets/combo-box.c: New widget taken and modified from libgal. A popup combo box widget. * src/gui/widgets/pixmap-combo.c: A widget that uses combo-box to display a table of pixmaps for selection. Currently used in modulator editor. * src/include/Makefile.am: Added missing entries for some header files. * src/libswami/Makefile.am: Removed linking of "plugins" into libswami which was causing some breakage for some users. Now being linked into swami binary. * src/libswami/SwamiObject.c: Removed "plugin" loading hackery. * src/libswami/SwamiPlugin.c: Plugin code ripped and modified from gstreamer, not yet being used. * src/libswami/SwamiPlugin.h: Plugin code ripped and modified from gstreamer, not yet being used. 2002-05-31 Josh Green * acinclude.m4: Removed comments that contained "AM_PATH_ALSA" as aclocal assumed that we were using it (how appalling!) * src/gui/SwamiUIGenCtrl.c: (cb_ctrl_value_change): Updated for changed function prototype for real time generator control. * src/gui/menutbar.c: (swamiui_menu_cb_lowpane_select), (swamiui_tbar_new), (tbar_cb_lower_view_button_toggled), (tbar_cb_piano_mode_button_toggled), (swamiui_tbar_set_lowpane_togbtn), (swamiui_tbar_set_piano_mode_togbtn): Fixed menu view radio buttons and now have an icon for the not as yet working modulator editor. * src/gui/pixmap.c: Added a new pixmap. * src/libswami/SwamiWavetbl.c: (swami_wavetbl_set_gen_realtime): Changed realtime generator control function prototype to use an SFItem as the layer rather than an index. * src/libswami/SwamiWavetbl.h: Changed prototype for swami_wavetbl_set_gen_realtime. * src/plugins/wavetbl_iiwusynth.c: (wavetbl_iiwusynth_register), (wavetbl_iiwusynth_init), (sfloader_preset_noteon), (sfloader_temp_preset_noteon), (sfloader_preset_foreach_voice), (wavetbl_iiwusynth_set_gen_realtime), (swamiui_wavetbl_iiwusynth_create_controls): Removed old inadequate real time generator control and implemented a routine to re-layer an audible and update changed voices. Only works for most recent note in temporary audible. 2002-05-28 Josh Green * src/gui/SwamiUIObject.c: Quit confirmation works now. Add selected files in multi file selection dialog works for patch file loading. Selecting zones in tree selects them in spanwin as well (not the other way around yet though). * src/gui/SwamiUISpanWin.c: Virtual piano key table is now loaded and saved from preferences. Fixed stuck notes problem. * src/gui/menutbar.c: (swamiui_menu_cb_save): Enabled Save and Save As options on main menu. * src/gui/pref.c: Re-enabled virtual piano key table configuration. * src/gui/widgets/piano.c: Fixed piano widget so it won't emit note on/off events for keys that are already in that state. * src/gui/SwamiUIProp.c: Added instrument zone properties for setting Root key override, exclusive class, fixed note, and fixed velocity generators. 2002-05-27 Josh Green * autogen.sh: Added '--add-missing --copy' switches to automake to fix problems with missing files and automake 1.5. * configure.in: Changed version to "0.9pre1". * src/gui/Makefile.am: Removed SwamiUISelector.[ch], added pref.[ch], SwamiUIModEdit.[ch] and item_paste.[ch]. * src/gui/SwamiUIGenCtrl.c: Patch item is now set explicitly with swamiui_genctrl_set_item and removed use of SwamiUISelector. Added generator default toggle buttons to unset generators and re-structured code a bit. * src/gui/SwamiUIGenView.c: Patch item is now set explicitly with swamiui_genview_set_item and removed use of SwamiUISelector. * src/gui/SwamiUIMidiCtrl.c: New routine swamiui_midictrl_midi_update_all which sends values of all controls to MIDI driver. Now using 0-15 for channel value sent to MIDI driver. * src/gui/SwamiUIObject.c: Changed many SwamiConfig variable key names and removed a few. Using a new object registration system to associate child objects to main SwamiUIObject. Added new modulator editor to GUI, not operation yet though. Open files routine now uses multi file selection widget to open multiple files. Multi file close dialog now functions properly. Sample load dialog now uses multi file selection widget and samples are named properly. * src/gui/SwamiUIObject.h: Removed child widget pointers from SwamiUIObject, use child registration system instead. * src/gui/SwamiUIProp.c: (sync_widget_to_property): Fixed some problems with handling of NULL strings for some property values. Fixed some bugs relating to setting properties on items not in Swami tree. Comment field of SFData items now converts newlines. * src/gui/SwamiUISampleView.c: Renamed swamiui_sampleview_set_sfitem to swamiui_sampleview_set_item as well as the sfitem field of the SwamiUISampleView object to item. * src/gui/SwamiUISelector.c: Removed from CVS, bad idea. * src/gui/SwamiUISelector.h: Removed from CVS, bad idea. * src/gui/SwamiUISpanWin.c: More renaming of 'sfitem' to 'item' and setting item to an invalid type now the same as setting to NULL. Added 'select-zone' and 'unselect-zone' signals for keyspan list selection. Rootkey ptrstrip now works (have to select keyspan though). * src/gui/SwamiUITree.c: Fixed a bug in preset add routine related to faulty iteration over GtkCTree nodes causing failure. Renamed swamiui_tree_get_selection_complete to swamiui_tree_get_selection_rclick. Now using a hash table for item->ctree_node lookups in preparation for multiple trees and to free up the user_data field of items. Added test for single selected item on 'tree-row-unselect' signal, might remove though as it causes unselect/select widget weirdness. * src/gui/SwamiUITreeMenu.c: Right click 'R' and Multi item 'M' menu items now use different, and proper, tree selection fetch routines. Added paste routines and removed un-implemented right click menu items. * src/gui/main.c: Swami will now accept sound font file names on the command line, which it will load. * src/gui/menutbar.c: Menu radio button under 'View' now work and are synchronized with the toolbar buttons. Green light toggle button now being used again, it stops and starts iiwusynth. * src/gui/pref.c: Added to CVS. Most preferences work now. * src/gui/pref.h: Added to CVS. * src/gui/util.c: New routine swamiui_util_option_menu_index. Modified swamiui_util_lookup_widget to work with regular glade widgets. Renamed some of the unused string utility functions. * src/include/gobject2gtk.h: Fixed G_OBJECT to work correctly. Added GTK based g_object_get and g_object_get_valist handlers. * src/libswami/SwamiAudiofile.c: Fixed a bug with initial parameter settings and changed a config variable. * src/libswami/SwamiConfig.c: Fixed a file handle leak. * src/libswami/SwamiMidi.c: Fixed bug where class was accessed before it was created. * src/libswami/SwamiObject.c: Added a flag to SFItem for toplevel patch objects which indicates whether they are active and part of the Swami tree and should emit signals. This allows property set routines to be used for items which aren't in Swami's tree without causing problems. Some SwamiConfig variables added. Child registration functions renamed. The swami_item_new routine now accepts a variable argument list of properties to set on the new item. "software" property of SoundFonts is now intialized for new items and set for saved ones. * src/libswami/SwamiWavetbl.c: An 'active' property added which allows the querying of the active state of a wavetable driver. Fixed bug where class was accessed before it was created. Added 'set_gen_realtime' function type for wavetable drivers. * src/libswami/gobject2gtk.c: Many changes to fix G_OBJECT macro. Added g2g_object_get_valist and g2g_object_get handlers. * src/plugins/wavetbl_iiwusynth.c: SwamiConfig variables added. Driver preferences now working. MIDI driver now closes when Wavetable driver does. SWAMI_MIDI_BEND_RANGE now handled. Update for change in iiwusynth.h, rename of iiwu_voice_start to iiwu_synth_start_voice. iiwusynth control dialog hacked and works for setting reverb, chorus and master gain settings. Some trace code for real time generators which is currently non-operative. * src/gui/SwamiUIModEdit.c: Added to build, modulator edit widget. * src/gui/SwamiUIModEdit.h: Added to build. * src/gui/item_paste.c: Added to build, item paste routines. * src/gui/item_paste.h: Added to build. * src/gui/widgets/multi_filesel.c: Added to build, multi file selection widget. * src/gui/widgets/multi_filesel.h: Added to build. 2002-04-30 Josh Green * src/gui/Makefile.am: Build flags for audiofile * src/gui/SwamiUIMidiCtrl.c: (swamiui_midictrl_init), (send_midi_event), (set_piano_octave): Fixed initial MIDI spin button control bug and piano octave setting now works. * src/gui/SwamiUIMultiList.c: (swamiui_multilist_init), (swamiui_multilist_new), (swamiui_multilist_set_selection), (destroynotify_unref_items), (swamiui_multilist_new_listbtn), (cb_listbtn_clicked): Adding more helpful stuff to the multi item list object, including routines to help with referencing a list of items and a routine to create list buttons. * src/gui/SwamiUIObject.c: (swamiui_object_init), (swamiui_open_files), (swamiui_cb_open_files_okay), (swamiui_close_files), (swamiui_save_files), (cb_save_files_ok), (cb_save_files_browse), (cb_save_files_browse_ok), (swamiui_delete_items), (swamiui_wtbl_load_patch), (swamiui_item_properties), (swamiui_new_item), (swamiui_goto_zone_refitem), (swamiui_load_sample), (swamiui_cb_load_sample_okay), (swamiui_cb_paste_items): Some routine renaming away from sound font centric to more generic "patch" names. Changes to SwamiUITreeMenuCallback routines so that they are no longer callback specific. Save file multi item dialog implemented. Sample loading dialog implemented. * src/gui/SwamiUISpanWin.c: (swamiui_spanwin_set_mode), (swamiui_spanwin_set_sfitem), (swamiui_spanwin_update), (swamiui_spanwin_piano_set_octave): Added a routine to set the piano octave. Spans now update correctly on mode SpanWin mode change. * src/gui/SwamiUITreeMenu.c: (swamiui_treemenu_class_init), (swamiui_treemenu_activate), (treemenu_cb_selection_done), (swamiui_cb_wtbl_load_patch), (swamiui_cb_new_item), (swamiui_cb_goto_zone_refitem), (swamiui_cb_load_sample): Created a SwamiUITreeMenuCallback type to handle all menu callbacks. Updated callback handlers to use the new more specific non-callback functions and wrote wrappers where necessary. * src/libswami/Makefile.am: Added SwamiAudiofile.[ch] to the build. * src/libswami/SwamiAudiofile.c: (swami_audiofile_class_init), (swami_audiofile_driver_register_info), (swami_audiofile_select_driver), (find_driver_id_GCompareFunc), (swami_audiofile_get_driver_info), (swami_audiofile_init_driver), (swami_audiofile_load_sampledata), (swami_audiofile_init_sample), (swami_audiofile_open), (audiofile_okay), (swami_audiofile_close), (swami_audiofile_read): Audiofile loading is now working, a lot done (tired, must go to sleep). * src/libswami/SwamiObject.c: (swami_object_init), (swami_patch_load), (swami_patch_save), (swami_get_patch_list), (swami_item_insert), (swami_item_insert_before), (swami_item_new), (item_get_property): More renaming away from sound font centric routines to generic "patch" names. New routine `swami_patch_save' to save patch files. 2002-04-12 Josh Green * Makefile.am: Removed libltdl from automake SUBDIRS. * acinclude.m4: Removed unused macros left over from Smurf and added two new ones `AM_PATH_LIBSOUNDFONT' and `AM_PATH_IIWUSYNTH'. * autogen.sh: Removed build generation stuff for libswami as it is now one unified autoconf/automake build. * configure.in: Massive build changes, now a unified build system for libswami and gui. Should be cleaner with more checks for required libraries. * libltdl/*: Removed libltdl library, decided to use GModule. * src/gui/Makefile.am: Build changes, fixed splash_png.c generation, hopefully. * src/gui/SwamiUIObject.c: SwamiUITreeMenu activate callbacks now pass SwamiUITree object as the first parameter, so updated callbacks. (swamiui_cb_new_item): New SwamiUITreeMenu callback function to create a new item. (swamiui_cb_goto_zone_refitem): New SwamiUITreeMenu callback function to goto a zone's referenced item. * src/gui/SwamiUITree.c (swamiui_tree_init, tree_cb_item_prop_change): SFItem property change updates now update SwamiUITree. (swamiui_tree_freeze): Bug (bad cut and paste) that thawed instead of froze. (swamiui_tree_add_sfont, swamiui_tree_add_preset) (swamiui_tree_add_inst, swamiui_tree_add_sample): Now using `swami_item_get_formatted_name' to generate node labels for items. (swamiui_tree_item_set_pixmap): New function to set a pixmap in the first column of a SwamiUITree by sound font item. Was previously private and called `set_node_label'. * src/gui/SwamiUITreeMenu.c: Added an entry for SFITEM_SAMPLE_DATA to rmu_menus which caused the wrong menu options to be displayed for certain item types. Added callbacks for `New ' and `Goto ' menu entries. (swamiui_treemenu_activate): Now passing SwamiUITree object as the first parameter to menu item callbacks. * src/gui/help.c (swamiui_help_about): Commented out unused COMPILE_OPTIONS variable which will most likely be handled differently when the plugin system works. * src/gui/swami.glade: Added a couple of buttons to the Sample properties widget to allow selection of SampleData from a file or another sample, although neither is working yet. * src/gui/widgets/Makefile.am: Added GTK_CFLAGS to INCLUDES. * src/libswami/Makefile.am: Updated to be a part of the unified build system, as libswami is no longer built with a separate autoconf. * src/libswami/SwamiAudiofile.c: Minor changes, still not working yet. (swami_audiofile_load_into_sampledata): New function to create an SFSampleData object and load a sample into it. * src/libswami/SwamiObject.c: (swami_object_class_init): Added ITEM_PROP_CHANGE signal which is emitted when an SFItem's property is changed. (swami_item_new, new_item_unique_name): New function to create a unique SFItem and add it to the SwamiObject sound font tree. (swami_item_get_formatted_name): Updated to return formatted names suitable for SwamiUITree nodes. (swami_item_set_valist, swami_item_set_property, item_set_property): Updated to emit ITEM_PROP_CHANGE signal. * src/libswami/acinclude.m4: Removed, libswami now built from top. * src/libswami/configure.in: Removed, libswami now built from top. * src/libswami/marshals.c: Added to build. For custom signal marshallers. * src/libswami/marshals.h: Added to build. * src/plugins/Makefile.am: Statically compiling plugins for now until the plugin system works. 2002-04-07 Josh Green * src/gui/Makefile.am: Added SwamiUIProp.[ch] to the build. * src/gui/SwamiUIObject.c: Added multi sound font item properties dialog. * src/gui/SwamiUIProp.c: Properties object now working. * src/gui/SwamiUISampleView.c: Update for swami_item_get/set function renames. * src/gui/SwamiUISpanWin.c: Update for swami_item_get/set function renames. * src/gui/SwamiUITreeMenu.c: Enabled menu item for item `Properties'. * src/include/gobject2gtk.h: GValue strings are now dynamically allocated and freed on g_value_unset. * src/libswami/SwamiConfig.c: Fixed a bug where string config variables were set to static string values as defaults. Only occured when no config files present (caused segfault when opening a sound font file). * src/libswami/SwamiObject.c: Shortened swami_item_get/set_property functions to swami_item_get/set, i.e. removed "property". * src/libswami/SwamiObject.h: Rename of swami item get/set property functions. * src/libswami/gobject2gtk.c: Added g2g_value_unset to handle free of string values. * src/plugins/wavetbl_iiwusynth.c: Synchronized with iiwusynth header file which renamed all sfloader "delete" functions to "free". 2002-04-01 Josh Green * src/gui/Makefile.am: Added SwamiUISampleView.[ch] to the build. * src/gui/SwamiUISampleView.c: Added to CVS. * src/gui/SwamiUISampleView.h: Added to CVS. * src/gui/SwamiUIObject.c: Added sample view to interface. Created a swami_object variable to simplify things, don't need to cast swamiui_object anymore. * src/gui/SwamiUIProp.c: Messing with properties object, not functional yet. * src/gui/SwamiUISpanWin.c: Spans window implimented as a GtkList with a hack to allow KeySpan widget to grab mouse, still has some problems. * src/gui/SwamiUITree.h: Changed *_LAST enums to *_COUNT. * src/gui/util.h: Added RGB2GDK macro. * src/gui/widgets/keyspan.c: Synchronized with changes from Smurf, which adds some friendliness to the span widget. * src/gui/widgets/ptrstrip.c: Changed GtkPSPtr to PtrStripPointer. * src/gui/widgets/samview.c: Changed GtkSVMark to SamViewMark. * src/libswami/SwamiObject.c: Added more properties to SFSample items. * src/plugins/wavetbl_iiwusynth.c: Updated to use new libsoundfont sample storage management. swami-2.2.0/HACKERS000066400000000000000000000043661361104770400136520ustar00rootroot00000000000000Documentation for fellow hackers and those wishing to contribute code to the Swami project. Contributions without following these guidlines are ok, but it likely means more work for me, since I'll probably modify things so that it is correctly assimulated. Code style --------------- Code indentation style is pretty much the GNU style of the 'indent' command with a couple exceptions: Braces are not indented (i.e., they are on the same column as the statement they follow). Note that currently most of the project does in fact have 2 space brace indenting, but I'd like to change that. Also, pointer arguments in functions don't have a space between the * and the argument name (anyone know how to turn that off with indent?) Example: int function (GObject *obj, int arg) { int i; if (1 == 1) { for (i = 0; i < 0; i++) { printf ("This coding style is yummy!\n"); } } } The following indent command will get things almost right. If anyone knows how to turn off the spaces between the * in function pointer arguments and the argument name, do let me know. For some reason that just annoys the hell out of me ;) The other thing that is annoying is breaking lines on _("") macro i18n calls, but I doubt indent can be forced to not to do that. If I could fix those two issues I'd probably run indent on all the code, since the current bracing indentation style is not to my liking. indent -bli0 -sc -ncs myfile.c Commenting code --------------- Please comment all new public functions with GTK doc style comment blocks. Refcounting/memory allocation is also good to comment. Please do so like this: sf2 = ipatch_sf2_new (); /* ++ ref */ file = ipatch_file_new (); /* ++ ref */ ptr = g_new (MyStruct, 1); /* ++ alloc */ ... g_free (ptr); /* -- free */ g_object_unref (file); /* -- unref */ return (sf2); /* !! caller takes over reference */ So in other words, use "++" to indicate an allocate or object reference, "--" to indicate free or object unref and "!!" to indicate something special with allocation or reference counting (such as returning allocated or referenced object to caller). In this way it should be easy to scan a function quickly and notice if there are any ref/allocation issues (an unref forgotten, etc). swami-2.2.0/INSTALL000066400000000000000000000044321361104770400136720ustar00rootroot00000000000000+-------------------------------------------------------+ + Swami - Installation + + Copyright (C) 1999-2002 Josh Green + + Email: jgreen@users.sourceforge.net + + Swami homepage: http://swami.sourceforge.net + +-------------------------------------------------------+ If compiling from CVS ----------------- You need to run "./autogen.sh" in the toplevel directory. You'll need autoconf, automake, libtool and perhaps other libraries and there development packages in order for this to work. Configure build system ----------------- Change to toplevel Swami source directory and execute: ./configure Some useful options to the configure script ("./configure --help" to list more options) you probably won't need to specify any of these: --with-audiofile-prefix=PFX Prefix where AUDIOFILE is installed (optional) --disable-splash Don't compile Swami splash intro image TIPS in using the configure script ----------------- Support for external libraries is for the most part auto detected. So you probably won't need to supply any switches to the configure script. If the configure script does something unexpected or fails it is often useful to look at the config.log file. If you install or remove a library used by Swami and you want to re-run configure, you may want to delete config.cache first. Required libraries ----------------- Swami requires a sound file library to load sample files (WAV, AU etc). Currently only audiofile is supported (libsndfile support soon). audiofile homepage: http://www.68k.org/~michael/audiofile/ libsndfile homepage: http://www.zip.com.au/~erikd/libsndfile/ GTK 1.2+ is required. It depends on a number of other libraries as well. Chances are you already have it. GTK homepage: http://www.gtk.org Optional libraries ----------------- The libpng library is used for the splash intro image. If you don't have it you won't get the splash image. You can also force the splash image to not be compiled (save about 60k in the Swami binary, but lose some of the Swami atmosphere) with the --disable-splash switch. Compile and install ----------------- To compile the package execute: make To install the binary file "swami" issue a: make install Please contact me (Josh Green ) if you have problems or suggestions with the build process. swami-2.2.0/NEWS000066400000000000000000000015311361104770400133350ustar00rootroot00000000000000+-------------------------------------------------------+ + Smurf Sound Font Editor - News + + Copyright (C) 1999-2002 Josh Green + + Email: jgreen@users.sourceforge.net + + Smurf homepage: http://smurf.sourceforge.net + +-------------------------------------------------------+ Jun 5th 2002 - Swami v0.9.0pre1 released (yee ha!) --- Smurf Sound Font Editor (RIP) --- Sep 6th 2001 - Smurf v0.52.5 released May 5th 2001 - Smurf v0.52.1 released May 2nd 2001 - Smurf v0.52 released Jan 31st 2001 - Smurf v0.50.1 released Jan 1st 2001 - Smurf v0.50.0 released Sep 12th 2000 - Smurf v0.49.8 released Jul 5th 2000 - Smurf v0.49.5 released Mar 25th 2000 - Smurf v0.49.1 released Mar 20th 2000 - Smurf v0.49 released Dec 6th 1999 - Smurf v0.46 released Nov 8th 1999 - Smurf v0.45 released Sep 12th 1999 - Smurf v0.40 released, first public release! swami-2.2.0/README.OSX000066400000000000000000000130361361104770400141710ustar00rootroot00000000000000SWAMI-2.0 Installation on Mac OS X.5.5 (Leopard) ================================================ Ebrahim Mayat Josh Green Last updated: 9th October, 2008 Requirements: - fluidsynth - fink package distribution - X11.app, X11SDK.pkg, OpenGLSDK.pkg and QuickTimeSDK.pkg (Leopard Install DVD) - fink packages: cairo, gtk+2, gtk+2-dev, audiofile, flac, gettext, intltool, libtool14, libgnomecanvas2-dev, libxml2, pygtk2-py24-dev, pkgconfig, libart2, libglade2, pango1-xft2-ft219-dev, fftw3 and gtksourceview for example: "$ fink install gtk+2-dev" libinstpatch: configure && make && sudo make install: $ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure "LDFLAGS=-L/usr/local/lib -L/sw/lib" "CFLAGS=-I/usr/local/include -I/sw/include" SWAMI: configure && make && sudo make install Building an app bundle with GTK-OSX =================================== BALATON Zoltan Last updated: 16th November, 2013 Resources: http://www.gtk.org/download/macos.php IMPORTANT! If you have packages installed from MacPorts, Fink, Homebrew or other repositories you have to make sure that these are not visible while building GTK-OSX. The easiest way is to create a new user for this and run everything while logged in as that user. Mixing GTK-OSX with packages from other builds will fail so make sure your path and environment is clean before continuing! 1. Build GTK-OSX (see: https://live.gnome.org/GTK%2B/OSX/Building) - Download and run gtk-osx-build-setup.sh - Set up your environment: $ cat <>$HOME/.profile DEVELOPER_DIR="/Volumes/10.7/Developer" PATH="$DEVELOPER_DIR/usr/bin":$PATH:"$HOME/.local/bin" export DEVELOPER_DIR PATH EOF $ . $HOME/.profile - Customise .jhbuildrc-custom for example: setup_sdk("10.6", sdk_version="10.6", architectures=["i386"]) moduleset=os.environ['HOME'] + '/custom.modules' module_autogenargs['cairo'] = '--enable-ft=no' skip=["gnome-doc-utils", "cups"] - Bootstrap jhbuild and build some packages $ jhbuild shell # make sure to enter the jhbuild shell first $ jhbuild bootstrap $ jhbuild build meta-gtk-osx-bootstrap $ jhbuild build meta-gtk-osx-freetype $ jhbuild build python $ jhbuild build meta-gtk-osx-core 2. Teach jhbuild about some more dependencies and build them - Edit $HOME/custom.modules to have something like this: - Build them: $ jhbuild shell # re-enter shell if exited from it, otherwise not needed $ jhbuild build libfftw3 $ jhbuild build fluidsynth $ jhbuild build libgnomecanvas $ jhbuild build libxml2 3. Now we could teach jhbuild to also build libinstpatch and swami like above if we built a release from tarballs but I simply built these by hand instead: $ jhbuild shell # always use the shell for anything needing stuff built by jhbuild $ cd $HOME/swami/src/libinstpatch $ ./configure --prefix=$HOME/swami/inst --build=i386-apple-darwin12.5.0 \ --host=i386-apple-darwin12.5.0 --target=i386-apple-darwin12.5.0 $ make && make install $ cd $HOME/swami/src/swami $ ./configure --prefix=$HOME/swami/inst --build=i386-apple-darwin12.5.0 \ --host=i386-apple-darwin12.5.0 --target=i386-apple-darwin12.5.0 \ PKG_CONFIG_PATH=$HOME/swami/inst/lib/pkgconfig:$PKG_CONFIG_PATH $ make && make install 4. Now we have everything built, let's make an app bundle - Install gtk-mac-bundler (see https://live.gnome.org/GTK%2B/OSX/Bundling) $ cd $HOME/Source $ git clone git://git.gnome.org/gtk-mac-bundler $ cd gtk-mac-bundler $ make install - After reviewing the swami.bundle file for correct paths build a bundle: $ cd $HOME/swami/src/swami/package/osxbundle $ jhbuild run gtk-mac-bundler swami.bundle $ ln -s Resources/lib/swami $HOME/swami/Swami.app/Contents/PlugIns swami-2.2.0/README.md000066400000000000000000000400061361104770400141150ustar00rootroot00000000000000# Swami - README ### Copyright (C) 1999-2019 Element Green and others http://www.swamiproject.org [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/swami.swami?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=2&branchName=master) _Note that this readme might be outdated_ ## 1. What is Swami? Swami (Sampled Waveforms And Musical Instruments) is a SoundFont editor. SoundFont files are a collection of audio samples and other data that describe instruments for the purpose of composing music. SoundFont files do not describe the music itself, but rather the sounds of the instruments. These instruments can be composed of any digitally recordable or generated sound. This format provides a portable and flexible sound synthesis environment that can be supported in hardware or software. ## 2. What happend to the Smurf SoundFont Editor? Nothing actually. Since I was already doing an entire re-code of Smurf I decided it would be good to change the name to give the program a fresh start and because I'm planning to make it more than just a SoundFont editor. Also the word 'Smurf' is copyrighted so I thought a change wise in this day and age of law suits. Swami does not yet completely replace Smurf in functionality, but it will. ## 3. License See [Copying](https://github.com/swami/swami/blob/master/COPYING). ## 4. Changes from Smurf Most of the changes have occured in the programming architecture. Much of this work has been done in preparation for really cool features (TM) :) The SoundFont editing code is now completely abstracted from the GUI in separate shared libraries making things like: scripting support, multiple client concurrent access to SoundFont objects and SwamiJam (networked semi-realtime composition with friends) more feasible. Most importantly things are much cleaner now so I don't go insane in attempts to add functionality. New features: - FluidSynth soft synth support (actually its required) which allows any OS supported sound card to be used and gives us modulators (real time effects) and routeable digital audio output for adding additional effects and for easy recording. - More operations work for multiple items. Including loading/saving/closing of multiple SoundFont files, loading multiple samples, and setting/viewing properties on multiple items. - Modulator editor for editing real time control parameters - Default toggle buttons for generator (effect) controls Things missing (as of this README): - Undo support - virtual SoundFont bank support - OSS AWE/SB Live! wavetable backend ## 5. Requirements Look at the INSTALL file for instructions on compiling and installing Swami and for more details on software requirements. Version numbers listed are what I developed Swami on, older or newer versions of the listed software may or may not work, but give it a try! And do let me know if you encounter any problems. Swami has the following requirements: - Linux, Windows and Mac OS X. Probably anywhere else GTK+ will run. - GTK+ v2.0 - FluidSynth v2.0 (software wavetable synthesizer) - libsndfile GTK homepage: http://www.gtk.org FluidSynth homepage: http://www.fluidsynth.org libsndfile homepage: http://www.mega-nerd.com/libsndfile ALSA homepage: http://www.alsa-project.org OSS homepage: http://www.4front-tech.com ## 6. Supported sound cards Any FluidSynth supported sound card (i.e. works under ALSA or OSS in Linux), since Swami uses FluidSynth which does all synthesis in software. A wavetable backend is planned for using hardware based SoundFont synthesizers like the SB AWE/Live! for those who don't want to use their precious CPU cycles :) ## 7. Features Current features include: - Loading of multiple SoundFont 2.x files *.SF2 - Saving of multiple SoundFont files - Copy Samples/Instruments/Presets between and within SoundFont objects - Load and export multiple audio sample files (WAV, AU, etc) - View/set multiple Preset, Instrument and Sample parameters - A GTK piano widget to play notes with computer keyboard/mouse - Wavetable loading of entire SoundFont - Piano MIDI controls (Pitch bender amount, volume etc.) Features provided by FluidSynth: - Flexible driver system that supports OSS, ALSA, LADSPA, Jack, Midishare, etc - Real time effects via modulators and C API ## 8. Future plans The following are in the works or planned: - Undo support (just haven't written a front end for it really) - Copy/Paste generator parameters - Graphical manipulation of generator parameters Was part of Smurf, not yet added: - Loading, saving and editing of virtual bank (*.bnk) files - Multiple undo/redo tree system, allowing for multiple redo "branches" - Sample cut support - Sample waveform generation - International language support (currently disabled until there are some updated translations, contact me if interested) ## 9. Troubleshooting drivers (or why does Swami crash on startup?) [Some of these switches haven't been enabled yet!] Some new command line switches were added to help with trouble shooting problems with Swami. Crashes experienced on startup of Swami are often caused by audio drivers. Type "swami --help" to get a list of usable switches. Since Swami, by default, tries to open the drivers on start up, a way to disable this behavior was added. "swami -d" will cause Swami to start, but drivers will not automatically be opened. Once started you can change your driver preferences (set certain ones to NONE) to try to determine which driver is crashing. Then issue a Control->"Restart Drivers" to cause the drivers to be re-opened. Swami v0.9.0pre1 Copyright 1999-2002 Josh Green Distributed under the GNU General Public License Usage: swami [options] Options: -h, --help Display this information -d Disable drivers (try if Swami crashes on start up) -c Ignore preferences and use factory defaults ## 10. Using Virtual Keyboard interface [Most of this is true, although some things haven't been implemented yet] The wavetable and piano interface have been changed in Smurf v0.52. Some of the new features include: * MIDI channel control (on toolbar, titled "Chan") Sets what MIDI channel the piano plays on. All other MIDI controls (bank, preset and MIDI controls on pop up menu) are remembered for each channel. * MIDI bank and preset controls (also on toolbar) Sets the current bank and preset for the selected MIDI channel. These are often changed automatically by the user interface when selecting items in the SoundFont tree, they can be changed manually as well. * Entire SoundFont loading Allows you to load an entire SoundFont. This is particularly useful to make a SoundFont available for sequencing with other programs. * Controls for changing how interface works with wavetable * Piano follows selection When enabled: virtual keyboard will "follow" the SoundFont tree selection. This will cause the selected item to be the one heard on the piano. If selected item is a Preset in a loaded SoundFont, then the piano will select the Bank:Preset of that item. Otherwise it is set to the Temporary Audible Bank:Preset (configurable from preferences, defaults to 127:127). By disabling this feature you can have more control over what is being played by the virtual keyboard. * Auto load temp audible When enabled: selected items in the SoundFont tree that aren't Presets in a loaded SoundFont, are automatically loaded into the wavetable device. They are mapped to a temporary Bank:Preset which is configurable from Preferences. Disabling this allows for faster editing, if one does not care to hear items. * Temporary audible - The temporary audible enables you to listen to components of a SoundFont that aren't directly mapped to a MIDI Bank:Preset. This is done by designating a Bank:Preset for temporary use. It defaults to 127:127 but can be changed in Preferences. When a non-Preset or a Preset that isn't in a loaded SoundFont is loaded (either from the "Auto load temp audible" option or manually from the "Wavetable Load" item on the right click menus), it is mapped to this temporary bank and preset number. ## 11. Creating Preset/Instrument zones To add zones to an Instrument hold down the CTRL key and select the samples you want to be added (SHIFT works too for selecting a range of items). Then "Right Click" the Instrument you want to add the samples to. Select "Paste" from the pop up menu. A zone will be created for each Sample (or Instrument zone) you selected. This same procedure can be applied to Presets using Instruments as zones. To create a "Global Zone", "Right Click" on the Preset or Instrument you want to create the zone under. Select "Global Zone" from the pop up menu. Global Zones are used to set "Default" generator (and modulator) parameters that the other zones don't already have set. ## 12. Using the sample viewer The sample viewer is used to change loop points for samples and instrument zones. Some things have changed in the sample viewer and probably will be changing again. The sample zoom/unzoom is now performed with the SHIFT key held down. Regular left mouse clicks are now used for sample selections for audio editing functions (currently only "cut"). Tips to using the sample viewer: Zooming IN/OUT Hold down SHIFT on the computer keyboard and click and hold the LEFT mouse button in the sample window, a white line will appear to mark your click, move the mouse to the left or the right to zoom into the sample. The distance you move the mouse from the original point of your click determines the speed in which the view zooms. If you then move the mouse to the other side of the line the view will unzoom. The RIGHT mouse button does the same thing, except the first direction you move the mouse unzooms the view. Selecting audio data Left click and drag to mark an area. An existing selection can be changed by holding down CTRL and Left or Right clicking to change the left or right ends of the selection respectively. ## 13. Undo system **Not enabled yet!** Swami has a rather flexible undo system. Many actions aren't currently undo-able, but will be implimented soon. Currently all actions relating to new SoundFont items, pasted SoundFont items, and deleted items are undo-able. The undo system is based on a tree rather than queue or list. This means that multiple changes can be easily tested. A traditional undo system is oriented as a list. Here is an example to help understand the Swami undo tree system compared with the traditional one. 1. You change some parameters related to the Modulation Envelope of an instrument 2. You don't quite like the changes, so you undo them 3. You try some other values for the same Modulation Envelope Lets say now you want to try the parameters in Step 1 again to compare to the settings in Step 3. In a traditional undo system you would not be able to do this because Step 3 would cause the "redo" information from Step 2 to be lost. Using a tree, multiple branches of "state" information can be stored. So rather than Step 3 "over writing" the redo info from Step 2, it would simply create another "branch" in the tree. Note: This was just an example of usage, but Swami doesn't support undo-ing of parameter changes yet. When just using the Undo/Redo menu items the undo system acts as a traditional list oriented undo system. The "Undo History" item on the Edit menu will bring up a dialog that lists previous actions. The dialog buttons available are Undo, Redo, Jump, Back, Forward, < (left arrow), > (right arrow). The current location in the state history is indicated by 2 greater than symbols ">>" in the left most column. Undo and Redo act just like the menu items, undo-ing (moving up the undo history) and redo-ing (moving down the undo history) the next/previous action from the current location. The "Jump" button will jump to the desired position in the undo history, causing actions to be undone/redone as necessary to realize the desired state. The Back and Forward buttons act like the traditional web browser function. Currently the last 10 positions in the undo history are remembered. This makes testing different settings easier. This isn't really useful yet as the currently undo-able actions aren't really ones you would wan't to test different settings with. But in the future when generator parameters are tracked in the undo history, this will be useful. The < (left arrow) and > (right arrow) are the second and third column headers of the history list. These columns are used to indicate other branches in the tree. Items that have other "sibling" branches will have a number in brackets (example: [ 1]) listed in the < (left arrow) and/or > (right arrow) column(s). The left arrow switches to older branches the right arrow to newer ones. Numbers in these columns indicate how many alternate older or newer branches (left arrow or right arrow respectively) are available. To switch to an alternate branch, select an item that has a number in one of the arrow columns. Click the corresponding arrow button at the top of the list. The selected item and all items below will be changed to reflect the new branch. ## 14. Virtual Banks **Not implemented yet in Swami!** Virtual banks allow you to make a SoundFont bank from many other SoundFont files. Swami currently supports loading and saving of the AWE .bnk format. This is a simple text file format listing each Preset and where it can be found SoundFont file:Bank:Preset and its destination Bank:Preset. **NOTE:** Make sure you set the "Virtual SoundFont bank search paths" field in Preferences. It should be a colon seperated list of where to find your SoundFont files. Example: "/home/josh/sbks/*:/tmp/sbks". Adding a '/*' to the end of a path will cause that directory to be recursively scanned (all sub directories under it). **Current limitations (will be fixed in future)** SoundFont files will not automatically be loaded with a virtual bank, so you'll have to load them by hand. Default SoundFont bank can't be cleared Can't edit virtual bank mappings #### Adding a Preset to a virtual bank Simply select and paste Preset items into the Mapping section of the virtual bank. #### Setting default SoundFont The default SoundFont sets the default Preset instruments. The Preset mappings override the default Presets. To change the default SoundFont simply paste a SoundFont item to the branch of the virtual bank. ## 15. SoundFont information, links and other resources I wrote an "Intro to SoundFonts" and its available on the Swami homepage http://swami.sourceforge.net. Its a general overview of what a SoundFont is. More information on the SoundFont standard can be obtained from www.soundfont.com. There is a PDF of the SoundFont technical specification at www.soundfont.com/documents/sfspec24.pdf. If you find that it has moved then look under the Resources section off of the soundfont.com home page (although it looks like you have to get a login to go there). Thanks to Frank Johnson at EMU for clarifying this. ## 16. Thanks Thanks to the following projects for making Swami possible: - FluidSynth (its what makes the instruments sound :) - gstreamer for gobject2gtk code and helpful examples of using GObject - Glade GTK interface builder - Gimp for graphics editing - Blender for render of campfire in desert Splash image - xmms code which I borrowed the multi selection file dialog modifications from - Piano to computer keyboard mapping inspired from soundtracker ## 17. Trademark Acknowledgement SoundFont is a registered trademark of E-mu Systems, Inc. Sound Blaster Live! and Sound Blaster AWE 32/64 are registered trademarks of Creative Labs Inc. All other trademarks are property of their respective holders. ## 18. Contact Contact me (Element Green) particularly if you are interested in helping develop Swami. Programming, web page design, and documentation are all things that are lacking, so if you have an interest, do contact me. Also contact me if you find any bugs, have trouble compiling on a platform you think should be supported, want to tell me how much you like Swami, or have suggestions or ideas. Email: element@elementsofsound.org Swami homepage: http://www.swamiproject.org swami-2.2.0/TODO.tasks000066400000000000000000000226431361104770400144610ustar00rootroot00000000000000 CRAM read/write Preferences FluidSynth panel Multi-level paste (Sample -> Preset) External sample editing Sample cut/crop Sample dicer Highlight ranges on mouse over and status bar Use real unit value for effect controls Default lower view to SwamiPanel interfaces SoundFont sample start/end ranges GigaSampler dimension editor Patch item interface selection buttons Box select note/velocity ranges, move as group and set as group Highlight piano buttons on mouse over Integrate sample tuner into sample editor Add fundamental tuning waveform playback Implement loops as canvas ranges Loop finder built into sample viewer Invalid value detection (duplicate names, invalid loops, etc) Interface editor property toggle Error output dialog Re-implement save dialog Append file extensions on save (if not specified) Ignore key presses with piano with certain widgets Don't play piano when typing in text entries or views. Add "Load Samples" menu item to Samples branch in tree Keyboard octave selector and root note selector above piano Smarter keyboard piano key mapping Re-enable save dirty flag check on exit Some right click menu items wrong Copy, Close and Delete are on menu when they shouldn't be sometimes. Multi-item options should check all items in selection. Search position/deactivation when tree is clicked Prevent files from being opened multiple times Handle stereo samples correctly with SoundFont files SF2IZone loop override negative values broken Piano crashes when out of view and note is played Changing preset bank/program not resorting New Zone doesn't work Remove New Zone option or allow empty zones that need samples assigned to them. swami-2.2.0/azure-pipelines.yml000066400000000000000000000144271361104770400165050ustar00rootroot00000000000000# C/C++ with GCC # Build your C/C++ project with GCC using make. # Add steps that publish test results, save build artifacts, deploy, and more: # https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc jobs: - job: Linux pool: vmImage: 'ubuntu-latest' steps: - script: | sudo apt-get update sudo apt-get -y install \ libglib2.0-0 \ libsndfile-dev \ libfluidsynth-dev \ libgnomecanvas2-dev \ libgtk2.0-dev displayName: 'Prerequisites' - script: | git clone https://github.com/swami/libinstpatch.git cd libinstpatch mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DLIB_SUFFIX="" .. make -j4 sudo make install displayName: 'Compile & Install libinstpatch' - script: | git clone https://github.com/FluidSynth/fluidsynth.git cd fluidsynth mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DLIB_SUFFIX="" .. make -j4 sudo make install displayName: 'Compile & Install fluidsynth' - script: | mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=$HOME/swami_install -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 .. make sudo make install displayName: 'Compile & Install swami' - job: macOS pool: vmImage: 'macOS-10.14' steps: - script: | brew update brew install glib gobject-introspection libsndfile pkg-config fluidsynth gtk+ libgnomecanvas displayName: 'Prerequisites' - script: | git clone https://github.com/swami/libinstpatch.git cd libinstpatch mkdir build && cd build export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" cmake -DINTROSPECTION_ENABLED=0 -DLIB_SUFFIX="" .. make -j4 sudo make install displayName: 'Compile & Install libinstpatch' - script: | mkdir build && cd build export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" cmake -DINTROSPECTION_ENABLED=0 -DCMAKE_INSTALL_PREFIX=$HOME/swami_install -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 .. make displayName: 'Compile swami' - job: Windows strategy: matrix: MinGW_x86: platform: Win32 toolset: v141_xp gtk-bundle: $(gtk-bundle-x86) libsndfile-url: $(libsndfile-url-x86) libgnomecanvas-url: $(libgnomecanvas-url-x86) libgnomecanvas-dev-url: $(libgnomecanvas-dev-url-x86) libart-url: http://ftp.gnome.org/pub/gnome/binaries/win32/libart_lgpl/2.3/libart-lgpl_2.3.21-1_win32.zip libart-dev-url: http://ftp.gnome.org/pub/gnome/binaries/win32/libart_lgpl/2.3/libart-lgpl-dev_2.3.21-1_win32.zip mingw-url: $(mingw-url-x86) artifact-name: "swami" imageName: 'vs2017-win2016' pool: vmImage: $(imageName) steps: - task: DownloadBuildArtifacts@0 inputs: buildType: specific # https://dev.azure.com/tommbrt/_apis/projects?api-version=5.0 project: 'd3638885-de4a-4ce7-afe7-f237ae461c07' pipeline: 3 artifactName: fluidsynth-$(platform) downloadPath: '$(Build.ArtifactStagingDirectory)' displayName: 'Get fluidsynth' - task: DownloadBuildArtifacts@0 inputs: buildType: specific # https://dev.azure.com/tommbrt/_apis/projects?api-version=5.0 project: 'd3638885-de4a-4ce7-afe7-f237ae461c07' pipeline: 1 artifactName: libinstpatch-$(platform) downloadPath: '$(Build.ArtifactStagingDirectory)' displayName: 'Get libinstpatch' - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 curl -LfsS -o libgnomecanvas.zip $(libgnomecanvas-url) || exit -1 curl -LfsS -o libgnomecanvas-dev.zip $(libgnomecanvas-dev-url) || exit -1 curl -LfsS -o libart.zip $(libart-url) || exit -1 curl -LfsS -o libart-dev.zip $(libart-dev-url) || exit -1 curl -LfsS -o mingw.zip $(mingw-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 7z x -aos -- libgnomecanvas.zip > NUL || exit -1 7z x -aos -- libgnomecanvas-dev.zip > NUL || exit -1 7z x -aos -- libart.zip > NUL || exit -1 7z x -aos -- libart-dev.zip > NUL || exit -1 7z x -aos -- mingw.zip > NUL || exit -1 rm *.zip REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 cd mingw*\ && cp -rf * .. && cd .. && rm -rf mingw* || exit -1 cd $(Build.ArtifactStagingDirectory)\fluidsynth-$(platform) && cp -rf * d:\deps\ && cd .. && rm -rf $(Build.ArtifactStagingDirectory)\fluidsynth-$(platform)\ || exit -1 cd $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform) && cp -rf * d:\deps\ && cd .. && rm -rf $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform)\ || exit -1 displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" REM remove that path from PATH to make sure sh.exe is not found (cmake will complain otherwise) set PATH=%PATH:C:\Program Files\Git\bin;=% set PATH=%PATH:C:\Program Files\Git\usr\bin;=% mkdir build && cd build || exit -1 cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=0 .. || exit -1 mingw32-make.exe all || exit -1 displayName: 'Compile swami' - script: | @ECHO ON cd build mingw32-make.exe install || exit -1 cp d:/deps/bin/libgnomecanvas*.dll $(Build.ArtifactStagingDirectory) del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll displayName: 'Copy Artifacts' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: $(artifact-name)-$(platform) swami-2.2.0/cmake/000077500000000000000000000000001361104770400137165ustar00rootroot00000000000000swami-2.2.0/cmake/CheckDIRSymbolExists.cmake000066400000000000000000000066671361104770400207010ustar00rootroot00000000000000# - Check if the DIR symbol exists like in AC_HEADER_DIRENT. # CHECK_DIRSYMBOL_EXISTS(FILES VARIABLE) # # FILES - include files to check # VARIABLE - variable to return result # # This module is a small but important variation on CheckSymbolExists.cmake. # The symbol always searched for is DIR, and the test programme follows # the AC_HEADER_DIRENT test programme rather than the CheckSymbolExists.cmake # test programme which always fails since DIR tends to be typedef'd # rather than #define'd. # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link MACRO(CHECK_DIRSYMBOL_EXISTS FILES VARIABLE) IF(NOT DEFINED ${VARIABLE}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") SET(MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) IF(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") ELSE(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS) ENDIF(CMAKE_REQUIRED_LIBRARIES) IF(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") ELSE(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES) ENDIF(CMAKE_REQUIRED_INCLUDES) FOREACH(FILE ${FILES}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") ENDFOREACH(FILE) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nint main()\n{if ((DIR *) 0) return 0;}\n") CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c" @ONLY) MESSAGE(STATUS "Looking for DIR in ${FILES}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS} "${CHECK_DIRSYMBOL_EXISTS_LIBS}" "${CMAKE_DIRSYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - found") SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeOutput.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "passed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - not found.") SET(${VARIABLE} "" CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "failed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) ENDIF(NOT DEFINED ${VARIABLE}) ENDMACRO(CHECK_DIRSYMBOL_EXISTS) swami-2.2.0/cmake/CheckPrototypeExists.cmake000066400000000000000000000023741361104770400210710ustar00rootroot00000000000000# - Check if the prototype for a function exists. # CHECK_PROTOTYPE_EXISTS (FUNCTION HEADER VARIABLE) # # FUNCTION - the name of the function you are looking for # HEADER - the header(s) where the prototype should be declared # VARIABLE - variable to store the result # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # Copyright (c) 2006, Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. INCLUDE(CheckCSourceCompiles) MACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) SET(_INCLUDE_FILES) FOREACH (it ${_HEADER}) SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") ENDFOREACH (it) SET(_CHECK_PROTO_EXISTS_SOURCE_CODE " ${_INCLUDE_FILES} int main() { #ifndef ${_SYMBOL} int i = sizeof(&${_SYMBOL}); #endif return 0; } ") CHECK_C_SOURCE_COMPILES("${_CHECK_PROTO_EXISTS_SOURCE_CODE}" ${_RESULT}) ENDMACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) swami-2.2.0/cmake/CheckSTDC.cmake000066400000000000000000000023151361104770400164140ustar00rootroot00000000000000message(STATUS "Checking whether system has ANSI C header files") include(CheckPrototypeExists) include(CheckIncludeFiles) check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" StandardHeadersExist) if(StandardHeadersExist) check_prototype_exists(memchr string.h memchrExists) if(memchrExists) check_prototype_exists(free stdlib.h freeExists) if(freeExists) message(STATUS "ANSI C header files - found") set(STDC_HEADERS 1 CACHE INTERNAL "System has ANSI C header files") set(HAVE_STRINGS_H 1) set(HAVE_STRING_H 1) set(HAVE_FLOAT_H 1) set(HAVE_STDLIB_H 1) set(HAVE_STDDEF_H 1) set(HAVE_STDINT_H 1) set(HAVE_INTTYPES_H 1) set(HAVE_DLFCN_H 1) endif(freeExists) endif(memchrExists) endif(StandardHeadersExist) if(NOT STDC_HEADERS) message(STATUS "ANSI C header files - not found") set(STDC_HEADERS 0 CACHE INTERNAL "System has ANSI C header files") endif(NOT STDC_HEADERS) check_include_files(unistd.h HAVE_UNISTD_H) include(CheckDIRSymbolExists) check_dirsymbol_exists("sys/stat.h;sys/types.h;dirent.h" HAVE_DIRENT_H) if (HAVE_DIRENT_H) set(HAVE_SYS_STAT_H 1) set(HAVE_SYS_TYPES_H 1) endif (HAVE_DIRENT_H) swami-2.2.0/cmake/DefaultDirs.cmake000066400000000000000000000027121361104770400171300ustar00rootroot00000000000000# Several directory names used by libInstPatch to install files # the variable names are similar to the KDE4 build system include ( GNUInstallDirs ) # BUNDLE_INSTALL_DIR - Mac only: the directory for application bundles set (BUNDLE_INSTALL_DIR "/Applications" CACHE STRING "The install dir for application bundles") mark_as_advanced (BUNDLE_INSTALL_DIR) # FRAMEWORK_INSTALL_DIR - Mac only: the directory for framework bundles set (FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "The install dir for framework bundles") mark_as_advanced (FRAMEWORK_INSTALL_DIR) # PLUGINS_DIR - the directory where application plugins will be installed if ( UNIX ) set (PLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/swami" CACHE STRING "The install dir for plugins") else ( UNIX ) set (PLUGINS_DIR "plugins" CACHE STRING "The install dir for plugins") endif ( UNIX ) mark_as_advanced (PLUGINS_DIR) # IMAGES_DIR - the directory where images are installed if ( UNIX ) set (IMAGES_DIR "${CMAKE_INSTALL_DATADIR}/swami/images" CACHE STRING "The install dir for images") else ( UNIX ) set (IMAGES_DIR "images" CACHE STRING "The install dir for images") endif ( UNIX ) mark_as_advanced (IMAGES_DIR) # UIXML_DIR - the directory where UI XML is installed if ( UNIX ) set (UIXML_DIR "${CMAKE_INSTALL_DATADIR}/swami" CACHE STRING "The install dir for UI XML files") else ( UNIX ) set (UIXML_DIR "." CACHE STRING "The install dir for UI XML files") endif ( UNIX ) mark_as_advanced (UIXML_DIR) swami-2.2.0/cmake/FindGtkDoc.cmake000066400000000000000000000043751361104770400167050ustar00rootroot00000000000000# CMake macros to find the GtkDoc documentation system # Output variables: # # GTKDOC_FOUND ... set to 1 if GtkDoc was found # # If GTKDOC_FOUND == 1: # # GTKDOC_SCAN_EXE ... the location of the gtkdoc-scan executable # GTKDOC_SCANGOBJ_EXE ... the location of the gtkdoc-scangobj executable # GTKDOC_MKTMPL_EXE ... the location of the gtkdoc-mktmpl executable # GTKDOC_MKDB_EXE ... the location of the gtkdoc-mkdb executable # GTKDOC_MKHTML_EXE ... the location of the gtkdoc-mkhtml executable # GTKDOC_FIXXREF_EXE ... the location of the gtkdoc-fixxref executable set(GTKDOC_FOUND 1) find_program(GTKDOC_SCAN_EXE gtkdoc-scan PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_SCAN_EXE) message(STATUS "gtkdoc-scan not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_SCAN_EXE) find_program(GTKDOC_SCANGOBJ_EXE gtkdoc-scangobj PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_SCANGOBJ_EXE) message(STATUS "gtkdoc-scangobj not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_SCANGOBJ_EXE) get_filename_component(_this_dir ${CMAKE_CURRENT_LIST_FILE} PATH) find_file(GTKDOC_SCANGOBJ_WRAPPER GtkDocScanGObjWrapper.cmake PATH ${_this_dir}) if(NOT GTKDOC_SCANGOBJ_WRAPPER) message(STATUS "GtkDocScanGObjWrapper.cmake not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_SCANGOBJ_WRAPPER) find_program(GTKDOC_MKTMPL_EXE gtkdoc-mktmpl PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_MKTMPL_EXE) message(STATUS "gtkdoc-mktmpl not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_MKTMPL_EXE) find_program(GTKDOC_MKDB_EXE gtkdoc-mkdb PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_MKDB_EXE) message(STATUS "gtkdoc-mkdb not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_MKDB_EXE) find_program(GTKDOC_MKHTML_EXE gtkdoc-mkhtml PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_MKHTML_EXE) message(STATUS "gtkdoc-mkhtml not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_MKHTML_EXE) find_program(GTKDOC_FIXXREF_EXE gtkdoc-fixxref PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_FIXXREF_EXE) message(STATUS "gtkdoc-fixxref not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_FIXXREF_EXE) find_program(GTKDOC_CHECK_EXE gtkdoc-check PATH "${GLIB_PREFIX}/bin") if(NOT GTKDOC_CHECK_EXE) message(STATUS "gtkdoc-check not found") set(GTKDOC_FOUND 0) endif(NOT GTKDOC_CHECK_EXE) swami-2.2.0/cmake/GtkDocScanGObjWrapper.cmake000066400000000000000000000024261361104770400210070ustar00rootroot00000000000000if(NOT APPLE) # We use pkg-config to fing glib et al find_package(PkgConfig) # Find glib et al pkg_check_modules(GLIB REQUIRED glib-2.0 gobject-2.0) foreach(_flag ${EXTRA_CFLAGS} ${GLIB_CFLAGS}) set(ENV{CFLAGS} "$ENV{CFLAGS} \"${_flag}\"") endforeach(_flag) foreach(_flag ${EXTRA_LDFLAGS} ${GLIB_LDFLAGS}) set(ENV{LDFLAGS} "$ENV{LDFLAGS} \"${_flag}\"") endforeach(_flag) foreach(_flag ${EXTRA_LDPATH}) if(ENV{LD_LIBRARY_PATH}) set(ENV{LD_LIBRARY_PATH} "$ENV{LD_LIBRARY_PATH}:\"${_flag}\"") else(ENV{LD_LIBRARY_PATH}) set(ENV{LD_LIBRARY_PATH} "${_flag}") endif(ENV{LD_LIBRARY_PATH}) endforeach(_flag) message(STATUS "Executing gtkdoc-scangobj with:") message(STATUS " CFLAGS: $ENV{CFLAGS}") message(STATUS " LDFLAGS: $ENV{LDFLAGS}") message(STATUS " LDPATH: $ENV{LD_LIBRARY_PATH}") execute_process(COMMAND ${GTKDOC_SCANGOBJ_EXE} "--module=${doc_prefix}" "--types=${output_types}" "--output-dir=${output_dir}" "--type-init-func=${type_init_func}" WORKING_DIRECTORY "${output_dir}" RESULT_VARIABLE _scan_result) if(_scan_result EQUAL 0) message(STATUS "Scan succeeded.") else(_scan_result EQUAL 0) message(SEND_ERROR "Scan failed.") endif(_scan_result EQUAL 0) endif(NOT APPLE) # vim:sw=4:ts=4:et:autoindent swami-2.2.0/cmake/UnsetPkgConfig.cmake000066400000000000000000000010411361104770400176020ustar00rootroot00000000000000macro ( unset_pkg_config _prefix ) unset ( ${_prefix}_VERSION CACHE ) unset ( ${_prefix}_PREFIX CACHE ) unset ( ${_prefix}_CFLAGS CACHE ) unset ( ${_prefix}_CFLAGS_OTHER CACHE ) unset ( ${_prefix}_LDFLAGS CACHE ) unset ( ${_prefix}_LDFLAGS_OTHER CACHE ) unset ( ${_prefix}_LIBRARIES CACHE ) unset ( ${_prefix}_INCLUDEDIR CACHE ) unset ( ${_prefix}_INCLUDE_DIRS CACHE ) unset ( ${_prefix}_LIBDIR CACHE ) unset ( ${_prefix}_LIBRARY_DIRS CACHE ) unset ( __pkg_config_checked_${_prefix} CACHE ) endmacro ( unset_pkg_config ) swami-2.2.0/cmake/UseGtkDoc.cmake000066400000000000000000000216741361104770400165620ustar00rootroot00000000000000# CMake macros to use the GtkDoc documentation system find_package(GtkDoc) # gtk_doc_add_module(doc_prefix sourcedir # [XML xmlfile] # [FIXXREFOPTS fixxrefoption1...] # [IGNOREHEADERS header1...] # [DEPENDS depend1...] ) # # sourcedir must be the *full* path to the source directory. # # If omitted, sgmlfile defaults to the auto generated ${doc_prefix}/${doc_prefix}-docs.xml. function(gtk_doc_add_module _doc_prefix _doc_sourcedir) set (_multi_value DEPENDS XML FIXXREFOPTS IGNOREHEADERS CFLAGS LDFLAGS LDPATH SUFFIXES TYPEINITFUNC EXTRAHEADERS) cmake_parse_arguments (ARG "" "" "${_multi_value}" ${ARGN}) list(LENGTH ARG_XML _xml_file_length) if(ARG_SUFFIXES) set(_doc_source_suffixes "") foreach(_suffix ${ARG_SUFFIXES}) if(_doc_source_suffixes) set(_doc_source_suffixes "${_doc_source_suffixes},${_suffix}") else(_doc_source_suffixes) set(_doc_source_suffixes "${_suffix}") endif(_doc_source_suffixes) endforeach(_suffix) else(ARG_SUFFIXES) set(_doc_source_suffixes "h") endif(ARG_SUFFIXES) set(_do_all ALL) set(_opts_valid 1) if(NOT _xml_file_length LESS 2) message(SEND_ERROR "Must have at most one sgml file specified.") set(_opts_valid 0) endif(NOT _xml_file_length LESS 2) if(_opts_valid) # a directory to store output. set(_output_dir "${CMAKE_CURRENT_BINARY_DIR}/${_doc_prefix}") set(_output_dir_stamp "${_output_dir}/dir.stamp") # set default sgml file if not specified set(_default_xml_file "${_output_dir}/${_doc_prefix}-docs.xml") get_filename_component(_default_xml_file ${_default_xml_file} ABSOLUTE) # a directory to store html output. set(_output_html_dir "${_output_dir}/html") set(_output_html_dir_stamp "${_output_dir}/html_dir.stamp") # The output files set(_output_decl_list "${_output_dir}/${_doc_prefix}-decl-list.txt") set(_output_decl "${_output_dir}/${_doc_prefix}-decl.txt") set(_output_overrides "${_output_dir}/${_doc_prefix}-overrides.txt") set(_output_sections "${_output_dir}/${_doc_prefix}-sections.txt") set(_output_types "${_output_dir}/${_doc_prefix}.types") set(_output_signals "${_output_dir}/${_doc_prefix}.signals") set(_output_unused "${_output_dir}/${_doc_prefix}-unused.txt") set(_output_undeclared "${_output_dir}/${_doc_prefix}-undeclared.txt") set(_output_undocumented "${_output_dir}/${_doc_prefix}-undocumented.txt") set(_output_tmpl_dir "${_output_dir}/tmpl") set(_output_tmpl_stamp "${_output_dir}/tmpl.stamp") set(_output_xml_dir "${_output_dir}/xml") set(_output_sgml_stamp "${_output_dir}/sgml.stamp") set(_output_html_stamp "${_output_dir}/html.stamp") # add a command to create output directory add_custom_command( OUTPUT "${_output_dir_stamp}" "${_output_dir}" COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}" COMMAND ${CMAKE_COMMAND} -E touch ${_output_dir_stamp} VERBATIM) set(_ignore_headers_opt "") if(ARG_IGNOREHEADERS) set(_ignore_headers_opt "--ignore-headers=") foreach(_header ${ARG_IGNOREHEADERS}) set(_ignore_headers_opt "${_ignore_headers_opt}${_header} ") endforeach(_header ${ARG_IGNOREHEADERS}) endif(ARG_IGNOREHEADERS) # add a command to scan the input add_custom_command( OUTPUT "${_output_decl_list}" "${_output_decl}" "${_output_decl}.bak" "${_output_overrides}" "${_output_sections}" "${_output_types}" "${_output_types}.bak" DEPENDS "${_output_dir}" ${ARG_DEPENDS} COMMAND ${GTKDOC_SCAN_EXE} "--module=${_doc_prefix}" "${_ignore_headers_opt}" "--rebuild-sections" "--rebuild-types" "--source-dir=${_doc_sourcedir}" ${ARG_EXTRAHEADERS} WORKING_DIRECTORY "${_output_dir}" VERBATIM) # add a command to scan the input via gtkdoc-scangobj # This is such a disgusting hack! add_custom_command( OUTPUT "${_output_signals}" DEPENDS "${_output_types}" "${ARG_DEPENDS}" COMMAND ${CMAKE_COMMAND} -D "GTKDOC_SCANGOBJ_EXE:STRING=${GTKDOC_SCANGOBJ_EXE}" -D "doc_prefix:STRING=${_doc_prefix}" -D "output_types:STRING=${_output_types}" -D "output_dir:STRING=${_output_dir}" -D "type_init_func:STRING=${ARG_TYPEINITFUNC}" -D "EXTRA_CFLAGS:STRING=${ARG_CFLAGS}" -D "EXTRA_LDFLAGS:STRING=${ARG_LDFLAGS}" -D "EXTRA_LDPATH:STRING=${ARG_LDPATH}" -P ${GTKDOC_SCANGOBJ_WRAPPER} WORKING_DIRECTORY "${_output_dir}" VERBATIM) # add a command to make the templates add_custom_command( OUTPUT "${_output_unused}" "${_output_undeclared}" "${_output_undocumented}" "${_output_tmpl_dir}" "${_output_tmpl_stamp}" DEPENDS "${_output_types}" "${_output_signals}" "${_output_sections}" "${_output_overrides}" ${ARG_DEPENDS} COMMAND ${CMAKE_COMMAND} -E remove_directory ${_output_tmpl_dir} COMMAND ${GTKDOC_MKTMPL_EXE} "--module=${_doc_prefix}" WORKING_DIRECTORY "${_output_dir}" VERBATIM) set(_copy_xml_if_needed "") if(ARG_XML) get_filename_component(ARG_XML ${ARG_XML} ABSOLUTE) set(_copy_xml_if_needed COMMAND ${CMAKE_COMMAND} -E copy "${ARG_XML}" "${_default_xml_file}") endif(ARG_XML) set(_remove_xml_if_needed "") if(ARG_XML) set(_remove_xml_if_needed COMMAND ${CMAKE_COMMAND} -E remove ${_default_xml_file}) endif(ARG_XML) # add a command to make the database add_custom_command( OUTPUT "${_output_sgml_stamp}" "${_default_xml_file}" DEPENDS "${_output_tmpl_stamp}" "${_output_unused}" "${_output_undeclared}" "${_output_undocumented}" ${ARG_DEPENDS} ${_remove_xml_if_needed} COMMAND ${CMAKE_COMMAND} -E remove_directory ${_output_xml_dir} COMMAND ${GTKDOC_MKDB_EXE} "--module=${_doc_prefix}" "--source-dir=${_doc_sourcedir}" "--source-suffixes=${_doc_source_suffixes}" "--output-format=xml" "--main-sgml-file=${_default_xml_file}" ${_copy_xml_if_needed} WORKING_DIRECTORY "${_output_dir}" VERBATIM) # add a command to create html directory add_custom_command( OUTPUT "${_output_html_dir_stamp}" "${_output_html_dir}" COMMAND ${CMAKE_COMMAND} -E make_directory ${_output_html_dir} COMMAND ${CMAKE_COMMAND} -E touch ${_output_html_dir_stamp} VERBATIM) # add a command to output HTML add_custom_command( OUTPUT "${_output_html_stamp}" DEPENDS "${_output_html_dir_stamp}" "${_output_sgml_stamp}" "${_output_tmpl_stamp}" "${ARG_XML}" ${ARG_DEPENDS} ${_copy_xml_if_needed} COMMAND ${GTKDOC_MKHTML_EXE} "${_doc_prefix}" "${_default_xml_file}" WORKING_DIRECTORY "${_output_html_dir}" VERBATIM) # fix output refs add_custom_target("${_doc_prefix}-gtxdoc-fixxref" DEPENDS "${_output_html_stamp}" ${ARG_DEPENDS} COMMAND ${GTKDOC_FIXXREF_EXE} "--module=${_doc_prefix}" "--module-dir=." ${ARG_FIXXREFOPTS} #${_remove_xml_if_needed} WORKING_DIRECTORY "${_output_dir}" VERBATIM) add_custom_target(doc-${_doc_prefix} ${_do_all} DEPENDS "${_doc_prefix}-gtxdoc-fixxref" ${ARG_DEPENDS}) add_test(doc-${_doc_prefix}-check ${GTKDOC_CHECK_EXE}) set_tests_properties(doc-${_doc_prefix}-check PROPERTIES ENVIRONMENT "DOC_MODULE=${_doc_prefix};DOC_MAIN_SGML_FILE=${_doc_prefix}-docs.xml;SRCDIR=${_doc_sourcedir};BUILDDIR=${_output_dir}" ) endif(_opts_valid) endfunction(gtk_doc_add_module) swami-2.2.0/cmake/cmake_uninstall.cmake.in000066400000000000000000000015551361104770400205040ustar00rootroot00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"${file}\"") IF(EXISTS "${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF("${rm_retval}" STREQUAL 0) ELSE("${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"") ENDIF("${rm_retval}" STREQUAL 0) ELSE(EXISTS "${file}") MESSAGE(STATUS "File \"${file}\" does not exist.") ENDIF(EXISTS "${file}") ENDFOREACH(file) swami-2.2.0/config.h.cmake000066400000000000000000000045571361104770400153460ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H @HAVE_ERRNO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H @HAVE_FCNTL_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MATH_H @HAVE_MATH_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H @HAVE_STDARG_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDIO_H @HAVE_STDIO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H @HAVE_STRING_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H @HAVE_WINDOWS_H@ /* Define if using the MinGW32 environment */ #cmakedefine MINGW32 @MINGW32@ /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #cmakedefine NO_MINUS_C_MINUS_O @NO_MINUS_C_MINUS_O@ /* Name of package */ #cmakedefine PACKAGE "@PACKAGE@" /* Define to the address where bug reports for this package should be sent. */ #cmakedefine PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@ /* Define to the full name of this package. */ #cmakedefine PACKAGE_NAME @PACKAGE_NAME@ /* Define to the full name and version of this package. */ #cmakedefine PACKAGE_STRING @PACKAGE_STRING@ /* Define to the one symbol short name of this package. */ #cmakedefine PACKAGE_TARNAME @PACKAGE_TARNAME@ /* Define to the version of this package. */ #cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ /* Version number of package */ #cmakedefine VERSION @SWAMI_VERSION@ /* Source build? (resources loaded from source/build dirs) */ #cmakedefine SOURCE_BUILD @SOURCE_BUILD@ /* Source code directory */ #cmakedefine SOURCE_DIR "@SOURCE_DIR@" /* Plugins install directory */ #cmakedefine PLUGINS_DIR "@PLUGINS_DIR@" /* Images install directory */ #cmakedefine IMAGES_DIR "@IMAGES_DIR@" /* UI XML file install directory */ #cmakedefine UIXML_DIR "@UIXML_DIR@" #endif /* CONFIG_H */ swami-2.2.0/docs/000077500000000000000000000000001361104770400135665ustar00rootroot00000000000000swami-2.2.0/docs/CMakeLists.txt000066400000000000000000000002541361104770400163270ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # add_subdirectory ( reference ) swami-2.2.0/docs/manual/000077500000000000000000000000001361104770400150435ustar00rootroot00000000000000swami-2.2.0/docs/manual/Swami-Manual.sgml000066400000000000000000000015111361104770400202200ustar00rootroot00000000000000
Swami Manual Josh Green
jgreen@users.sourceforge.net
0.1 2001-02-01 jmg Initial document created This is the Swami manual. It contains information on using Swami and some brief info on patch files and MIDI as well as links to other information resources.
swami-2.2.0/docs/reference/000077500000000000000000000000001361104770400155245ustar00rootroot00000000000000swami-2.2.0/docs/reference/CMakeLists.txt000066400000000000000000000003111361104770400202570ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # add_subdirectory ( libswami ) add_subdirectory ( swamigui ) swami-2.2.0/docs/reference/libswami/000077500000000000000000000000001361104770400173335ustar00rootroot00000000000000swami-2.2.0/docs/reference/libswami/CMakeLists.txt000066400000000000000000000012701361104770400220730ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # set (ignore_headers builtin_enums.h i18n.h marshals.h swami_priv.h SwamiState_types.h ) if (GTKDOC_FOUND) include (UseGtkDoc) gtk_doc_add_module (libswami ${CMAKE_SOURCE_DIR}/src/libswami XML libswami-docs.xml SUFFIXES c h IGNOREHEADERS ${ignore_headers} EXTRAHEADERS ${CMAKE_BINARY_DIR}/src/libswami/version.h TYPEINITFUNC swami_init\(\) CFLAGS -I${CMAKE_SOURCE_DIR}/src LDFLAGS -L${CMAKE_BINARY_DIR}/src/libswami -lswami LDPATH ${CMAKE_BINARY_DIR}/src/libswami DEPENDS libswami ) endif () swami-2.2.0/docs/reference/libswami/libswami-docs.xml000066400000000000000000000044211361104770400226130ustar00rootroot00000000000000 ]> libswami Reference Manual For libswami 2.1.0. The latest version of this documentation can be found on-line at http://www.swamiproject.org/api/libswami/. General Type independent control/event network Misc Object Hierarchy API Index swami-2.2.0/docs/reference/swamigui/000077500000000000000000000000001361104770400173515ustar00rootroot00000000000000swami-2.2.0/docs/reference/swamigui/CMakeLists.txt000066400000000000000000000012201361104770400221040ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # set (ignore_headers builtin_enums.h i18n.h swamigui.h swamigui_priv.h SwamiguiPythonView.h ) if (GTKDOC_FOUND) include (UseGtkDoc) gtk_doc_add_module (swamigui ${CMAKE_SOURCE_DIR}/src/swamigui XML swamigui-docs.xml SUFFIXES c h IGNOREHEADERS ${ignore_headers} TYPEINITFUNC swamigui_init\(0,NULL\) CFLAGS -I${CMAKE_SOURCE_DIR}/src LDFLAGS -L${CMAKE_BINARY_DIR}/src/swamigui -lswamigui LDPATH ${CMAKE_BINARY_DIR}/src/swamigui DEPENDS libswamigui ) endif () swami-2.2.0/docs/reference/swamigui/swamigui-docs.xml000066400000000000000000000062501361104770400226510ustar00rootroot00000000000000 ]> swamigui Reference Manual For swamigui 2.1.0 The latest version of this documentation can be found on-line at http://www.swamiproject.org/api/swamigui/. General Controls Canvas items and related GUI Widgets Misc Object Hierarchy API Index swami-2.2.0/package/000077500000000000000000000000001361104770400142315ustar00rootroot00000000000000swami-2.2.0/package/osxbundle/000077500000000000000000000000001361104770400162345ustar00rootroot00000000000000swami-2.2.0/package/osxbundle/Info.plist.in000066400000000000000000000020241361104770400206070ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable swami CFBundleIconFile swami.icns CFBundleIdentifier net.sourceforge.swami CFBundleInfoDictionaryVersion 6.0 CFBundleName Swami CFBundlePackageType APPL CFBundleShortVersionString @VERSION@ CFBundleSignature ???? CFBundleVersion @VERSION@ NSHumanReadableCopyright Copyright 1999-2014 Element Green et al., GNU General Public License. LSMinimumSystemVersion 10.6 swami-2.2.0/package/osxbundle/gtkrc000066400000000000000000000000661361104770400172730ustar00rootroot00000000000000#gtk-theme-name = "Quartz" gtk-key-theme-name = "Mac" swami-2.2.0/package/osxbundle/swami.bundle000066400000000000000000000126611361104770400205550ustar00rootroot00000000000000 ${env:JHBUILD_PREFIX} ${env:HOME}/swami/inst ${env:HOME}/swami gtk+-2.0 ${project}/Info.plist ${prefix:app}/bin/swami ${prefix:app}/lib/swami/*.so ${prefix}/lib/${gtkdir}/modules/*.so ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/engines/*.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-png.so ${prefix}/lib/pango/${pkg:pango:pango_module_version}/modules/pango-basic-coretext.so ${prefix:app}/share/swami ${prefix}/share/themes ${project}/swami.icns ${project}/gtkrc swami-2.2.0/package/osxbundle/swami.icns000066400000000000000000003364701361104770400202470ustar00rootroot00000000000000icns8TOC 8is32s8mkic088ic07pil32 Hl8mkis32ૐ{xUU/.EXlUl$200.@YoATE$!56 ;L5REZS&!;7&&6H`U]DQ_>8H-1+,5LZ[QAA/+.+80)-1RbtV/#HgfxŃnP>75WwvVO6.BiɟoI9)|?lWܻйٻ`O9#|8mǩĮV*%N-Ѷ˶ы44pmaި`Ae}~ׇ6/䭬 @*ZN..MMUH'!s8mk9Δ999Δ999Δ9ic088PNG  IHDR\rf$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  @IDATxieqy@7 i7P!ʢ$RH$klb;<1c9?)c- iLH"H ^h޽}5 $@vwnUeeeeUef-9S^{-|k殜~ƵZZOVFUV`+w9 OS~ { }wٲe>?pk/q<\r?ٙ3g6lV={vA֠_"n։'YY__q,ꀏ旁7,Ox0y;M)Ҏr~ѩcd8 ?ǁ(4&á/WQ \2?Θ?=}M7MZjꡇYb5kLoBanP-®$(npMCToxxXax1lfx­t-ʀgM$ͺY4 ~L'yxa V~!҆*M_TLɴ>Qߑa`7hf5 + ܰ[ouJ&M2[Xït4$Q^X]6 ‡a 6Q~+ኗ_yiAaڥc%óǥ/=1f1nB^r/S \2cC^{ׯa_}{;m+WMӧOT0ٰRRӊ-wO7[CR Y 8U0^ar^Vqax1kSN=xoҖ%#Go޼%P|vߋbmR("QЂ` ðrX-r $BL\͝xcП ٙPgf3<2NS< ㆭ҇!n ҄몍 Ʃ8;usq7[rwuKkK3l7k׮u!F^7)H( Fx X8.2(gNΠ,sg8J~~>v | o)(َ4埉pAiM|,떑ߙ՘|e;T|տOK >-MDp\|>x.e0o5ng]3FxFBmO~˷m۶G$&s~0\ k00 Sv:>x;rhw#''NP4 *N.JŝkG#Mǩa3ܥXzUfnݺuk _agxnK*gom;x \4&^r [`a{So|qmA02M+_KKŅLըAU{;x@wӮ;GLO<SO?,+gbQFse)`"B +ٓg^#f33sIa ˻/Vhleu71=ch4<$\֪- 8oq~7(/?*%w4Xm87aÆ8}\Doco#Sh'רU1'Q`ZZ^?3O=@YnY[ ZNDYGY*|9Pw~i0[Z|4s'K7M/]۰q36umm|s435w g! f+\Kǎ;CˤxqNZ>O}j<\ @__mA> P!UfMl)e/ 0nf{w==Ϩ_ fT2&>H`Ub^9e'|!^<ņemZX[2M7toapc/~p@cOlzZ`"Qpw"xEpnFB[5VG(GhW)`u bG$@%>}3τ::g` dlܭ\Q1ܮç۸~>>Cw>J<̱0|>&aC,< mmKbIqضwFٞ:pfv,tGH/˫^wԇm< *k+) RBW0qZ=ԞoO>TÏv;ySۼ~C-wJ<na5$±|.ퟏFaRU|~S$4IZjh+,N\2lmWt\sUyӦC>3Hw~o?y\ah!U7㏘6۷o_n P`WL#ioGnGnVG8 65$,m8OO?tgݎlklԭX|PE+8fD׏)ThiIRˈk.lOgP 9͆Y~}e4kX"҆.VZ`gư hlY 'CX3pG(Ey|_M{j4!E(\vP`+|ʼncj7v=X{X{^2Tͱ0Rztχ#WaII.U>wHby=#MXw?O'qߎNp ?~xD~w=nm[.ʞz0ԆR֥`vvzWw}VYhO!:eP"m\ 4@ W /Siߋ5Ӈza 㥾›3qr`s.~+(J6Y7_:z2gV}%~ɀ_/03;;߈?d??Tx踿GG~b{O:Z_Wqvq7{]=w'Xou;d0[S )_}1ҧIizIg6 /<^!Wʩ }Z땾S U9 gU)c@#TYθ h>p}[ƻo馃?L^у_7r;QߤCJAC \V_!xC;so}n'cGw'{sW %aW [TKV8)vKTK,H? aKƹ_+0m)Ű9WxQy @aY ?iU0LM-[.gypu,QJ&bT` Wx0K`?MY&1Y|P^c5=F_>BBg]UqN}uc}wǓyzS}G8NS :cXozB #R1 )=ªU5WW42'Kgt!^7OjFpT|A圆!PIQa&]O_O& @;:t#3)_{.}t/cWuW_#<$EGU©ڥ`|a|!`%0gAe**e ҪL!/E!/"Q+~ŪR)Z=4a=^Xd2wO4ٲ6oo22d }a pGƕ{SO=]{ckE܀o09Dx5x׾` bM1'̑Tfg>:5Pµ +WH*iaZQVt{`R _" Lz}gjް̢iJI-ճ•V XRByNtcoꮼrk, x&$KnVW!w!xM}5e|X鿍50kNi21}uww=C?~sucBcSiWc꟰T4J1CZL %y _QtSS1 7Iaؤ^j:/rbU΅zJIZTMኄ^xCф Ud7їlhw :\n$0^ Q?K޽^K3׌W >3v?}FA:=/|/O}3ݕ[6n AzB#%_&&O7V9QEk-+&^鑭 ]rbro 刯puwÍ7p0_PR^25+B~ه_}Pvj6rISt߷tvwnx^w4VM,Wt< z5ӊ 'x. _0 ㅗ)o7';-хڼ=5v66}o'+9@T`B&?oGGrYWD^%D}Wba?LORu*C]=tv/uw7>!NK04D3u)!Wا-L>٬~Ԅ/W.4x3 !3Q%h4⃅.]3(n%?#}?uj};?K]:CZaCp5wg?fCZUI%%%4|9ԟ_OCkf2 7kAҍ[^L@"GZ*$&&Y&r- 0?]00l Գ^xsfTMo.͖FOE9ӡ ,A+p?=^^ϥ7[+F+अÿDV?g*߫e&Zx_GPauoaZ6x5 kݻ7<㺾WM@_AiTRym5J{짧 }\>_%# #^hLUdg@B @e8$f.4\7eTh/bMAT>Vo_J'_ex) C׾n;*}wx}"_PzpF]j(uZ 2{>o_UˁWI1Ƽqxok3F!+l{Nu/wvm]uSgc7%hF(sRѯSS5(A9,7̥E#}@L~[.dkE'FekPI^ jY2}H抏OVë,Ö.Ab ? wWs䧯Gqj(h Zړ[;jOk0Ǔqbo:Lj+Wh,V31\3Zi &h:Oc.>_/;n~)vJ(Ĩ6x۞gk _|jpPtF eo5!mK4H6w-e񅈕xDR^\X6̱"90Zʙ|О.4z\0ܨC] TQ1fiqgxbD_qj'?gY yǣsnB7\f8 `0$fxuC~;矱!˪dh|N}o}=uW_wyXqqKՓpa8͌onztu9JK>fl(Ե(o #L,\r +jb;:N<@4%UHax"eɟeX4%/ pBI ;cZiX,LQFȁ39~|AeG1.k+ٍ_h7sL]%3axx݆qnظ>ʙU_y*`&G'?Y @]wu#ߡ~ @yW$~s=ﺫvW\;Չ`4o+<ʼnO-C(fo\M߄G/u##1X@+΋3K#P1Z8nک8zY&p[$xOv= $07] ϺpgaaR |څ^ţ9ݟ}q.q ٳ?M; @k%)}Oa߇!X PȔ2sGʸMIm5:q˚}G??W`U'!~ǪêVY˼gƣ]w}{'NAcWrtp&rG{QOt+Fk7hg+?.4{3!n F2Va)~g*52Xfd' aN^R!&/ )*K5`%k8ŋF ].z?O_w!o햍>@3;/:Dwτ?SJ_Y36PY?æ+&9d&x5"+?L]l-k~fqy|owO]}ؠx7yc]ʨDN]+[Ap NMo]?K-X;H|npyWM laA@ Z_$$`)EJ$^p K)υ%#P5I< !!9\ UF%5E+wX;5~w73(ϢoO.m, Vw'b0#,X*D{61n\s)B$sp~R?g ,nwYڎ>яW a!~N&Oр>? |~XXBņߗ?_{MWPFIMZy1/vc\?a2rj;55.^䝙Ȱ#{I4kbxN]Ip7!4&ͷ>',S/'Gนm12 Z98/5i唑> 'N~ãL]\Lt'4:H;ʟF Q3?#t.lU=Nf.ɳ89ŎFI{E+K/j18o2~`3;k8~h6H^?ݽumЄOcys떘);TMƚ%٩Bj {HE4 ꖊnF8dS:e08m]ˊl@T,@!}Dl\H.E hIqhl@\73y& ȑjρ1$,rŨLΎS\fOP(~0AwӧٍW}:NM17xmܸ Cw<_ϔWߧq-M°wU덎_SS bM=i:l9DCy!Ye,KO tCBPk +Bk3phk,3( NCQa RV4_ e6BuC[?TͥDixjI6I}P+Xս82n I=MA(ͽ8/[͵峰Ơuv݃%ddo"V᫭+Iƙ?L`{&}7'x[4O^-7I%d }5H $7 94erMKEOl@" 0O(?F& 4CFf<D.[RW g2f;5);y GH840؉JҭdT\Dh>b&Vse. =_n ̟{f"#yr^],?]{WcTf <[b(Z*Tqշjgj)8!vDvHXgeAcig!έfDPN%8*85v&p*Ȓ_Ҽg9MJ䊂"ȏMB(`b4]@ K0@xF< :{a`EA5 9OW9s Sc> /9kH 0p %;/^i ~UdVWe[%㒛994gJC:0DSA>kȩsJhɾf<{ cn#{o㊓QģR^6<lw5LϿD[*ybcB_![>YoG\C9Q"-Gɦ@!x?g)ZM_̒)(Iv=?zOܸ$qw|BZ8j\ [`sQ$˞i^M)g, #lX.'¬_]eYι1u%@RHro@*^U?{,'H} ~ú>2\ p&^Yg;S'N FEPP>{>nnʿuhjj.E+VȆ3qTe$7Ypa(CPp'>GUA; ʯJc`)mHt%Ҧ)& 8"b 3 3aAZ 84ʹ՚).T|x)8mtJBɋFa a|6T- `̟#0X ?7:1X4x~X8vM7uf D"cW3 }/5=}'s!poa/|ef_3X,3K :q 0q^RhߟUO &*^3b5>,|wzcأ|;"|E '>M<*s~ǩ=;Qu9[=ާ/Ad 6?⿅7iUo${r?gС9+aA?D[>L!݌qi"Z<|ö.9_ (1}!z`Ao[&OkpRE[GmkʑVψ˳N 9?'N(&g nוs+>8ۆI;p/.7P# _B6}w2}Z]uÖh\ֵ#Nٍ1% ̥@ܺcz1XG4bP$^1Bو$` &]" "4ʟAyNBQX@<`ۻ3Spo>6? Pp<WwnndYZ؍zlw%LeT1@3F4 "d E0DE%I0E )@cKu]s`Pm$FAa6S30QGқHH*as80>B3JԤ7[7mE_e#ty ^Ȩz(# R扑_]{*# |p_3=`92me: lN~&~~I՗uCBSϾ `nqFяu3sGou3n#bntVE԰0Ӽ4]wsYqy10RtIY |_L>,vZÄEIPxiVN9M@镹а +..~Oh_S$Or&a>ԩ3>Yg/u{&}O>;];vGp *F$_Ea(~5K%tMtiT:;}&K 83)ֿiax)=SKz\%ft!^ [b wS[m<7bh=jɃTg>ny6ߵGp_FcFOomOZX^T֨ R @!SBƢAy-tHzpB`7 {98o1fMN0#eeᬔН8~g7qP't\(6 CO.y4a(~뚚wd_Y8Y{}'wqɵ|Flj r Ei0ªBb,6 @r>>@ 6\>|I$Sx>ɘ? K C$gjebRZAm8Ap)?Q,qy\qwԏa`迃؀Tm\ g'Sґu5C`82o py+PRCB U&F)@ ך l]t۵xIBlp+HAa?{B]q!yh_pbW&~#p5@q*]⚑WPfAǏYJ IhS'E/"_C^ŷpڢ5֟?tPrO)Ug{˵!x;%t3uZ9>= @4 v观N/?5M8gC+c3ugsc-zdim^mWDivLk.D-ϤPiO$7 da"7@ QI#rQ^i [0s 4qѢi8bu񁛮8A%DVI}z,HT>@.?/%, <^GSii ߶NɇK}B"?Fa_0:5u 0}ð:@ @ߋw/δoa;}QzJ>dذC>۹cWwq427l6mR# YS{6QqW1j %@?U>I y`Y;2 o\Vk6ia{e:>8+҉wSaqEZQp^{q1p|1w!8 hx{ej~hQ=kW.UϬ%J\`a Q\,$MK+Bȳzӻ۾o/xJ΢lp2v_ # 1iH B*X/b 5tKc0@b @ ƎM3,> X`Abn=s#ռ І6> F„ɔmPH,dwZ2.nQ1 ઌV=&|ղ>Nx: e(8hb HohAg[ӏCYF\|e͊Ge~JZNhD!! FuuIXҰŋXsGx*} Fo%_@IDATbVxn~+lo*GfQY`a9a. L|ȁ,/Bm(.'NИpDV+nzo訃k(MKA3x'.Fy :M}\W1eꜛ jBfa&YD(: -E8C0įzr@Bي? %Ymbcԙn_*ŏ/ydz̄ nR!8vw[9$t)Fңs5ѵ_+V _{{ܷwLluz24ő>˺߳{>]4_*Ў*{Ff+.>6cف}pД_v&lU&>|464oSՑư%8!P$V^F N]?g4)"% T>@`x[IAUA精C3-[FN ک)\5N_M!b8 \A0 S.tދ8f|EiaM}陞 MsN~*݀GugEe@#[`VRK$L`RƼ1:߽ݾqEoӤGF# OFsOjTxYӔ`tS?~}+|3'ɧ݀ŷo^%9WOg$l?֤! I.<`nX:K98`AVkQq*x>(v d_ $< -7{fδ3-ØɈz Z)ڒɥށcJ mGQk |0k%Ře /R1(Q_RuIa glxkŶ%Atg9!ҧEp9tmźlߋ4% g{w]5q߮?5?9FѿMc#*m G}Ϧ)IgKa61rd7zҸ[~uH0O&˟"m3'eA!&(I%~B|,>8,sN QRS't''f]U[XCvLO/]` ~-yňþcݓleh  nt J?JZi/nnglnQ׬!0M0rnn,%_ݸ:=6t2ěS+]+Fd B#aN Ab'% kYn09s$/y0.wd_ ey# ? OBN%YŹ/EQV)GUm߄Va$_pt$ DHYұ `: ux?c@_1bȨo~h﮹2-]EԲQ֩ni€@Yh4"f B(t:qqwfc-;saQ.bxͶSиPA"O H04Ff4WF:X,Mgk̃77;fAr%Yԥ3%wSwC!kMyZ}ኗo@g7T _htq:szZ`Y/cDrP:1yrY򉟭4[ȹ8|>8 IWf)u<2"$_\eɝT/7xAHz3۹9./_D-qz}ݽw+Wyj_ 5qJ-z7bύTdqZ^B 4vl oO#IitU/EFM KAd30r0#]_Bu vt5E& 5]# j)ڌJh;Lcll~!Wk0l r'S臸>h9"a/cW{?7ط?*mlT!=Tlӧʋ;uZ) ('\+f]7{s̭x;ۛ -㑯)p:TiVa΂0BM5|-(lLCPn(4 ޗT<5 &ip*VM ag T~1+)>~1@ŲQA6I'79s1kpi9f"Fٍ u18M[$e@|w񐩅3̓izBY@h+}+_x c!N:]`m۶BfK2(}}]{mR]@;)<~c<[Q S֬}F"1åXv*F! ʃq(M) ӌ7B#K9|-7^TGG1ns8dSQwۚya|m/̽^텆"3tt5AIS Q <,@nE:^iؙ0W/hrڟo!du<=1藲8C3Y@Cn'7#R_֭,/w$ 'l#GՓr* tڭ X;Z{x  _y~m&|Co˧vw>eCQm?}+#k~unkT|I$G)(ɱklTƇ ;>Ƌ6<8=} u!٭z.^kddhB__6yOvϣvJ8l,(j4ͬ`vŸ5u;/qNT. 偙YD.֡ϰQFɪG3jVU~̫<3$3X}buh}-N!EYt ad>'Ng΅Z~fxhVu+C7!-ۘCԽQ8ŷrG~:JS*, `Gc7uwĈ7Vm%gHgWVhl>Sݤ ox{;t+޳ /`\ DǑǛ5By`?C|Y_ 7 8t?o<{zh,i}MQoPLPWY^ʎQFu NX@=4>8{9ǦzН{cw4&c!gwz^ջpuTLm#la?I} P/VerC# zYOrH[x/P(!<]BQao߱{Qq X͐12箽;;<*?]ivc42dZ˼˜9?cɭ)r*Soi y\?q QZ2] ]F@ML8ۈ)>O@ s1:|77nxlzŃ0rtgՎρ-3oRkPLqyp^#M2p^a*@E?%4bV$_S1mQ2uR mwp9;zD]Gm^a*ۚ~b/W1MJH\O#>:TGTt5йu _k( )jN<ߎGtW^ˋ#aTJlV#Ґ2~,^|1oV81_1"U a0B}4.)0(Yv)_c #y~+Q[)t@:Eÿ _L ꙭ7|YFMq,ȈN)En919/sg|8wUXÙg,,I3{]w}*<ǡPg&}~jf]lu6-6Ԩb~?ׁix7bm 2㶸)qE!Vɨ)faL+\y1.ZŋvrAR,l-ɜzJ yρw]ۺu|34 >j˖-鹿@3 *aW|Ɵ#gNvWRBƣIKU g-'5!* j">UΰlS[8VQptFb@ =ɿ5TV!#M?b&~*y4yE׏N7*{#`C!%-47J0$ FF9ގ0;kI?2ET"T4q3*fR_EN<. qHŇF4F5R)&: 3Y?dz0Lf+eU$5)y:ykC>5ڇS8%WQV=Gj PPn%,k~3vKݵwp/w~ ! sI_,K/겁z_7GLz%݅1:d7܇ZI~.oc)|R~aѕi%x 8, CRZâ)X! ܹ;NV𸮕RQʫ#4q6O8*$ )l!DG'RihThypv|Lו{2k|tmG>RXaf}Dɱ#|F/ث!5q, yROކ<Bc0D@Q3aT|G0y oAzRMHӨYfuF[]_I8A1)ZȄ0n~|vL@b3,;kYɿQw7Y4h!0=N?KδX3Y̕$oUtF6iXʷ%ՠBzi_%wwW|oj߰?孿?I3l yK`NXzmzS]4# k"\1ANȴVHXa tMJ4m1+ԧ5bNfX7I:ftg%5z"D }= t޳Ƚ 8u*z\հsڏ_#uy ?i:Nq޻tj5|IԻ^`BF9u}T _S?[@B) h&B7ܘ5a7'%,ӼuIbXLc9 V K2 q MiYdͳ|ix0ɧ}|ޙK;@^Y@/W}Qꘆ-@e0kTr.5^ U<[կȥ@Qr }ͧ  ]~w! M 5KaqEg6%e.GGz7xh @-nLL|<*;4_6?td*:iM;*GkQy0?kf\+DK]C O9aP TF.c0.|d0kY76PY'w} 4}ws7]:`exhF@e0Rd-FQm?axTqѷN ZI)XѨB&WbݯÛMSl"frkDsۦiU^hp<] &3} z I.,Fu,o4#qX,T~.Fs7Z$ѾwJ|Îs{*Y76N䧽MT8rN;AKz'Mey%/vD9UwuˏiF`4}[f~Ǘ^< :Ϻgď%>0g ~a)1r;P,YÙT\{H'/sr5vr.2a9Orcw_0F$[~^9Z[0[6?* ̬Ky"@Wf Ew K`TjG@bN[!c18k.ivq 6ŅĘ_gFHnϩy+=K'on# u(KhG5Zc]q_cr*kl}WbkIE+%%ftlL(R4Í6Xn8dt%:>ݞ;L.ӲHdHԜ*;n'Ⱥe2Ygb ?7! "&'ȒĔʕ<"Ng$@4@. X0Z65f}8Mt&hz UʍF=μ;_݌<'0K=)?gIv,I:'Ex$ @9vC-F6Wf#Cy/7 S@GՙH1<Q?~!/y%SS_ʡuhi> x웓޵qsz`*B mNi 6Iz@BL2jKQAӯ6Ro! 1uMm78qvO' sù;-("빀ޭ\g8'%EC?yOAk FAY~deC1礟'9Q&)*$u"ˆFzYH7u >bc2Eyvtc5U~? ~ltqwg_9NΨuBj- 6k3L22%q Vazbee;UO4aV١ý|E.ri9۽TPo'dF+WĜRxW_~駻Z(0U)S^Gٶ2%/,,.~5פlP~ZF "Qc><0N#1`:,Ro{w1.s?CYD(}<eˤN.Oe)P6h8o:(aB ZXX+ b=])4ei zFp_L_w yJZD!{?BBUz+APmlیmܧϰ)YeJ\I?b{Wa`,FoQAu=|a,k~ ade4z;;,ƅ\3rj-).JR(T2{Ұl7SJϠTg1UFww <#M?Iĵy<)?T9jt Ĭy6_/G*>>>L[j֚yw7^Yr#/ddS3P&TQΚL*Iٲ.fLū!\$Ņ4%}n']gպq+umC=t 6Yşi>qH^ec߰{ʟxboHg*"?Q{4Z8כ$)[A<t Ά7/B[8}APBk900Q5||q;#FӬ(|^Mn([ khvL ' R :Xx('> ڍGh @74W,K;wb1fDceUQ,ٖe?3B-Y6. \MeWfW1oy^7"#2x{=Z{{fcs3D8}JtZ8IMo˃!$?#r yJ=C7jCNL=2sFoulPYIPwC&L s2r3]uτD >*s\%-@aɌwN0LPnq'%[at_iTc1wbi$ x뭷fyÛ^sο}bEvnfAd9li2 n="pdapObDJC1i(3$U: Ҟ%q$?tzvsL!G^xnh3 ^zCFOi o~|S8?2ځKΨn5vst_wF1|&VX'~,9'e"q8m ^(P`~$ƍ&~?MS1/}_cs<[I iad7ѫ1"Ats4Sl^SXԭ ߨE=M9l*t2S8S~29-Ug(03Iw,0bۯ}G=)X1}dϊ4 ѬtQݻĽɬfJi.&b̯ӭ&$(šLF\͞YE 9&P~C"=.*n쇴GG7u? mş[5"#nOo@v?-NJQvݢ-?u>oM8t^vPE9EKK'?- pΟ&`~8:eZʯS&ff?#_@9QFIIxtS7]J- `,ح!-1e$aEq,ؽ#_ϳ s)ᥐgf^/#{Y͉I?' }B* _i>ԙa)>{lp:M^Oӎ4t!лw4dy/p욫2] imTU8y0?Ԅ$~M@ט0o>.ߑo97)`Śl&-*dZezkV-KՔ3E=3qLwK͘oūad=7$U/X"q_e~ p,.lwŋ%OCкC1P /D0HđXLz0FOZ3fk ;!c,2#_q AdKusR0;%#&| B p"{22`VDz4!2O0=CfKo σ>`RܜC7YNs-[=eaܱj;.F>"0L-m0;2!<= Q`2ȖYVq \  Geе`U#@Pق/'}lcWÀ-쉴?0'ǎĉFNٕ'wn0@0Iay_&}SƗ!Ϗ:YW,#!n/V?A* z:7LQ28nMCUoa M\q0i>w?_]g(Ôfbe&U‹ɿ (fw>6M5f !{:_xeD*!fbo%' LgRGYqsk}zA=u?! z8kOW%p@2w|?tTD&1ȗ`NICn0]X8mL ]Vr֝MZW]{L=#Ь p߾Gei?_OՓcA#s?|L`?V7\?Eޫ%eЭ@Va8 }c:q<"jlSjK"=sƅB߭/BQJs5w\99DyG2ܖbn)D P| _!Q#CjQ6V6]ӗJ=QqvX<aVߎ; | SI8ɭqz=n9r+1qyQm.oܕ'Pkjn.9g??+"UqLƓ̆~{.xpT#;<*ݓ/{o$_Ŗ4g-?z?#;g_ϖlgh{2gLSxÝgS[wfpd^ҡ]Q40V,)4tJ|_c|G JS]XRB SJ|OVR2S& 3*KH '~Qj.{c؄J2n!B˃i;<߀b8q o <,j&¼g>i:W?PVYc&dZ~lAcEf*.I?KFkhEAnx I01 Z<AuD rU\JdEj_p?w,&L8c,r*xKLA@YhEe6l_U+E3Ỷ/Ba%oNʪp0;7',c!~?-9?ݍ6, UfQܭoώL&l 7垺+nz7p!U>OBO{ŕ-3i>OWzn(5PJmL{)(ZҞGsMa$5ltLMU<1D`e[fzgj^ejN*b7lOЂL2=2LT2:TI#!٭&{u=q&8eHLB-VzKNahaM + f'gG3ojnBVbp-cD^QSO2Bd*6i; Nr~Z=94DZ)ld1?}"|yO}`~')[-(zvcK!k`zÔ9U4O~z3I< ULn.qYh/eAǾƆWqFL" 1zop [alf 4Ldbf !ȱ.?#" 8` +f_[^YP>ӿ#0bN0njEr! WƟ?&ɸ88+]/8UEP;g^ AVkSHz #v7ot~F߇OO;ퟥ@ʹL08EJTYJK]W5Ĥ4)7f4u>`}p@ww\ JOZs̘~Qezc7B$"ь*㬸+hYl^@]lDހcq Ɲ\jyXۚy0쳊tɴYa?L2Ъqe.X8?;䱠Po _GKB|?Aބ/Y~[~'cv[}KȴӐI^#4XԝcGᵫN} ?6~:ӜiM3gTq%#ޖ_%`!BzvI*I6-RTzc~ߌI`4 P[X#g2n2e\Y0;*CA`I95q/8Z13o, сC74Nҥ5[ϖ)V/#f]!\ϟ`Ekr_no1RYYgl8 ()XISX' 3'D49SXzSA'FW* 1Pnhc%/1GW捿ela@tHc}K Mׅ'xe} MCmRZ`A=By S?Wt5GOܕ7z^:7>"0Q=3sl$2)V(3Kޑ#tTLIjMOmR>!?ݷx?u/7ȑjE%kɜȼPM\eb|b+ǧ~^Щr=}HK%@*3}0 s|d$ г>oz>øJpIH-u'C0D@s !f[OG+=i.ҟ{%:?%.a3b!L7ٰ'76脉/c y92V'QM d)@i)# {q]#\qWE$/@?dZ,84t˳|U) W}hmP8<`j&!&& kyn",C&!\`i^c$yH?8SZ&Mk?Gy ^ T> bJ?s,eYh6u\^7=Rc]`VO W\kO'{)Cůi5^i]hi t >@2"G[ƴp `0OؠW\`.ItMi 1$@_hLn5S +_zn9K;΂{B`w|[.+e cO<$-hbYR{?a0>kG_=A2{3epg ڔiM!LelNWx-c!<4I8 :'CӮkOkVCbX(F/dd$5 '}.eLzŽ oU=Y>U-m$7˽t ȃ; hNPp`mt{;1 CvuCį' " XŨdcfdy!꿁sk/ goݧfmهzC#bNe9W]ԃaqTIa rm)57\c+~OF]}j:>@ٯWޕu07ST?eCK72k9$>=ab8a͟$x<sЫ@A`0d-?ekEPs}̛&acfg6z됩A^'׀y=w7rm99B<;2!d|prsŧu3VkQzG߲u3ؠkB.&mcUP}OCr6dkc+xeT0 `=wmU ^ҠR/s8ITd8 w$X.:׭by>6.z<`rN׈3T ~1+Lκd'zz\>uYPx<d>'{O2Dt3 uܛY19|;yz fne3n>'ڤj Ԙ%%=MsQMha=DXJ Z–K@a ߟ.MPJڧd_WpgߛƁ@9A2⒱{c!"agl/;7ra[Zw˴G[rF$NVJG^&(QYIJ-39pm{fyJq#AOuo3yxy7ZUZhj!+nh݌ǹ v a3U>@ݮ=;- G8;;zO*uXRZԔ!ΰ nM5i3`֧iz&lH\,W'_wrM+[}?p@*{XM8bp30@} 2SN=;p_\^z5ލciVw<~rZwO}@v;z|v)[p|V=ys+?ws{)L&S8ҎYx !5,g/ 是t!ས!€\; pPMN-Xi7yɃ.=Wo܁ԇǤ YDŗelٓ $2Ql* -}2U՞_μRzRt[f7l>66~m=n2k Dfp#@;z{})Lp_Mgy %_*O&>=ޛɌ?„-5glgc+ )3pZнFL7-%3hڭYhEYtZ,9]ou_} :y|g~wGݍO~ݹ.{j*mo Z6^N~8I@&@NfC.٧r8w"; ա2iiyM8H3ՍqthYSYֺ E ;Q|2DTLQUb=pK_س á#0E34! kg@ae'#{p7򲢠V fgXI`xYgT93ēT-IH!f0dt[%Iم§Lr~elDM]]>;ƛ݅N_|[XP(ο~Ǡ3%uTgNY#l: [ƞP7r+=b(Ǒ=J ɮ0mzzȧliF 䲕Ҕlw<]e."LɃnK@&M#vaܼÎOBeC&qVl[/ gf6|-"K`ϪdPԿVQo=i>{drdzCގnkOKz4vQf N-Ts*Vi~u[\;. $a]24ˬd1@9s!e>4pG&%1mo ϓh4s;m*@Wh%e'ȨBpLSzh( 6ݬ8DǏ:'&VF 3@\zn6nv7r:'` Mϰ!GB(`g >R 7;qҡ]~XZ`z1Ӎo'0~ 3\E.Ͳ7ޱ8-؀MBܳXZ< sHil*b=7RĬ-K+'sIP"qDœ(J8Y33 ^do80+, '^~[faiu\SoDCCfLg2=DRF&qUZM,xdh6^Lje|7 +Q>@y|('{ M Zx˴- t^h $NC4lON{,]n~oK۝BV`ʓCGF '^}Ap{ݏ?vx;`dXP9os-iɔ4D9"N,!..cz8Ucؼ_ZyKaiAR2=|&LAa3 NZk/-,A 8SLg}HUyBO l&'¹VGy8c{Z//A0dY,L"6Hpt*pItT;pWG O4afIh퐄rK7ǏY݇\'xޠO3t˅Y;y[ip` ܢ,|(<2?#.ĉ{y ͐' **~ ϻc 7嵵\_tim]W"peI.D0v1wLޱP^BYU̗m}]Hm!hh6g6lMB!'J⫊p- zÂ̕KBึAgQ;;?n~KXfLz,U~Iا4ƫylgbH*'v8l2y )% +03L=hS1a&S*c3Q#'3(hTÎՎnßv;[l~e?—(g N1U4yav3mLB't;uwu7v#a8!ۻi< {QkFC_PجyaP>"CEn;Ve/R6n#zN m.t_V^VΟOe厾v4Y+q\ј1zd|ݿQw?lxQӟ f5<_+?Y}:gd32}@ < -Jwv ",TU悋ķ 59C lf?&-&i=!,W{㩫]g\e+#Nn1CcbW@`MƜAD}f򩍳5_m8$&!Pj5}g7em8>q,EGE `~a%&O1v;•tgFs6l qDP9DZ=Yw ~n5mNV2+_#*إz*4ZF=B8 hwW\fOo,w,ëT9҃b]*fܰGM#=e G0yNר )hpUbA z@tX~b_;sǬ 'aҞm$/ƽv g}4 5*#G`Syֱ${ Z7=6O"+~.$ I@AH E"GXGzŤV>YܽU$ҥ)Hf6li8, XS 3N W!]2Rz- +w` !-6 ̀gQ8A]co(s63sJr|`ΣnlUwYitAlVdVyQ H#"7S0)+o!F fC8$GAKOSӃ6GLMonڿp[$ߩuM'L0R`t?|^k *|e!T\[aJi/iouc\9Eҥ9P?sU{VX=+*Jv2;ّ_QMK׍!~^>hlw5`~eDqaF@H:xc n-(0!p+A)4m 8 fU8\ӯ B R  +sȄ8dBD݌xGj"^Ut+/\tc& yzFsw۹2E+# %>sݤd:z{Tl#0o&ѿXYOcz`n:غL+%\~Lw*wa~#tt'̦ Ћzx'6}= q' (4s~'PIhS9()*X\1I1`-蘯wrKǁ7pie C L?G4H81ឞV>P̜&ܥ^F 'tFbHLx27dJf;`xFuaku0]G#/MAȷ趤Q;w[2N39 UVVQqB|_c(tA:OEU4_E3=V[,n=Z~Ki0ϥk]|Z?'}EHSz$u}U쓺uu[ca'CCy Z\z 'oЗlrB 4YU6lmmw;՞ɿd 25Jjј)Z-Ղ>~tpSZu]Z+àJV -ԌgPyG>QRuOܫodž /)zARg=3]b܊!+X*m NT8V`uk4oCMu`Qy|X8w~[x%߯b> uI%?:~8_=tmsJsg| Ci.'1uF2Dtp+򘶗zA^'r1\A%+[685ˬe"\^(Z3R=wFn냇fc-"gfFliqpf~# 2{2qR(B`*.DBl,?0 Ɵn[8P` X&") Ǽ>Gw,|DKy/qԣ5yST|`egJ~_rLFh[qnwg5 ^IdHi}~%n[=U|evqRx["gq[tD`^D~_"SW:nڸ\ʍk֭{fqL.R@ F_gǑj j=c=ς7X$*C l+"î.pCX ք@usS!SQhߵcI~rރ97 ]^4ڋ0, nʫŃG .Pތ>kF?7M5sta~ 3K-Gm\rvGNrU"XqvN+򼸸-/Ъsei7sE]FfOSNve2>E<}CnA s;i~ci^˭S̓_4eZo@_ p䇹kj8MA/ -A\fn3,A?742_8[wuO]ήU^nsB8xR,}Zg %{Ei<:w3~/9q/j0='S(3ZK}0l8z{ni+s(c!2՘j4xFk17ygKKK `<=*7Ƒ, VH|iiv _Rcdwe.@TǚGZT)~тUD98 wU7Vvuwt۫3 [:DUg3,=j9@J{8 T= 8,.SNEXc{[3<"E!hYN/ba-?%>,rѦkyiϓw玅zZz@wNwX%!xv? ^I=Ԅ͒T&eDǐ?ancs4?aWɳ}>_;;ǀ C "'ٵdbQ] X?XkRhE*(3vky FV+[&d€U]c>͑^E}Ci61JᦹٶBow7ǟ}g_z;kz_b{fjNR/@ \.֒hg> 1c+p1x48y6ܻ?3.suc#vec&{>gO\占PcN!z@GɛIQEcQi}725,aꡇzޠ{ȓzw'\!z"|"* fPv}7g3S2SM"cg٩E%z-l3bLL=[q --n 6qE}FpgYPjwBͲly(aa,IO^guЖ0܈7?\kNTWiAyуPGC,_mdOO-l(o;7[տ>!r~[򩲸ʮ8ncGʞW~!*9sL|2lقcS AT˜P*g1D6#x s@Č馐vי<)Wl F:Z nIdhQύ*2KHoڴy J2(<3K7WUd&:u Dp'>}mڧ[{9t*emŐ$И;{EVџ4G̴u>@^zyq%+b'\&Ub_HZը}vQ޴76?;tCqa?D*ieP ޝ`"ɽ Zm||3cS&!y> k4bʜ:$kOxm`L ]q̛痸 s(Ϥl3!@1:*IUnpt˰&KrOg"E|n(zY\AqtF?pݧdV PDLP-D6r<&§+JOڄLظ7p ğdlo˫K/{_E۸`w8pZkqOBqPe#0T@=BH_f1s:* :;enA’ \M~a "&1!I,ozrwc#/SW{rʭ diue`1eCI=kJYƾoPQ~/f/\n+FYq*t^6>>=נɌ0SP~EX zt1GU8Գaw;` B( 'OyjZ ]IHDgZcc0"q p]•vkYje3WpK p) غZkn  ^E D _2ISl m\nqA,CTp5tnKl,%zhHA1X^RT/ DNfnQ Y=E\{|\cqsσQHcʘߩW=ӝ&M [˄i0ct.cz \+N}zBgYwz_sv7>WO~>dbF/x9gY߱_R)V~T+/6-嵍OׁΉ- ༻ZmtA^O|d`s%@w0ɀ13җ_ rR(w<K[E g_q>cl缁%[̭0i -a1G_I^p/tC9=~:,tOw[e /U6,0[(2qK2ˀ Ih- OĆ);)᧭uSwu;wv{epc̒dy017e{P5e:.pznhs4k/tW^kˆ+À}:zB]|P{@rtWE  .a &2ƾ**fꍷqy}oc8`]8R&gQŊ-5Q{[2I$m s-Ƽ~ʐC$"䇒qlz/Љa`Wq yn ڂ 'xmA$=IȊQB[ 9J x2܉]n$bVM`OeenZ>3l6rс yHom-/A͔E8@NSe9۵񣅷?D,Xw?Rؘ"},d4/01E.w\4 d) uuYJٱ9Zif֐@]w%k3Vzs B[Li%UQzn:i{y k~EsTsÜ<ս}}V?;m^E<:飩SyJqq7k@OY-C^|]ΈxHe`Vg2/-CKJ"X Nj5_6{0[_ Cf{1{{V> w3\JH,.sq ݱeYl1~A^@ry@,g{sc}QG| :Q3Y?vںp!4Kz7BĜ5D:t#0z 0S7IC҇Ho|G.(bz0@'Goѽ]z?IDoչǮ+< HWnnΙޕX6t7FI=8nu?Q9t3]f;zc>Xm@pw`2nvc9"5;povh8h]{i:fc~fܴ3*Kes`qDdBg0f4 {#az\}<@Gtmx׺_#VgXOɲbVw }{F -iUX e:Ov9w@i^/~; j,ܹ9n2)xIP}{:tN/-? A&U#1[~ $.`s(9D80 섷҈'8a_ ׶ml0Y3\l KlU1p`M"ɀ٨%q#Wξ]sKݾnDĞ@0;TbD:H*Fa+VRš'2 H['U4_VHLgƅ{Ban:C`4TI1VyK ox L+ zҿn_Lq .W@IDATwY Vs0s@V '@{> S綻n_i>xdѐBV쑏6}n.5( #>x?|B~{!&*IdLL޽5׬xfH\(*i r\BF3jVLHR\ydY!LҞ7?`j| S.Z2*V V#+{~.gsizc儏)9c$i*582Eq̘ n4 BPMs+~П<#-0~D_G|ʎndfQ FÌel t^.Mi9Ma,LC/hDŽ|ҭʴڡwb޻?;ߣw$2hk4௩`f.?uUˇ0=~M4B"?(d/{ާϟ?y 8b2awfp@ꈽfc3HYfB/tKq3 P\~rfk{璎݅K7KW=ߙqAᐔ TKRTov^^g>2Xo1[h%2u /!M v[HSXEa->1W2a5#Щ}MU8 1Is20k' Z!=Іً,> 0-Vn>bffw[}%;݅WeY%7* CjXNVWfEG>dNe"=SKπ!@3+& ?3ۛGgK_^,@z V|),a↠EOJNFT$ *&Tw}[`oWR)t3McreJC#l0?eR/Az|L_ݾ@~|Y+k^LHC\{ [VF+#8b'F"c12?Iݵ]݅ο]O-o$+ʙ ~;Go|V|m!g<#v3f&Gzfnov"LID JCY,p|R,oֈaE˝c9ZBHoєzu^<`2-4>#[ĹW_^br5^^bvVfwBprޭ_va'SaBm= lHQ LB_?Ȗ (Ox ہc"eZW%H czS0 6da#qWVԎ; ݖQZDFB, Y2tCwߍTܘzM2C' ' Wst5/w G(<~{ow;:<}QyJPUM !ZZO2ejU-V zⓙR雡ҪXO=@8Yp@]_C ዴ4h`zB纅7j7uK1_9rGpdž}!X%mb.-omn~\Nhi2-Z+'0Ew˫)Cc(}W6j` g~Q繀+Wtl󈣭 HaT[:LaE R 2 >i"l8N̒[keݿѝA[ `|xRMu 7a݉3/{~wShb"EeO&V }5"Ú~#W25̏n%dcSe(VV}A_Q%t = >+n%$7Usgnktjl玳BL jl\'a#{nrʎ?1v=#M>T `\\<2Kq*\j8x]pWTz;;;8c>)$`+}q}@ZP0pEEz[%g.`BjL1v=u{=leg܃{uϾyo O ȖC^ͩ2p#@6=0mS/n])mn܊Vgfi-iߨăTWxJ| [BW~|3{垐s۷nWD7xq@ 1-"w 8ݭ? b%]Hu-[E

XVG5.r)w;'I߉%8UT}: %[CN]ٕ3W^a>orhsvL?ZkM( #LI4]sY2?BHĈl_Kݑz>˞Ky;c){CqP ׊<dFvś K+l>u!Tg|0;!͍twX߸Et$D8ICAO2}RUmi.ܩkn0”K] XȸV?)Pg>({?`_DК.)m'UT9nNBin!-4,F. NƯ=!îo͞muOo}zcyRGgp6IgG + #$ConRgɽBc\wvΑ~+L﷠$׬h٫A2ON#~XX2Ze~WxNmey?"–:J'q'IBvG 1(Ahz̋:\̆Ksf(] }" YGZ1u_/9{ۮ*A`sX"odK2(S\Wݿ工А9LĄC펍-lQ*mx_oA_jt1]bH=dqN3/pX[0KCX|beaw>m;쳺 ‡kȥNp; x}f{,iNXw õy) ;Xf"E_(h SX1zY1?92*j۸E`έ|\ wb ,LK(eEtO*tz,.!}nga+¤~x3y ~ۡ!NdeAIWNwG85y*nZU(0OBuGzcڡ>_uXuuypr2lFl`[iz/L/035~~ʠcǵuW BA޳g X2}ﳥtᥗ( XdS !Nęh'/LlEm@6>쇟/ZSKhB﹈ugeΰ*k B`< `<&MFfi<'=9ԄO@׸y7x%^NswKeNHY?=^ 0='{%T)'~Bg{ MP󭽷}WKݒŖe[a, k .pgp N\ xI^d˲eY[z߷Z﫚9{ՋrN333 nyւebz( ʧA›G `IoWn3Ի vNj1^8}š -T&H /c4R7TS^+ی57N?%yHpAx .]̩Sp֒vH( `U8#d% õkJ`8Yt| Uq^ UFi[Ϋ-9V̲'Dil)Onm}ijӍ\0ږu-[Wy*F% e9'g(҉tg M=e L8FrW}YoG'"\zx/<lyP׳.Hϼ׃GYǫYse z<5Umc3{ t  W~ҥF/cD'5]47K~ rJUC0ɓzu^ @>q?ӝ`[J~8ǣ!^~ kg.w˾JP{vK? S3eWH"I+xICk eruGaL{8^"ZbN\_ e"'tIGYw|4}MeACڈB( rZtV>E 3?Ti [\r:rlou8K>'OPRsY<ʬ{mԩGq80TFH#fL8)4-8tJM=|se;H- co|u^PBxG#0n¼ N c㏳KC"$[b7Xe5yɝ|JEÚ*f;ö^rHsM_ZJ6T3@3HKnɝnRTeݽ?.YA*GUa~aTEϋPu IFہuro5"Ԁ=]fdM%r!̣9ee]=>؍xѣcqkZۥv xu'1K"~mT6u]VagiڨŃ( $\YvlBё>;1Y:ێV80226/M6lH&Id9&"l@g~#x?׳U-=1Nأ4i/z]ַ2lEKܹzI3nȷίtDļ]n3^SyzG%O #-*sxD p'fA1&ܫ? ƢN4qfDwTox;/ j̪@G ?nIj$#Pi.3U~Ԇk{*"Ӑ",o*)" ш% :rYij]H~$Us^56M5Qf;ޤҬAY "_F<uxj{|v 0l 'ڟwDWWqytOM񮠂Q9c?0TA٣%S~]'Mzf0xStH{njUK1^Nn##}{>]<F r T`[ײc:ːsG{?L~93 GG!; ŵ&#)pJP*a?;>(':_Ku͚!Qx;Wʹ _ƾj| e֬{yujl1>  K L/nrg*]`~IydlCOOd7LM+ȏ3<ʴ'=+SUO -_E\XK'9PLHbC:sHغEFճ@k6K'Ui=o GIg eEL3@64 ZZ4%%^Aɸ!ˀ4$w8xzj gy#oQ-3"p/{sb|M_0L1帲"l\KJF~ָY-TO"9,+ @9G V+u8B_P6\ÎC~|$p z-3{-olRٹRT=;n6lxeu?wR *߆mÙPw} =D @v -L|bvDDv=g2Fq *p‡%JSS;rVW]1`"/nm,/n^uhձ_稨rHAVr@@iZS/"nUQ$a*, 2=NslI*OB.g-ྀ Iι.El f`~؄gbR@Dfm33q!PMz.L9R( âT 3&BDˀXRoU QeZpYyIȩ 80 JS_Ͼ}0NMѢjm gAV3.g si歏Wnxc56f}w"Fg(uzb9N=I8ORֆ1꤅C>svs_ ~}{y>''fkKf^>7l=Wfռ|j2gf1s]{-BG[lai1Ȩ lBdFrC4ҡQcnv*,=e8q⧛ִu/a'懼@5@9H[}ƪ@"ܧY2gjM51٥+\X*ul0B!|F$N0dJjM.-qPƒ=``*/Bk lrFgmbDu t퓜mϜ5+f]-<͖E-}g [ӝ/nT2ڣcuh)7sgm[[_C;(ΝH0)˼Éܬqϐ ο(Vأl~ c .xtPȆLˀAMw "XkܶrnYwB!l?[..VXbkq՞\b\)y_Z q/i5!;. AXH@S6 & $@ђ.wb}KDR8=. J!d 8ՂQƕo< zj^͞4/f qgOZM=NMpjW5؇(p"*d'fTBhw+[zi&@:>DpѤ_ܚdoF4muſV Kc $?Y]sVh2o=rkzp]g9'O4۳ٿYžR۫xC뼠z6[(3Hf+`A`e\j`Ndm /i-}\Jj{?wD2aْv`Q|Ko5FW m݆\f((S(kgޭæ^[MMYI6< F9 Θpϗ{8(ē~3gp8CߔE/a;1D X*Օذކ1oA7A5hDg~%-2CU0fT.c_dTYtc2~ gn=7Գ.xVwi XӬ`H`=wqqd#-.5`Q#<0ņMW&?iQںG"ǀ9S6~ŃCR ,kl ԂqT%n`E<%@F#bdWF~4X`18D!gК#?˅"SMa5Tcbf{Ô=DY>zg%XNk>dR}4;t٢Ԣ?Ap]umGA(V TAd\ h1C[9^⟖=qe`7A j\XEFD757V/Y)1Ǐ>Z`uxσ` t!sŹ&$CU<_H/ӗ#F":H!Ji40ž?"EfLf7.bntGU, m78ܾЉ,5kmᕺ7 Om`:vkk1cG%^AtRdصa\srKeǏUvl y6&E4qCawktKIQ[[Yυ3d <:22rdPw]4(! [d׊ N+9bn:ER`׆|:uqF(F9u:678n-e&r:Qcɨaj@J@ܻ@9P Z̔2n-_W1Mp@"7~jr8?g oC= \;xvj RLʴLH5wG тYl"!Y;.Ytץ:%l g:l"n29 3tO=(+^{mUy^n6Y[ؑM,M>a_9^is#cp#jbr?i_CTC" :]qp瘏(M׼:z/W'}rÉJ 6Yn,cRK4bs}{opˀ,l*&O}2X Vm~e\FXf|diWP7;3D16/szQ]S3l{_zgb"@Mqr`vY>P@~ |=i{tL뒔iAXe QJq@vxk"3vg!Y▀ _9 'J%l7.Bq)`/p_-Tmն66FEsL*#[W{|ڳ:d>cn\",?z0t OqBJPLB@| p?_2\z'O*B0KiHB`-d﮾}3=eI;d/@V ‚$F2.X1Zv1ܡUpnr]!RؗFP-+IHx MDᠩ^G8[ ֵfb əf+u? kYHa-JJTK1-Mkx2ŸXN;3Jږgש-m3NC)t׿fa",nEOjHdhmտz[XNҦ<L :# JUf٢0]gWxq?>Mś%EJU,?%hf&ݍC!. 8KO|))X<)U+ ׳W=U};qpׄ:|njE%i`FQu|[)mq>>Q5{~.~!ln(@D?D}[gt|Xh6@CSy1 sP=@;sh%eףyäON./?3PzRXEC3pfg ׍s c]6҈Vm֬ڰ`ݙrA&ni*=0cL}o>DNQ'~˼:,n!%‹V8$$~?H|7Zv:U ^lui7YݚdhjՍ!740oB?ȟnYZ!0ͯbi>ś#[ܗ{qhfm2va,hvjl[YC럟H-d{)J&,_f35V//'q%TdouT!d@e6OPlG }ZATV6txǞb QE떮90P Of8vmwsO!4V_&ϫήo]HB~H>i(SxSV}:x#L4׀o9AKY[ Y?y&˽-@IG][0KKu f"0fe#%룝;Gnkq{+b;Hġo48Ϛ㺮&9{G#e}G`5@!̚[.Зrtl6'ܸV*;^>EC8_ 70cϿ|9Yc%+gSLtߜK%Sk)6|לNK|-Vڥ^QWqHS\635o r{uMoj#5;k^LQ tO9gzVG|vgPqnu{7<78d?0A3gXnx]_`A瞑~!¦BvlVFo@e|hsׇYxZQ+ѧ`>̃ڜ{{}]vX(Kxf2.@`ɯ!.澿qb8?Kf8Ȗ~oBr'pruY`ۯHpxN-ת~wU}?[ept,a[L\czWF<۷yGM3+1@oMkz|F5s(~aAE4d몵[o6^jӶ ) βs;}˥y)l䳨EwL2),?SƻYCJ)@GO ?{:ձ'a|ˎA%ڭ ~cw R>+qJĖM.c3g2j,W|x~&OӹHٝ4)E! rO.8))ID5A g3ۅ_s =WT;Re`eɐXr`qiLs`v_{|qk?oYZ}!9lYfWgX,5-s\#yǒe.ANxKAU[('C9;G@JU9Q>Qx#ޏ)0b2^Jv9gS/9]_ 4 [;"ׅmܴFgd &Ɔe!e&=x#4Vɻ8gp7^z)E5F9^Ȍ뮨\hG`˙20Q9s֟[Cβ^̶m<3at$%.cr{I0aS9CG 6gWs[f͢-Ym M^Wmlrg\0SiGu?2uFZ,*(9(bl':n[PbN?2% UGnY /г/̿~}5ptHHfu*ޅdNW_(g`߁N\% U ."\V fǻ9% "RG 9L,x,7yXmrn%X2a:vPDE ^8$RgP83$[eup_VuY6B\ 4U~|lkj _&E6ǐʶu3<3 pXO4I\ .?tWA{<<fjpEuO?r9пT%$: ?jF9}][>˾k]=.&: \^t@2@TO=U8ExU QZ Jw_.2'&SƤ2^{y%/;Y@{ߐp/0>[$ʖK lڼ1O>h,Tn4LhaLs` µkoAW}AP4khe`֥AuʭZU71^R=`W}ǹoZOv#yϭh(cwyg['%XuymK>bqaN꙼!0QvɗY{ܛxC}uxAf bu?G/8Y!Ygt{YOpNvP9c3g{)*_7`~8(w5_nE3g|Jnu`ȭ ŮϜaW8r&.+yvU  {y, N1v i460[,%ft])R43Ej0X@਷NsT| o0$gV͜\֪g.g&ï YƓY<_)I:oŵb՝AVX\X6e^'ڬǪK q\bCyyHuf'0߸zYV)B+ӦÌ5T__Ib@I[O~Ź9BښāBE7qu[?59w/E@n$BwFnezI~_ rM0O譾pjy?jװ\^SH㥚Ehj!+@X Hz<U;b޿jhҧy*s  gJ7/QhFd2%[2O'$2/0{d0/Q9@uc6}mŐIڨ_=\w'׆<81=ȡ Fep7©Nsg%GWpd wإ[fJr7oΎG4i_*kid&ǫB?VM~U5{(MQ5n{Ƭױ 7%= 5©Ǟ GցX-~?8lh<w{bUzZ[SFH Xo6w>i_K)5H`VDl=5E>9Gq%eڎftdc遝hzΫ{W7Ex)d|?}GB: 1N%ۡ6QW38` ZVL;kPtfA>R HelQ>"t8TYd$YW;,k ~ 3GJuW $>|w.]ϲuduG2M>ku#*PT}Tf?PR+Wb Av y@v jpAcr׽@\W!#A1O#鑻ֱ1r9kˡmMW?֨$duvU,k,GWz23BLJ@.G[ U{W站%NE 2SFrщ  H (e{hqD@PU0jgg^yH|?ܒ[b{Ff?w4LdF"߉RƦN=|NC>oԨ@r gS㥮~vKɩؿg!gh>Zc>Op%mxyt1 t fxHy歼)SΜKƺfgj_,33Ԕdpȧo滮j((_S]lߗTzmwOSqQ__;;A%K,L%gFWS'J#lGr%<\q9; 9 iC\g]ހ (-B\5 id$ȍAFıG2x&(*dحg~^ʙ_}Pŭaߍc9ws_q `~%Ķm3=9'$sq~^*#Yةpsl2kpq5É\z܃f6dЉx$N2VsK׮^#"OӍ_Z4BJiސA NMF^['t2TRT% 2'9|gʗ"h(:@$ft˘ 4ᵏ(nLQҋ)g7N b^ZWA |jMRrxI5j N@$PD`كmCzcGǽ0?E= {Yf3z.aCGQpfܪ2&">€ά&GPMjmH%9("L=p{=X w~ eKsg@ߣ5svSnm2ٲZ LB{Bۍ}ω.|䁠Xn ȡ( K}5#'=zܼK#9N~icI#AL1SPksMwl"y6{L`e,h^\0BWSVĦ |q޽ >lݍ;g~Ow/8sK,OScmN驴8uUz3^Ǡ+~]Up꽟`ȡ1[9਩\fU}پ=zڳ&0efb|lG5~9 {?~'s?/@ٟ_/A/y=BUO]@}ka0 _JR2\50|y>~I/eO_f& n3*;Ie[䭷m>בW[v\Q FY=,Em<p ysLk/_-޽R2,@h&=[;~DHv M8&].4GFOq=xG^k濐yEN=4@/ 3c%T5وKyt4f=`Odp(x*@٩1[d^S @ctG9'&iɧɾA 7TgW[{M7i.7}50u~>Varĝa-$?/$ͪ lloر<{jr`g5 3MvY.V],|8I]hC5M/c礵},Muq̃%>^>nA(Giޱo0.eFr PDOyΙf:_G?у AfYؒed0 ~/Hǜ:5ɖ1%22:Ig7,wtu?f=Ǚ}fmAC oz~x7'Ƴ7RpCF.˜eEwjp˽ʯֳu?_ ~sΐ1PQ628bok/2t&룁f&1⋨΂Q <00}g5CH' -2' !PP!31Jמ_Q0 Ћi?)KmAf= |W'o[ADJ/t^9=|_dK//CmwFݣ92H.]B 3\4k3Mzߵ<IZ<9|U)=uR .QT@ ?0)cg" QW$u<43O1ㇸ litd@ZY:Ջn,idpJ  $rשN \z~%TN&_}*zKqg/ɔiMvƍ|cBȚ)ݛc_%M̔8 w%H,0`\ɖq&'ȇ:Zt$%sWWc} =fH"K4W=T\ǿ%xCWQO.?3٩pQx; OKۊ k4d kޠXraUem;n'x5͖ohƵp8xZ4t6_ſkƛmWTnxCӍ݈9֟w0p'AL!O`iܢW3aB`h3 $w p ͔5?]'(5T0k2I#Rnmޜݍ8M}ު 7fGM&.AEo53W[twԒ>-Ù5x 4Z0S7g6mE7@{"tN`O :m0/0B5I4M=ri68~qB_<OJ9`l =v!; .#S}1󏡳goI@縼\3 9ɇLG!A=xS;~սrtW"}WO^. 8;;j&fi<(6W9tHLnǵ r$RP2R]R<>pȑ4z<+sY]"k}k+C/KtIuCU0+ؒK")Y¤]f7{7wBEڱ-wy~K( ϔM-d5~IN}›aKv 3sfzֶ z(ͱV_܊]19A_A0+P U˿< X{X {DXlb2aX,_19(1fdeؔ!x7`aPg^0ƽǃ1WD\mZC_}O0=%ɋ57׀E 'v' /5ĩ3e%a#~_K8t4S6|jhPU.`߾}3IM]e:$8 x2P%PJ:{|V_dPW/5Xʔ[|s/+w1c_@g {~"SdȺur`'p^6P/حKtR1{q Nѹ>A y;IYq yTZ8ʙjF/!g!}3i4r諮Ows/;,,Z3fQu/in\-mPP|5@oR}QOR[8?q e, A.*eba.^B>/O!Uț>@kd1̣_#q/1?݄e,^-=S}8!@`Q ^4)9Kk'sCAx?81"*BuүTH=y@RVw24sk+bv??O Ji)n7i?G)٠j/\X1=[j6ψ2꒕D|lCb>AzubQVG;U_ZMSѣd~"'~K4LJY׷~}{A^]`?/0׌yTQ,Cl]z=\9+t3`Tr̹]=S,6Pޞ2yZO?@QFTs]qWmD&Y(OM =G7 ^o>%_9nWs# -5j[fzݟ@U"|z;YUs\Qd Y_AH, 8GPpcsa>h$.]z;>_hQ joZyfɿA ޖ_@wƯ krTTLazr󳅧Q|¡yC#I&R5&ڳ `3=GCznI`M: NS`f_!Ӷ@&PQzeq1[,_P.cd|d9Ȃynf&S/*`ۉ <iSt"Y'uң1RAD7t.<]pCbmUX A]*TWe2#N=<:/lLg{H@d lٙ#4.YD_7y&p/XF vOU,u{?ƲIVn"~}~%MYH3~dx c{5m3<煮 OQ9bCt7Dcov3\x% Ggy8s됆$:Os%9iQp䎻u)7rz3U?Q>#Aߣk:OR??#\I=⑶K}vM(pݯ ~"@5Rwvḛ3, }Z|}ܑ0( 7k0De5&z܀?3)>l9qtg'G1V;){=Oz,\fێq_Gf)w[X: 5k84+Fb=_/M`~xdQ єΫW .?fʪ_}Hk loP%-zmŲ,x4 h/VG5\3ZK+UaiKƬgdMUE7ͥ1mzQ#?h1K)'队7~ lҙEYYrn.]6pԴAܵyquݚ}_ƾ0ˎP\r$݇C!GY (/dQdr?tsUc0琞}lLL!u˘^Qf 2/^4@|րzBD]PHۯ0CV}-gIr ~E=B4y.IPq ;B^qc#<2&:Uƒ>&%#zǢhՋtx׻NJu{TnPŬeK4DqCd?ہ~۠^\LuɃTwFq :%C{C|$IG_N>3fo Z(a)߹?Pݺ?}J#ShnI{Yv n@n4c;~3 h}3?Nj?}-ƥ Ȝ}T&vgAίۖ24ǂ{1cg}gKW(̏CԿgn(ՋAxի^u$Nߣs~a;WoM [ U064ؘ " :ARoFȯ@ | ] }|ry^/T\7>f 廀>n kT躕`_47}/Լ0g[Ӟ 'tTxt=+Ynr= >]F}GA GǾQ,/NeTYO_#Ͽ btgg ٟс? juRq~ֹԻ^(n<91OdiML7}[ srF:KpE-v>?|la*:*z7ݺ>]S3ԥk@$)nj{n}6i&c.ݒaZ5ꡭeVC1KG 8,5Gӳɺm)9qp!W;2"Moz0ae:' ](X tb`g!>"O x WlCr'`kpr?@^?w%Tg9(ja\ WRL鳐nd5%5/ZT#b\)E| G:iisV3  ,5A~h~*ka/? ໤4=̓\HF;t tf4YOU3LysPI^ә!gY1Z)}"װ$ FiJs>76].zTruл*m_lr.HY%Ì- <ͱ}맩yq2̞$+'޷@O#A`h:D ~ W1߈tARRٟ_|׏g|A9Ĭ%<$1 /?TgA[t:5麕; x^SNյw}L#]DA":t0,ȴmcU2龍Y3}w ?Ƚ EZsRqA`Ρ{x`~-w`^W2ȺW@]wBDӼ[خeH@pˀ)i..96ƻE2)3@Qy 2I;˫o5wc'0j7 4&POT[[=)aR5yOП kGG1VJd4;e .: , 6: h[s ko=qjd6IB`ƚ`w !F$,\ǖ] 9ѰO)!Yވ̶PFt]肻سKۼڹ,0gevBWO~ɣ?BmCJC-oM%? xߛq_@X9D#cPw/FoqK]2J&dze-Ϗ?ׯi[S9Qu_3hET ]njމeW 1fnȡk66T72N u>4߽)ZC^rkw}wϲ=@'Yp `e78Twe bbhP./;@ _me;>?|pe~⾐>7lm&q׳} Sx޶Mˠ[_{S~^)~>y^h?@eszw+&^Dk~\J/0e%U &T: ]Ӽs紝+17s~6J>]ҜZε}K[=7#ぱ';DepxxM&_3*X9nV~Oz0 2n2\[Ӭ`j@Qc/&bO2w΅%7i6幾R~[|.L? ] \J -ܩ{i/AfʌMZ_̕P?Ks5+sD3.fCe 3:1𜥛fq/z'Œ AA͵j {*|v^9J7Ku gT6+z ߥep̓;齪U `^,dYx+- ro*YA]m!Y%8?E,"@aPA!1@`"+S<27ñnkmҬjb72\sq+z3O|q}%K_Ui< ">|5xà$Kpyխ7b^wKh݄X[|6cv†sG !%E: J::q/k/z Np|ǎ{hǎvS HW͝:Mqak%%K.Y, ne־0HTU$5ho~ܭsIԐք>0e]_J@&Q$){,y;ieS\Ҙi|Uu -p[(*w赯}KX ܂};pWTC a5ϳ'?_%ݢ裴1 2|JJ6Up9[s SJ]|6|qB8/,]{`\(qK+tn+'eTxSo'|9rakzJJl#]8l[eVm Я{ w I;.|rk WҹfLV{y>nOcKYFYR8~wܮ+Wms'|H7:Rm %,Ǭ #Sq nHtOk~ևdl'Ndzc} A{X@ky;^Diir{w>ӽH\G\u+q3r"u} ~[&|PHsͰK)at(tGvifsśmbY`FY `־m۶n͚5U|K 2u5[ŧ7- 7P Ů9L51OkFoskqp|J$l v#O:w^X^H-pzcea u& [D+̲dXGXZ.+xC;Oˁa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  @IDATxԽ _Gu{K"Rkl`̀1$`v`%/ag&3q$$c2Y 7lˋlKԭ:ߩ[elauk9u:unT\B xS7|sMozSo|mBmaa7J}[HX,vJVtמ\[X*ʓ"Ϧ9QAOLf2꫕%sٟ [!4* U(뮻.u6>uccc[hu_b$AD٢CmquAi8"0缝 \sMsmH&iMA)~E@R>Sʿo677_q/ [\ohjj2$ .BdPtYA3cz1S<677'Է|ۀ0È @Tq_"b)[a~~f8f P`tr"dV__S"*N+Qg8/qKooMW~K!]wu%|}C}}}sD:DE4HOI" ,Da:>d'O ȩ09@3v!ߺzޚ[zzwiuvv:6 U eR4u} 6+UFHͮB˹7cHx_QEkPMӒ2.+=iGm O#I;ե[KH^ ~}DEˡ=&m!_%mVے%0B^41Vms7<?bKcߊҴ]֑{_ j/H" ^/Um۶]2E2ؘrH\N\R>>6~'mO٩In)L"Z X\:ڳ?O&Fӡ|ΚZVYwW565Z:6PpFjV/ˋ|^/}މ_t|$`C;1uQ۱sv{~e 6sKS(C(D7qIMBx2ѫB@>8q[*Oaw#3Fu睿S g 0G1'>6,V,7NН*/KzE!~1um5&<*׊ eHOy%ֈ 7i uF#g?Oq|};Wa̸Z1L6[p}yBN3u!~&Δ'N[$+MB#]aQb<8VdĄ{^+^9s'bJi0D`YlMz*wE.%U|S:\ +Fȑ#/l_l{jPID7 t0f)c^$_"K⹮:T~* y#a>G `-3'ʳXER`Rbe-!HZLE`m9oSQ}ĩۗHgk@"ɰ/Zhs$w$OOHp]!UؤBO]W +,X Ak hE>oGh7_[Hiy;/p2$< &|qo&YmI#}~e|_wunɉ)w~/ЎBq5?g oC!0a6I8d+!4]Dx1@ r x sR ϧ y$}r/z^rP¼O"?fY+nk73Q"#m>o-[.,Mp+n"OegRf歳c@>$0UL`Օ8fu֐y=&2!. ,A6нp$c2*()R4xL:G(r,J'[/0lLhc.!~ddnᵖ)#gb Wqs|v>ێ! ч ^׷*&gJ*kNC=Y'-'>yӰNXoϥ͖+@|zxta֘~%jt00J `"a!FQ4(j&W2d` $lFb&Sڜ5qI})[b yDL!h7]A&RK6Tn}w82S)kuMp{~Z&(__9:zg@qh9He!4^9Dt:n+9CdKYRHs*XBdɱRJ*Acn ObPRSa(DMpԜ6ql+ jAF[Z;-ۿ/}#e`?Qx& @kWw^[F';v'1}&qD|">=:MPhtRh I5E*C$_] _ ݒ|M]Dfm8hVx.\X1r;[ȟnr&pNsak qB[+(22vqun0JF1ԅ(x0^h̖KN Y^.LPWu~_S3Yw `#Bed@DݫHȪWHT$IOzlHkP^@LpzxNvc&Pp;&AЫxc5Kk(CZɃ)E=17K\E$Hf ^$:Da:O (L1i% ʁ:(%>xI&Req߷lAj6LT( 3GG Y4ID&pP9a.bά{)W jV3(.e'2_  e0 >$3fBM >  ;Tj(A5OCӈG䋺@0A,{q)Pʱ6m?1['10yfNh\y啢]&X\ ?*lAĪiZ$O룆g7ۼa XN(\CCCuuqkft@}<Qt:SpZG~BƤuUR˸ii_XJY*o;_{Fm8*^8~M6p{~lw~~\^}^ XhM-0@:Zxp#L#`S( "5H <_ B%bzP{xɡ+7/FڱbP?A`HRv$6@OD`FеsZT Ķ_sKlVjdeDFmu:+\_uSsA5]ts/!¢%e 4]׆'1>5߬@A=7[2*a&u^6`b͍d;#48gy[\nuG,sQ.dKuH43uYTS 'ekx fHjQM1R Cǵj*PLJ+mJQ uB t1^9MW mPc UznO36p:a6}w]1/NC{يU}H?UklH߂"h؆fP;(մPhOQ8GP/VLqm."_FV`A~ Ylj9< Q4ͳȂr> ?2>D |tUwN  b4BJz;BemElf0rK g:Ԣ2CC;c*k@k4tx9K;P9.χP[ :tض=0/6dBX*V ER+V7PBgi+|'QK;I cBTa 1P~eqڋn I,}+F'3%?OW)Y'&A׼/RgjKaw52.׻z=8o1~3ız-Jr椢p aiHčCI3%M9{Cxj g]ܼԵ&ll@`gɆO^3R^ $ 7>S[jZ ]uµ4@.9 Ie2Bµ~zرv߅4rTq&'M,7Y?5BJgUw\zur)^(T J?>ej/I>z(^[b)*6U]3됁'[@Q; *HZ*'R=9d",΀P%M`G#+ LƙDˠT5ap67QYwVjCER;@ʶNJ-27Zs|C,Rҋa#>ˁƁ SznҰZeauD&DUN oxXAĴĀLWia7 Ԣ"-68bn$+#Wl][;!<{ yz!!`$DL%ϦN^1/\O I_|ӳY8? BHPpCLw#.2i]VȰRzRãtO|_{ ~e UW" Hi؇D /ty #Y b^lBўyRW].fYi_+$j Q"{ wԴmak[Q?[?ZB_8" 0Z PH*K^1@HEd,_8af"WY@IJr.Bi@罃R͗-&[ԂUBIXА˩VI>(PO͵߫`@yN4&luvz.wR҄#h*6o!G.<9 sR^zϒ~_IRo;6qd+ 2ZcoA_|p>A/<\@t\Wa 0OPߠk ;귑T) \.B)!OvC/T!"_ |&!kLU}~j!$K\/Ibe n!aGHBX+m X5}fPf"&Lơ;l$dɫ<#^??$͓S>]`Ei)l3 Lc͠!MC^_sxR\C/қF5qBX&rz')xO ӗD+.}d [W,H7Hb 'r`4ʍDr / &B$C^]qXվHc852@޼"@@BkyCҨͲFpc sS*GvLHK⧍ *l{4Ri*]D_!i2.4n^w^HrQb5^80zܻ  RfzRpD 2J!*uF//L t ۃ9#Qļr0$ tMwdq6UEܠ3h@/#rdȗ0yg1a;Y~&uUGjOv3ϻPxb0էk8gI\|FO~cZB^dÇ _)SZ+IU ־a h~$W݁ g5ײm"sG㔍YgXKX: ~jҪj.<^i\zS;JgM\ Qq&œCnB;xT^D+㔉& XE~RWŚ?Oey1VlpZ"g ώ ֦nark99$ZN!Fy7:ߌGC iX_OD}Yp0jD0vjZe`(H_!߇C 1k3Bp]u4{ZQĺg)TfJji0E8y+w|Rsot5_@o|i0e16OE҇8Q?Ly;(uGV* wh oK29f |UF<"pAJ<ĉAT!4vXc1b Fkd+OVV*K o~ ?ye sӦUċV7HVݖOf {l:?Gs)<:|i8^\4 WQbFγA#gwu+v gB72䟍Yӳ%LR50ZVgW`&%5p狯C\ȧ'˻`kG%H)VHH9+@y(JpBZ`9hD'j<ŋ<h|[r8IKh ASnB}FxJ۸~E`ų-LJ܃}q><{({35o#k٨u3fyװ)s;6\&Q{/ڬxp(TChrF^':)(k15Ъ-?)`xxy8 ZL!za#qLD+,<e*!)C(Va4$ ճ&$ bI7ЛB;{C)7 "@ʬaa ɲ. 6w?@J W7 Kʕs eNd A/^ Xzg>!C퓦s] W^aTrxwo*³*bcd=uw2MV%jy ~E ?M+V*X UF! $k{4뾚Gwڝ,a !\lV*NwbT YXbw)? BCC]%bL54dp>!΋ ~s OpbE%<3(.ٟ <*#_rMz >)e_%j Hh%SE%އ$K53nSad"t+{G KpL1^~Xʤ @0kyÕK8ˣg`Ie3,vG0vf5~"^N| t@6"ݓlM?b#36^167} =Ni^Gekx%_ iaztq5(tJ:D!KDP𕄵XK=G"OOx_좍@9ɯnLRp=_^[r_i`WYx)C)sa H3ns8a+ayL PPXgJsg#8UB,1DR\_aޅ5BDGmUO{:׆Xr0c GܕE}͒4jX˻+!8˫Ip#;8@B~k3*D䋈FHϯ\5ߜi޵@ < 찕tK^ =,ҠjjhK-{fn~K!~;>B|,~CYXA#gţ.BTͥ6⓳!B"U6@[$7eXMT*bFQ  9* V[I{3Yo|rYے>[m[v%$a`]G#5Zĩmawf5q ~{^73i#F͢p %,Z&[d=o eLS) "t)sYѤBxi /H8 ~u,] v[X?V!]r\69#@+]"d \KbHkT+zrWczAZ5p" GCc \|kl>PJu#Xq^Yy1Ҿ_;iZ9jD,ӫ@%מ8f$Hَ?Z;[[R@`8ßw n;Aw0k? Ɉ'b:GOL1Q0= Ql-0k7,jM&6g\zTC@pu/ aiNpոoV41|*қFysh_ff)A4/0p\a>\Ir* ^ O\(QD 07=f{[[ uȵ+\k]nvɣNlVTe88XT$ 2rRl #Uw`?ꢴzXӴ6`ٚ/wA5KxEm]ryl`l6R΄!-:MR^J#L|g>SɊ cRs g@ETr2"uRǭqc:yf'GmϽ߱_n$ibIIq k*k'a}6=8jE}kh:_TJB%BϞ*IZ CV=Q6(̰hO^+wFk[ WQ){~V|"A`2ރ#JW-kHFb7>RG'Z!KL+Ԧ8Rk2{*K! c<˼GXC[zy߭6Je/:V'eOY)v^e-K8;cfԩcVCpaY;)19CR!š:wUϕUh *L~yfk_z6b3"ƒ>>΃ӧmߵ=H~3qģ2kjlڧ^ٛĪ!YgxA p@J e+FүԢ4 Tr(8eNɟbCY~OUmֽn-mq=ZCL ~D-o(SiرיNɸ+ZVuLMRՄS/v;lfxȲme:g1BHbkZ靎0T! 躥fsLޢpt 0* wզJ$xĥR9J"uk5CGmQ|=mKd8ի"RQܽF_as3q96){@1Ј\O$ ~[}:yeOؖ-j>aK0Pۭ-񩢪U:ywk'T5>d!0Vh2I=ε!WS(I"0`cZvPHR >q^|&8D486kR~F'=O4Խz81_>Beܼy>9tzVo@>PeugN?poq섙۟<0%]r=Dx:P˗ZJ>R.oC'Ӡ.3!âϧ/0f&m?e^ ?4YNp] MUHš6PލceH}"^+vR)@0t*T.sU A(W Vߜ:aLJmhdVg+wR}O_S>o;#eMݝ}>~N66{zGFRvAE - nN+6ZM}t|"֡4~3˗O S;?>j#ޝ67;|*N@`:'p|Q`bHG'(" ;QVµD#4Y%Ex=\Fzot92r:)Dy>ف!#),6cH.pNoz{ۧyq)m*GۤK]'&6L7 azgWYC{g OI}*'M~-.~7;} =ߵlk5tZ+CGa:ƛ^XݤJ6~ꤍ:`Ï1K-t ROW4tc;>\ ><B3u_MOAC=@ٝa=8 %lbe668: !PG gW ų=G٫zϐ>NWz 𪀭Ƭjş3\8\E3q=C=+K R0,j;~d'v?LS^WYJ'B{7 !!uSjZ,IDAT,.Dc$36LBIy-%X7e~s61{LNN[qQdHάA-^dX^s⺪WB;(rSSV{K`_PO)FLʇYc3;_SjsC ƫN-aNzfJk2$,YDU:Z90BNYIt/~xol^g£om$#34li_j/-lG&Ql7'}WL>l8DQH!1MIͷ I87RHlqoo25,"+B~ĤMzX/4]:t7LM&r;$?L3kK4UڬUP>ֹtn4J-GlazCS6H-\_=blFu⋶!#Xz }, YߞI[;DXS!L@K I@jҬᛛݏߵ%n;FD38x̎1朠lZp UuJȹQ:kong,p%(DTSuob9i|,$K:$~.@%| Tjva=  XBE$^`_i Òu*GbIF4"|Ym%/ҖH?θ^KdX6~zs/ 0mRmd晰YMST.̷ҿrܹSpÂYqUlYb7 Ceѣ4dCQJM JkP{}7{l]K 0YUpðk&u]6~i`RQ sj(%T RDlIڠgB_e,F.Z5HϹĺWmb*kjpv9cj8+@~~=ۿb0uEUs`d[PyW5nAmׇDmIl$ "~!x ʬYy616͞?STA! \82֥jT~'>hc'mWXs++%tqɭTv.^$s>v>Ja k]Fj;O $` ŞGsB03YHG Y7Yґ%dyֵr=jNsU%!?VuxbN쵃;cCpĵuP~q2P#\@([NJAO%,)#x?T*BnkFo[Hu/Oxq /!cU'qZ i5F,-LއN~m}-[u)_rJXBUZ>BgȦFl|#lS̰2-,; N~(}[4 D+O_c ` I0kDGC8Su-z?- /"V_u%!&ޑAzCVoD+s̪zas/̄Mưw-LWǢ'"v(: W!oﶾ%/xj똮.(iN3f31! 5o/R@W"hyUAysͦǺV;t^󙛛~x  >#95'5A@7e> _ᛈV+֯1.t%|-*h*VK>ϵͶmΛAk B $Q2 i0gLv L"IK<m!^m?`_Q%Aq|ul6? A` 6Qg'Cn>uI~9G)Ӏ2ڇwťyFKem|aNgA1:-eT~yz|kNihN{#Ρ;yCo֯{]¿a׋Iqyzx]A.0cL(JF@đmGTkJOTFp-萅`O߇a̷Z%W u9g2`Ê=&f`-g[t掾E ݌[je-x +R ,8@<{%Gtu굯eN@K`VzQU}w<V-y V@kORqսnQ`x<>Ԉ>=T]zW.SlďTSnuKmg?Eq37YgOs&RRU (E\Aj𗫯ԖJf- 2ˍX8~lKos?,]n:&n-ۈcMơt.LA&DN ޶(ꁉ& AC.x ae ' r2 _8?vGv|v4ZX }_ҮD軏q/60 .eס|Zcsoטz5qS{즅:F 9B!K$u%NgC]G3.iYv+=`广<]YgxZԚɼ;IlJC]Htg1''9oM77vr,ZG:rCic}!NxT8q~`S`Lgt*P ܈NphyAEg`[o݃_EKǠm'&lvv7\x\bm+}C*P7e.劶~_"^éѵLp6D+mm2(!!>9x:gw]9J<^x+@yrpTq17xf)*D L q<㠙eJ9gk6^&ŨWk%gG-g}wr/g6ۊ6fqC5hwVW3ΥD@+%]{+r /0w=>a}?o;s cym߱Mp!ыI*!6*]0C6tn&py\{LS-O{NFb./ţƪH- jPp')u*~]p{A$&MsLJݶul͚V'd$rhpGS5gKϳ՗3}v6WZZ3zcSƝ[du*X< !CB|!3"V%6.^GblT# P*I]CTi:ܴH\sk.{ֶnb!o%zU[^b819h{vўu= dEH ?J44%CZ*"Zy7dR\5So~3~Cz"4qvM Bٳ}xW`70BZ 0e@$鮚X ~kXv=+DO& @[A?g#8yaXTqwD<f/z! I?j`( ̇2yWCxA1W6oK$  _'74 \۶~غui=2J4Y[V+.Ϫ_Qϻ>;C)T8{{ bc7X㊛yǸbWX{kk[0C!7ZxN<,;hSG[pmoWEiBxQeHfXRb½-& ۱L0E  w[@~/CC"zK [}gH@6Z83E0n흛l՚Wa4^c0>Ss ZѣyqtΝM<.&M6;Z˟xiR$\-ux cP͛޺zYgZJ|FV|Qbb1;1&欭e 0#NC5)%YWՎ qw]7ixbv-@*c(@cVp(࿱?;54ӅgTX{P9\-&Z%L&!܂ǒ[UVu zuvxA۾j4:Ӄ~?f&^d 6z$<`ִz}tm@z%m0华`z>břZU&exewyym-a˹_!S>YKFGc\17!2ua HxğC|fXUp|LLL;5WA4D&x_Ϯ,"ҫ*F1uζh$i}% OMAQIA6*U:Xֹ>:F_mőK[ۮ H(Lִ៬%_][RI<ʺӣaGl|d{O+ז\1(>0ZCxl% L]^J‡n ݨuy0%b ;M҇G/y4W ~*P}{Fy<)t=]-FiwH{bYO(yVLcqI4k\=S9,ӓT5u Xub&Tn]VaFr 0&-6]4Vԥp/@5Nc=V2; c%4ϲSD׽O j cI~S!Xt+]O)2h/FM^F 좩OF@誝Qe$j)s8[*1Ƚ2S`+A⥠JP_Lc!k]ESI k=/"V, d &Cc[1"[s YXVLoSC%m X]A>H/O}~9&}!V?W,!z YJcRfJ|xBވ.ײ 4n5-g17?*c66> WA^8uK;,Y('y1DL`<HCq>,Smpi=} (qxzm۰ Q(޴BCr8ߐ9JbcmHx=).RGɿu|h#Vy2V 0iQd54AẔfa>w i>>`ӿnaQȈ$zI|/=d uk K"-- >9y `\GkmJ%3_\@E&x2 hCLtXC74x15~,Ba55]-dBcmH>< _a5鋼,3Lx)B&¡VT*ܮy lR9*z?Z/ ˅n!OH!gӖk~5d732r ~erB\RJ>B|&dtM'7P&`! NpjB*8~vV[-p1 -*$-!.‹ $ VRn")" .2"4BJLWzw9b5=sf<3gfyfMlBw,8@dѦK -2 p*ȊT@N0Ypbh|&0[s*"W0@L jf#ҀlhLXaQ H-|N BYbѥݤ~?I$"]lSxPbt|dNߕ!*2M"F-=ؕ |, $2AOOO?ŻiQ{p;p2)n.[Z߮~C="q$0*{ B8Js?`1p&Fk+ib83WR6;s7GON0 +c!Feme- zQW􏵝G,n<?Z3Kp欩>L@s_iсn;w| `..]/^dAյQT/7 d4ITēOs I|)k1 * ݶi Gcl;E ~>SRgƛݲ=Wv>9+hlD` cP;Lea1ZFK}!,@2 0SG&>S[\Jk #yT/3r60#yv_bulmZ 0-X9J9Zz3'n MCnd3r"G1 Sxl N` =kVc:Q;z֕*^eȿb>\e3Wp9f85xC^"wB&-wL^a2Ƌ]8L ‹#3ԫ0jzA|Sv5{fl#50?C5 ̝[]Qe\*yBL_M3Z?YaK5 ;v\ga`e`eV~|S9 a-tkZ6omZ|M@/7T~%}2 ^L,]CV,M!M®hk#y~… ,ad|I&#6B.7 `.!N1O`:use7V=g{~ana4di)$43 "'OAWO~MoW3So n6",vP(lelQ9ye3 TsMjm><o ӊuy_ke Z_0, fnݺ&i|% (V,>aFF8P2*[Pod>kyhZ%m}O d^4sk)%Vc6l k'KY=Gya(F~8|nL@k#`FN!".|Nly#)iŎ3Ӈ`;ͥ`rRFpm! Q?B}B}o@aw x5/3E0@pkn.ؙ>m/`w=˱|Sf"͇c\(S1~0r߬Rk`;߬e-]4^{]O{4|Y>p ||62ۋî6Uvb /`&/x o>Oz6nѼj4w3䢲p=Ի uuu ;h9ݎ]Y Hj^ϝoܗjIn\/sgŬ~MC8kLdfyj9d`'IENDB`il32 HĸUͿx_RPXi{}æd'  %Lqz}U Һa!-0 //+#Cnri΢*640..//.--..#Zgd͘2/<=973..220/, "O^Uң(47:JLF>+ '453/ !LW9Uһ$'96ABRVQ>$"!0971 &&$NRbMN@EEU]XB%%#0=;1 )+ 1IGͦAabGFFR`]N,#;A>- ".,(">EȿeZe\MFGI_bXF32>FE<# )2-*$.=@ͮ)Ac`WWHGFO`^P65IMD, "34/,'$7;ĕXdYW^UEGCGQ?9B, !0940-)$15yW_VY^bUDA8/'! "#1>:52.*%+.aFQV[^_bXB2&""!%8>9:72.+%')T!EMXXhp`ZG5' &0:CAT\;63/+&%'R!ENW[dw^UR;:OMOcl;94/+'$$ZAKTTX`|lbCCbjy}dA5/2/*&$j=DRnbNO[pz~XX~yjP;3IS3,*'#{59KeH?BM]GG\L?9?[s3**&!&'78Nxw]JBA00ABK^vnA..(#M%(.6Xv|qiHHir|tT2*%&" p[N(+.:Rgu|SS|ufP8.*(Q^,xIrf@-,-1=?'!!'><1-,.Civ* }m7ghM<93 39=Ojf3UU;sW FluT()TviDiiO@XU7"/cc/"7UW= d_N$ <^^<$ UWO.%)"")$#9RD5%#&%''%(%LA<82+'&%$#" U@;5.)'$ ĸUͿw^PNWhz}åc#!Kqz}U Һa$'&% " Anri ΢#,(" !#Zgd ͘ &.,'"9 N^Uң"'%/87/% LW9Uһ1+37?>5'  NRaDB59:?@9( !/IGͦ9VT;:;=A;,>EȿdQXJ<;<:>;-  ,=@ͮ%8XQBA:<;86+  7;ĕOXHBD>9<7-$ 05xNRDCDC=86) *.`;BCDB;<;0#7 %*R9=D?R{S,,! 7J"'P9>DCK`)wMy`!$X 6<@:8@nvT56SpvT !i34@aL% ;`qvQQvpX-3F !z,*:S!:328Kyl ! "+'>pnI$x&Km|e0! KJn{teW98Xgu{kE  pTE?[mvNNulY<IX'xGl_4&%7bp' }m3a~y`@(K*Bc{|`/UUsV@f{}oLMp}zc=iiN8SQ/"__#/QQ5d_N5UU5 UWO-9RE4"  GA=70*%#""!  U@;5.*'$ ĸUͿyaTRZk{}Ŧf- "#,Nr}UҷV 0<@@AA@?:1% 1isiϠ=R5EGGFLOOLEDB=.Y?Vhd͜p?]VYL10JUMQ*jU^\ҤOql\_\S]],,MW9Uһ*;'_z]Pfi8:*NNdaIgn_os@C,4HGͧMumvyisWk4F5+?Eȿg(iyub{b>PG;01=@ͮ/Ntvxl숒vt}pF[J@5*8;Ė#ewsys䗌smPg\NC9-35z')ftr|ͫfsn]RF;0..c'VhtrvqlaTH=0+)V0Vewyݶo`VI>2)'T0VfwвmfWI>3)$\*Rdpq}Ѡ~`MSI<1(k#NXtvأu^VB=1&|GInyt|T<<.#-7OS빕uswIF6)P0C\ћU?,-( m53?Jj˜gJ>5> sZtFEIYzcayXIEH{?1i]ϒpj=gh>ksRUUsK甀끖{ikPs ȱm"__U+}ii-+UWRb5''5b! 9R?IX734 8O3L2 :;.++*(&-!&Ud;3.)'$Rl8mk[–[// ''33'' //[[–[[// ''33'' //[–[swami-2.2.0/package/rpmpkg.sh000077500000000000000000000023611361104770400160720ustar00rootroot00000000000000#!/bin/sh # Package a Swami tarball into RPM packages # Careful with this script!! It needs to be run as root to build RPMs. # In particular an "rm -rf $TMPDIR" is done RPMROOT=/usr/src/RPM PKGDIR=. TMPDIR=${PKGDIR}/packtmp if test ! -e "./$0" ; then echo "Run this script from within the package directory" exit 1 fi if test -z "$1" -o ! -e "$1" ; then echo "Usage: $0 swami-x.xx.x.tar.gz" exit 1 fi if test $LOGNAME != "root" ; then echo "Should be run as root to build RPMs" exit 1 fi SWAMIDIR=`basename $1 .tar.gz` # Unset LINGUAS so all languages will be built unset LINGUAS rm -rf ${TMPDIR} && \ mkdir ${TMPDIR} && \ tar -C ${TMPDIR} -xzf $1 && \ cat ${TMPDIR}/${SWAMIDIR}/swami.spec \ | sed 's|^\./configure.*$|& --disable-alsa-support --with-audiofile'\ ' --disable-awe-caching|' \ >${TMPDIR}/swami-binary-rpm.spec && \ mv ${TMPDIR}/${SWAMIDIR}/swami.spec ${TMPDIR}/swami-binary-rpm.spec \ ${RPMROOT}/SPECS/ && \ cp $1 ${RPMROOT}/SOURCES/ && \ rpm -bs ${RPMROOT}/SPECS/swami.spec && \ rpm -bb ${RPMROOT}/SPECS/swami-binary-rpm.spec && \ find ${RPMROOT}/RPMS/ -name "swami*.rpm" -exec mv {} ${PKGDIR} ';' && \ find ${RPMROOT}/SRPMS/ -name "swami*.rpm" -exec mv {} ${PKGDIR} ';' && \ rm -rf ${TMPDIR} exit 0 swami-2.2.0/package/win32/000077500000000000000000000000001361104770400151735ustar00rootroot00000000000000swami-2.2.0/package/win32/swami.nsi000066400000000000000000000332631361104770400170350ustar00rootroot00000000000000; NSIS2 Script ; Modified by Element Green ; Originally by Alexander Shaduri . ; Compatible with NSIS Unicode 2.45. ; Public Domain ; pass /DNO_GTK to makensis to disable inclusion of gtk. ; gtk installer name for embedding !define GTK_INSTALLER_EXE "gtk2-runtime-2.24.10-2012-10-10-ash.exe" !define PRODUCT_VERSION "2.1.0" !define PRODUCT_NAME "Swami" !define PRODUCT_NAME_SMALL "swami" !define PRODUCT_PUBLISHER "Element Green" !define PRODUCT_WEB_SITE "http://www.swamiproject.org" ;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\AppMainExe.exe" !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" !define PRODUCT_UNINST_ROOT_KEY "HKLM" !define REGISTRY_APP_PATHS "Software\Microsoft\Windows\CurrentVersion\App Paths" !include "FileFunc.nsh" ; GetOptions ; --------------- General Settings ; This is needed for proper start menu item manipulation (for all users) in vista RequestExecutionLevel admin ; This compressor gives us the best results SetCompressor /SOLID lzma ; Do a CRC check before installing CRCCheck On ; This is used in titles Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION} ; Output File Name !ifdef NO_GTK OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-nogtk.exe" !else OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}.exe" !endif ; The Default Installation Directory: ; Try to install to the same directory as runtime. InstallDir "$PROGRAMFILES\${PRODUCT_NAME}" ; If already installed, try here InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" ShowInstDetails show ShowUnInstDetails show ; --------------------- MUI INTERFACE ; MUI 2.0 compatible install !include "MUI2.nsh" !include "InstallOptions.nsh" ; section description macros ; Backgound Colors. uncomment to enable fullscreen. ; BGGradient 0000FF 000000 FFFFFF ; MUI Settings !define MUI_ABORTWARNING ; !define MUI_ICON "nsi_install.ico" ; !define MUI_UNICON "nsi_uninstall.ico" ; Things that need to be extracted on first (keep these lines before any File command!). ; Only useful for BZIP2 compression. ;!insertmacro MUI_RESERVEFILE_LANGDLL ;!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ; Language Selection Dialog Settings ;!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}" ;!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" ;!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language" !ifdef NO_GTK !define LICENSE_FILE "docs/distribution-nogtk.txt" !else !define LICENSE_FILE "docs/distribution.txt" !endif ; Pages to show during installation !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" !ifndef NO_GTK !define MUI_PAGE_CUSTOMFUNCTION_LEAVE on_components_page_leave !insertmacro MUI_PAGE_COMPONENTS !endif !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES ;!define MUI_FINISHPAGE_RUN "$INSTDIR\gsmartcontrol.exe" ;!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\Example.file" ;!define MUI_FINISHPAGE_RUN_NOTCHECKED !define MUI_FINISHPAGE_NOAUTOCLOSE !define MUI_FINISHPAGE_NOREBOOTSUPPORT !insertmacro MUI_PAGE_FINISH ; Uninstaller page !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ; Language files !insertmacro MUI_LANGUAGE "English" ; --------------- END MUI LangString TEXT_IO_TITLE ${LANG_ENGLISH} "Swami" var install_option_removeold ; uninstall the old version first (if present): yes (default), no. ; ----------------- INSTALLATION TYPES ; InstType "Recommended" ; InstType "Full" ; InstType "Minimal" Section "!Swami" SecMain SectionIn 1 RO SetShellVarContext all ; use all user variables as opposed to current user SetOutPath "$INSTDIR" SetOverwrite On File "bin\swami.exe" File "bin\swami.ico" File "swami-2.ui" File "bin\fluidsynth.exe" File "bin\libart_lgpl_2-2.dll" File "bin\libfluidsynth.dll" File "bin\libgnomecanvas-2-0.dll" File "bin\libinstpatch-1.0.dll" File "bin\libsndfile-1.dll" File "bin\libswami.dll" File "bin\libswamigui.dll" File /r images File /r plugins ; Include the "docs" directory completely, excluding the unused distribution text file. !ifdef NO_GTK File /r /x distribution.txt docs !else File /r /x distribution-nogtk.txt docs !endif ; Add Shortcuts (this inherits the exe's run permissions) CreateShortCut "$SMPROGRAMS\Swami.lnk" "$INSTDIR\swami.exe" "" \ "$INSTDIR\swami.ico" "" SW_SHOWNORMAL "" "Swami - Instrument editor for MIDI music composition" SectionEnd !ifndef NO_GTK ; The SecGtkPrivate and SecGtkPublic sections are mutually exclusive. Section "GTK+ (for this program only)" SecGtkPrivate SectionIn 1 SetShellVarContext all ; use all user variables as opposed to current user AddSize 12200 ; ~ size of unpacked gtk SetOutPath "$INSTDIR" File "${GTK_INSTALLER_EXE}" ; TODO: in the future, when we have translations for this program, ; make the GTK+ translations installation dependent on their installation status. ExecWait '"${GTK_INSTALLER_EXE}" /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S /D=$INSTDIR' Delete "$INSTDIR\${GTK_INSTALLER_EXE}" SectionEnd ; disabled by default Section /o "GTK+ (shared installation)" SecGtkPublic SectionIn 1 SetShellVarContext all ; use all user variables as opposed to current user AddSize 12200 ; ~ size of unpacked gtk SetOutPath "$INSTDIR" File "${GTK_INSTALLER_EXE}" ExecWait '"${GTK_INSTALLER_EXE}"' Delete "$INSTDIR\${GTK_INSTALLER_EXE}" SectionEnd !endif ; !NO_GTK !ifndef NO_GTK var gtk_mode ; "public", "private" or "none" var gtk_tmp ; temporary variable !endif ; Executed on installation start Function .onInit SetShellVarContext all ; use all user variables as opposed to current user ${GetOptions} "$CMDLINE" "/removeold=" $install_option_removeold Call PreventMultipleInstances Call DetectPrevInstallation ; ask for gtk only if it's a non-gtk installer. !ifdef NO_GTK Call AskForGtk !endif !ifndef NO_GTK StrCpy $gtk_mode "private" ; default !endif FunctionEnd function .onselchange !ifndef NO_GTK ; Remember which gtk section was selected. ; Deselect the other section. ; If it was private, we check if public is checked and uncheck private. StrCmp $gtk_mode "private" check_public ; old selection StrCmp $gtk_mode "public" check_private ; old selection goto check_exit check_public: SectionGetFlags ${SecGtkPublic} $gtk_tmp ; see if it's checked IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; unselect the other one IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF} SectionSetFlags ${SecGtkPrivate} $gtk_tmp goto check_exit check_private: SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; see if it's checked IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit SectionGetFlags ${SecGtkPublic} $gtk_tmp ; unselect the other one IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF} SectionSetFlags ${SecGtkPublic} $gtk_tmp check_exit: ; store the current mode StrCpy $gtk_mode "none" SectionGetFlags ${SecGtkPrivate} $gtk_tmp IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_private mode_end_private StrCpy $gtk_mode "private" mode_end_private: SectionGetFlags ${SecGtkPublic} $gtk_tmp IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_public mode_end_public StrCpy $gtk_mode "public" mode_end_public: ; MessageBox MB_ICONINFORMATION|MB_OK "gtk_mode: $gtk_mode" /SD IDOK !endif ; !NO_GTK functionend !ifndef NO_GTK Function on_components_page_leave StrCmp $gtk_mode "none" "" noabort Call AskForGtk noabort: FunctionEnd !endif ; !NO_GTK ; Section descriptions !ifndef NO_GTK ; this page is shown only when using gtk !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecMain} "Swami - Instrument editor for MIDI music composition" !insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPrivate} "GTK+ libraries, needed by Swami. \ This will install a private version of GTK+, usable only by Swami." !insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPublic} "GTK+ libraries, needed by Swami. \ This will install a system-wide version of GTK+, shareable with other programs." !insertmacro MUI_FUNCTION_DESCRIPTION_END !endif ; ------------------ POST INSTALL var gtk_dll_abs_path Section -post SetShellVarContext all ; use all user variables as opposed to current user ; Don't set any paths for this exe if it has a private GTK+ installation. !ifndef NO_GTK StrCmp $gtk_mode "private" skip_exe_PATH !endif ; set a special path for this exe, as GTK may not be in a global path. ReadRegStr $gtk_dll_abs_path HKLM "SOFTWARE\GTK\2.0" "DllPath" WriteRegStr HKLM "${REGISTRY_APP_PATHS}\swami.exe" "Path" "$gtk_dll_abs_path" !ifndef NO_GTK skip_exe_PATH: !endif !ifndef NO_GTK WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode" "$gtk_mode" !endif WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR" WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "${PRODUCT_PUBLISHER}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\swami_uninst.exe" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\swami.ico" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoModify" 1 WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoRepair" 1 ; write out uninstaller WriteUninstaller "$INSTDIR\swami_uninst.exe" SectionEnd ; post ; ---------------- UNINSTALL Function un.onUninstSuccess HideWindow MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." /SD IDOK FunctionEnd Section Uninstall SetShellVarContext all ; use all user variables as opposed to current user SetAutoClose false !ifndef NO_GTK ReadRegStr $gtk_mode HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode" StrCmp $gtk_mode "private" "" skip_gtk_remove ; remove private GTK+, specify the same custom options are during installation ExecWait "$INSTDIR\gtk2_runtime_uninst.exe /remove_config=yes /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S" ; _?=$INSTDIR ; Delete "$INSTDIR\gtk2_runtime_uninst.exe" ; If using _? flag, it won't get deleted automatically, do it manually. skip_gtk_remove: !endif ; add delete commands to delete whatever files/registry keys/etc you installed here. Delete "$INSTDIR\swami_uninst.exe" DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}" Delete "$INSTDIR\swami.exe" Delete "$INSTDIR\swami.ico" Delete "$INSTDIR\fluidsynth.exe" Delete "$INSTDIR\libfluidsynth.dll" Delete "$INSTDIR\libgnomecanvas-2-0.dll" Delete "$INSTDIR\libinstpatch-1.0.dll" Delete "$INSTDIR\libsndfile-1.dll" Delete "$INSTDIR\libswami.dll" Delete "$INSTDIR\libswamigui.dll" RMDir /r "$INSTDIR\images" RMDir /r "$INSTDIR\plugins" RMDir "$INSTDIR" ; only if empty !ifndef NO_GTK StrCmp $gtk_mode "private" skip_exe_PATH_remove !endif DeleteRegKey HKLM "${REGISTRY_APP_PATHS}\swami.exe" !ifndef NO_GTK skip_exe_PATH_remove: !endif Delete "$SMPROGRAMS\Swami.lnk" SectionEnd ; end of uninstall section ; --------------- Helpers ; Detect previous installation Function DetectPrevInstallation ; if /removeold=no option is given, don't check anything. StrCmp $install_option_removeold "no" old_detect_done SetShellVarContext all ; use all user variables as opposed to current user push $R0 ; detect previous installation ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" StrCmp $R0 "" old_detect_done MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ "${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \ previous version or `Cancel` to continue anyway." \ /SD IDOK IDOK old_uninst ; Abort goto old_detect_done ; Run the old uninstaller old_uninst: ClearErrors IfSilent old_silent_uninst old_nosilent_uninst old_nosilent_uninst: ExecWait '$R0' goto old_uninst_continue old_silent_uninst: ExecWait '$R0 /S _?=$INSTDIR' old_uninst_continue: IfErrors old_no_remove_uninstaller ; You can either use Delete /REBOOTOK in the uninstaller or add some code ; here to remove to remove the uninstaller. Use a registry key to check ; whether the user has chosen to uninstall. If you are using an uninstaller ; components page, make sure all sections are uninstalled. old_no_remove_uninstaller: old_detect_done: ; old installation not found, all ok pop $R0 FunctionEnd ; detect GTK installation (any of available versions) Function AskForGtk SetShellVarContext all ; use all user variables as opposed to current user push $R0 ReadRegStr $R0 HKLM "SOFTWARE\GTK\2.0" "DllPath" StrCmp $R0 "" no_gtk have_gtk no_gtk: MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ "GTK2-Runtime is not installed. This product needs it to function properly.$\n\ Please install GTK2-Runtime from http://gtk-win.sf.net/ first.$\n$\n\ Click 'Cancel' to abort the installation \ or 'OK' to continue anyway." \ /SD IDOK IDOK have_gtk ;Abort ; Abort has different meaning from onpage callbacks, so use Quit Quit goto end_gtk_check have_gtk: ; do nothing end_gtk_check: pop $R0 FunctionEnd ; Prevent running multiple instances of the installer Function PreventMultipleInstances Push $R0 System::Call 'kernel32::CreateMutexA(i 0, i 0, t ${PRODUCT_NAME}) ?e' Pop $R0 StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." /SD IDOK Abort Pop $R0 FunctionEnd ; eof swami-2.2.0/po/000077500000000000000000000000001361104770400132545ustar00rootroot00000000000000swami-2.2.0/po/ChangeLog000066400000000000000000000010421361104770400150230ustar00rootroot000000000000002005-10-15 gettextize * Makefile.in.in: Upgrade to gettext-0.12.1. 2003-06-24 gettextize * Makefile.in.in: Upgrade to gettext-0.11.5. * boldquot.sed: New file, from gettext-0.11.5. * en@boldquot.header: New file, from gettext-0.11.5. * en@quot.header: New file, from gettext-0.11.5. * insert-header.sin: New file, from gettext-0.11.5. * quot.sed: New file, from gettext-0.11.5. * remove-potcdate.sin: New file, from gettext-0.11.5. * Rules-quot: New file, from gettext-0.11.5. swami-2.2.0/po/Makefile.in.in000066400000000000000000000153771361104770400157430ustar00rootroot00000000000000# Makefile for program source directory in GNU NLS utilities package. # Copyright (C) 1995, 1996, 1997 by Ulrich Drepper # Copyright (C) 2004-2008 Rodney Dawes # # This file may be copied and used freely without restrictions. It may # be used in projects which are not available under a GNU Public License, # but which still want to provide support for the GNU gettext functionality. # # - Modified by Owen Taylor to use GETTEXT_PACKAGE # instead of PACKAGE and to look for po2tbl in ./ not in intl/ # # - Modified by jacob berkman to install # Makefile.in.in and po2tbl.sed.in for use with glib-gettextize # # - Modified by Rodney Dawes for use with intltool # # We have the following line for use by intltoolize: # INTLTOOL_MAKEFILE GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ PACKAGE = @PACKAGE@ VERSION = @VERSION@ SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ top_builddir = @top_builddir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ datadir = @datadir@ datarootdir = @datarootdir@ libdir = @libdir@ DATADIRNAME = @DATADIRNAME@ itlocaledir = $(prefix)/$(DATADIRNAME)/locale subdir = po install_sh = @install_sh@ # Automake >= 1.8 provides @mkdir_p@. # Until it can be supposed, use the safe fallback: mkdir_p = $(install_sh) -d INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ GMSGFMT = @GMSGFMT@ MSGFMT = @MSGFMT@ XGETTEXT = @XGETTEXT@ INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot ALL_LINGUAS = @ALL_LINGUAS@ PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; else echo "$(ALL_LINGUAS)"; fi) USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep \^$$lang$$ $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep \^$$lang$$`"; then printf "$$lang "; fi; done; fi) USE_LINGUAS=$(shell if test -n "$(USER_LINGUAS)" -o -n "$(LINGUAS)"; then LLINGUAS="$(USER_LINGUAS)"; else if test -n "$(PO_LINGUAS)"; then LLINGUAS="$(PO_LINGUAS)"; else LLINGUAS="$(ALL_LINGUAS)"; fi; fi; for lang in $$LLINGUAS; do printf "$$lang "; done) POFILES=$(shell LINGUAS="$(PO_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.po "; done) DISTFILES = Makefile.in.in POTFILES.in $(POFILES) EXTRA_DISTFILES = ChangeLog POTFILES.skip Makevars LINGUAS POTFILES = \ # This comment gets stripped out CATALOGS=$(shell LINGUAS="$(USE_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.gmo "; done) .SUFFIXES: .SUFFIXES: .po .pox .gmo .mo .msg .cat .po.pox: $(MAKE) $(GETTEXT_PACKAGE).pot $(MSGMERGE) $< $(GETTEXT_PACKAGE).pot -o $*.pox .po.mo: $(MSGFMT) -o $@ $< .po.gmo: file=`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: $(GETTEXT_PACKAGE).pot: $(POTFILES) $(GENPOT) install: install-data install-data: install-data-@USE_NLS@ install-data-no: all install-data-yes: all linguas="$(USE_LINGUAS)"; \ for lang in $$linguas; do \ dir=$(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES; \ $(mkdir_p) $$dir; \ if test -r $$lang.gmo; then \ $(INSTALL_DATA) $$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ echo "installing $$lang.gmo as $$dir/$(GETTEXT_PACKAGE).mo"; \ else \ $(INSTALL_DATA) $(srcdir)/$$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ echo "installing $(srcdir)/$$lang.gmo as" \ "$$dir/$(GETTEXT_PACKAGE).mo"; \ fi; \ if test -r $$lang.gmo.m; then \ $(INSTALL_DATA) $$lang.gmo.m $$dir/$(GETTEXT_PACKAGE).mo.m; \ echo "installing $$lang.gmo.m as $$dir/$(GETTEXT_PACKAGE).mo.m"; \ else \ if test -r $(srcdir)/$$lang.gmo.m ; then \ $(INSTALL_DATA) $(srcdir)/$$lang.gmo.m \ $$dir/$(GETTEXT_PACKAGE).mo.m; \ echo "installing $(srcdir)/$$lang.gmo.m as" \ "$$dir/$(GETTEXT_PACKAGE).mo.m"; \ else \ true; \ fi; \ fi; \ done # Empty stubs to satisfy archaic automake needs dvi info ctags tags CTAGS TAGS ID: # Define this as empty until I found a useful application. install-exec installcheck: uninstall: linguas="$(USE_LINGUAS)"; \ for lang in $$linguas; do \ rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \ rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo.m; \ done check: all $(GETTEXT_PACKAGE).pot rm -f missing notexist srcdir=$(srcdir) $(INTLTOOL_UPDATE) -m if [ -r missing -o -r notexist ]; then \ exit 1; \ fi mostlyclean: rm -f *.pox $(GETTEXT_PACKAGE).pot *.old.po cat-id-tbl.tmp rm -f .intltool-merge-cache clean: mostlyclean distclean: clean rm -f Makefile Makefile.in POTFILES stamp-it rm -f *.mo *.msg *.cat *.cat.m *.gmo 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 Makefile.in.in distdir = ../$(PACKAGE)-$(VERSION)/$(subdir) dist distdir: $(DISTFILES) dists="$(DISTFILES)"; \ extra_dists="$(EXTRA_DISTFILES)"; \ for file in $$extra_dists; do \ test -f $(srcdir)/$$file && dists="$$dists $(srcdir)/$$file"; \ done; \ for file in $$dists; do \ test -f $$file || file="$(srcdir)/$$file"; \ ln $$file $(distdir) 2> /dev/null \ || cp -p $$file $(distdir); \ done update-po: Makefile $(MAKE) $(GETTEXT_PACKAGE).pot tmpdir=`pwd`; \ linguas="$(USE_LINGUAS)"; \ for lang in $$linguas; do \ echo "$$lang:"; \ result="`$(MSGMERGE) -o $$tmpdir/$$lang.new.po $$lang`"; \ if $$result; then \ if cmp $(srcdir)/$$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ rm -f $$tmpdir/$$lang.new.po; \ else \ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ :; \ else \ echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ rm -f $$tmpdir/$$lang.new.po; \ exit 1; \ fi; \ fi; \ else \ echo "msgmerge for $$lang.gmo failed!"; \ rm -f $$tmpdir/$$lang.new.po; \ fi; \ done Makefile POTFILES: stamp-it @if test ! -f $@; then \ rm -f stamp-it; \ $(MAKE) stamp-it; \ fi stamp-it: Makefile.in.in $(top_builddir)/config.status POTFILES.in cd $(top_builddir) \ && CONFIG_FILES=$(subdir)/Makefile.in CONFIG_HEADERS= CONFIG_LINKS= \ $(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: swami-2.2.0/po/Makevars000066400000000000000000000035031361104770400147510ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = $(PACKAGE) # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = Josh Green # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = Josh Green # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = swami-2.2.0/po/POTFILES.in000066400000000000000000000012761361104770400150370ustar00rootroot00000000000000# List of source files containing translatable strings. # Each source file that has gettext translatable strings should be listed here src/libswami/SwamiObject.c src/libswami/SwamiPlugin.c src/libswami/SwamiRoot.c src/libswami/SwamiWavetbl.c src/swamigui/glade_strings.c src/swamigui/help.c src/swamigui/main.c src/swamigui/patch_funcs.c src/swamigui/SwamiguiItemMenu.c src/swamigui/SwamiguiMenu.c src/swamigui/SwamiguiModEdit.c src/swamigui/SwamiguiMultiList.c src/swamigui/SwamiguiPaste.c src/swamigui/SwamiguiPiano.c src/swamigui/SwamiguiProp.c src/swamigui/SwamiguiRoot.c src/swamigui/SwamiguiSampleCanvas.c src/swamigui/SwamiguiSampleEditor.c src/swamigui/util.c src/swamigui/widgets/icon-combo.c swami-2.2.0/po/Rules-quot000066400000000000000000000032311361104770400152560ustar00rootroot00000000000000# Special Makefile rules for English message catalogs with quotation marks. DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot .SUFFIXES: .insert-header .po-update-en en@quot.po-update: en@quot.po-update-en en@boldquot.po-update: en@boldquot.po-update-en .insert-header.po-update-en: @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ tmpdir=`pwd`; \ echo "$$lang:"; \ ll=`echo $$lang | sed -e 's/@.*//'`; \ LC_ALL=C; export LC_ALL; \ cd $(srcdir); \ if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ rm -f $$tmpdir/$$lang.new.po; \ else \ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ :; \ else \ echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ exit 1; \ fi; \ fi; \ else \ echo "creation of $$lang.po failed!" 1>&2; \ rm -f $$tmpdir/$$lang.new.po; \ fi en@quot.insert-header: insert-header.sin sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header en@boldquot.insert-header: insert-header.sin sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header mostlyclean: mostlyclean-quot mostlyclean-quot: rm -f *.insert-header swami-2.2.0/po/boldquot.sed000066400000000000000000000003311361104770400155770ustar00rootroot00000000000000s/"\([^"]*\)"/“\1”/g s/`\([^`']*\)'/‘\1’/g s/ '\([^`']*\)' / ‘\1’ /g s/ '\([^`']*\)'$/ ‘\1’/g s/^'\([^`']*\)' /‘\1’ /g s/“”/""/g s/“/“/g s/”/”/g s/‘/‘/g s/’/’/g swami-2.2.0/po/en@boldquot.header000066400000000000000000000024711361104770400167060ustar00rootroot00000000000000# All this catalog "translates" are quotation characters. # The msgids must be ASCII and therefore cannot contain real quotation # characters, only substitutes like grave accent (0x60), apostrophe (0x27) # and double quote (0x22). These substitutes look strange; see # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html # # This catalog translates grave accent (0x60) and apostrophe (0x27) to # left single quotation mark (U+2018) and right single quotation mark (U+2019). # It also translates pairs of apostrophe (0x27) to # left single quotation mark (U+2018) and right single quotation mark (U+2019) # and pairs of quotation mark (0x22) to # left double quotation mark (U+201C) and right double quotation mark (U+201D). # # When output to an UTF-8 terminal, the quotation characters appear perfectly. # When output to an ISO-8859-1 terminal, the single quotation marks are # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to # grave/acute accent (by libiconv), and the double quotation marks are # transliterated to 0x22. # When output to an ASCII terminal, the single quotation marks are # transliterated to apostrophes, and the double quotation marks are # transliterated to 0x22. # # This catalog furthermore displays the text between the quotation marks in # bold face, assuming the VT100/XTerm escape sequences. # swami-2.2.0/po/en@quot.header000066400000000000000000000022631361104770400160440ustar00rootroot00000000000000# All this catalog "translates" are quotation characters. # The msgids must be ASCII and therefore cannot contain real quotation # characters, only substitutes like grave accent (0x60), apostrophe (0x27) # and double quote (0x22). These substitutes look strange; see # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html # # This catalog translates grave accent (0x60) and apostrophe (0x27) to # left single quotation mark (U+2018) and right single quotation mark (U+2019). # It also translates pairs of apostrophe (0x27) to # left single quotation mark (U+2018) and right single quotation mark (U+2019) # and pairs of quotation mark (0x22) to # left double quotation mark (U+201C) and right double quotation mark (U+201D). # # When output to an UTF-8 terminal, the quotation characters appear perfectly. # When output to an ISO-8859-1 terminal, the single quotation marks are # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to # grave/acute accent (by libiconv), and the double quotation marks are # transliterated to 0x22. # When output to an ASCII terminal, the single quotation marks are # transliterated to apostrophes, and the double quotation marks are # transliterated to 0x22. # swami-2.2.0/po/insert-header.sin000066400000000000000000000012401361104770400165160ustar00rootroot00000000000000# Sed script that inserts the file called HEADER before the header entry. # # At each occurrence of a line starting with "msgid ", we execute the following # commands. At the first occurrence, insert the file. At the following # occurrences, do nothing. The distinction between the first and the following # occurrences is achieved by looking at the hold space. /^msgid /{ x # Test if the hold space is empty. s/m/m/ ta # Yes it was empty. First occurrence. Read the file. r HEADER # Output the file's contents by reading the next line. But don't lose the # current line while doing this. g N bb :a # The hold space was nonempty. Following occurrences. Do nothing. x :b } swami-2.2.0/po/quot.sed000066400000000000000000000002311361104770400147350ustar00rootroot00000000000000s/"\([^"]*\)"/“\1”/g s/`\([^`']*\)'/‘\1’/g s/ '\([^`']*\)' / ‘\1’ /g s/ '\([^`']*\)'$/ ‘\1’/g s/^'\([^`']*\)' /‘\1’ /g s/“”/""/g swami-2.2.0/po/remove-potcdate.sin000066400000000000000000000006601361104770400170670ustar00rootroot00000000000000# Sed script that remove the POT-Creation-Date line in the header entry # from a POT file. # # The distinction between the first and the following occurrences of the # pattern is achieved by looking at the hold space. /^"POT-Creation-Date: .*"$/{ x # Test if the hold space is empty. s/P/P/ ta # Yes it was empty. First occurrence. Remove the line. g d bb :a # The hold space was nonempty. Following occurrences. Do nothing. x :b } swami-2.2.0/py-compile000077500000000000000000000047451361104770400146540ustar00rootroot00000000000000#!/bin/sh # py-compile - Compile a Python program # Copyright 2000, 2001 Free Software Foundation, 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, 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. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # called as "py-compile [--basedir DIR] PY_FILES ... if [ -z "$PYTHON" ]; then PYTHON=python fi basedir= case "$1" in --basedir) basedir=$2 shift 2 ;; --help) echo "Usage: py-compile [--basedir DIR] PY_FILES ..." echo "Byte compile some python scripts. This should be performed" echo "after they have been moved to the final installation location" exit 0 ;; --version) echo "py-compile version 0.0" exit 0 ;; esac if [ $# = 0 ]; then echo "No files given to $0" 1>&2 exit 1 fi # if basedir was given, then it should be prepended to filenames before # byte compilation. if [ -z "$basedir" ]; then trans="path = file" else trans="path = os.path.join('$basedir', file)" fi $PYTHON -c " import sys, os, string, py_compile files = '''$*''' print 'Byte-compiling python modules...' for file in string.split(files): $trans if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): continue print file, sys.stdout.flush() py_compile.compile(path) print" || exit $? # this will fail for python < 1.5, but that doesn't matter ... $PYTHON -O -c " import sys, os, string, py_compile files = '''$*''' print 'Byte-compiling python modules (optimised versions) ...' for file in string.split(files): $trans if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): continue print file, sys.stdout.flush() py_compile.compile(path) print" 2>/dev/null || : swami-2.2.0/src/000077500000000000000000000000001361104770400134255ustar00rootroot00000000000000swami-2.2.0/src/CMakeLists.txt000066400000000000000000000003771361104770400161740ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # # Process subdirectories add_subdirectory ( libswami ) add_subdirectory ( swamigui ) add_subdirectory ( plugins ) swami-2.2.0/src/libswami/000077500000000000000000000000001361104770400152345ustar00rootroot00000000000000swami-2.2.0/src/libswami/CMakeLists.txt000066400000000000000000000150671361104770400200050ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # include_directories ( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${GOBJECT_INCLUDEDIR} ${GOBJECT_INCLUDE_DIRS} ${LIBINSTPATCH_INCLUDEDIR} ${LIBINSTPATCH_INCLUDE_DIRS} ) #adding installed dirent support for Windows if ( WIN32 ) #let the user choosing additionnal search path set (DIRENT_INCLUDE_PATH "" CACHE FILEPATH "Path of dirent.h" ) set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${DIRENT_INCLUDE_PATH}) unset (HAVE_DIRENT_H CACHE) check_include_file ( dirent.h HAVE_DIRENT_H ) if ( HAVE_DIRENT_H ) include_directories ( ${CMAKE_REQUIRED_INCLUDES} ) endif( HAVE_DIRENT_H ) endif( WIN32 ) # ************ library ************ set ( libswami_public_HEADERS builtin_enums.h SwamiContainer.h SwamiControl.h SwamiControlEvent.h SwamiControlFunc.h SwamiControlHub.h SwamiControlMidi.h SwamiControlProp.h SwamiControlQueue.h SwamiControlValue.h SwamiEvent_ipatch.h SwamiLock.h SwamiLog.h SwamiLoopFinder.h SwamiLoopResults.h SwamiMidiDevice.h SwamiMidiEvent.h SwamiObject.h SwamiParam.h SwamiPlugin.h SwamiPropTree.h SwamiRoot.h SwamiWavetbl.h util.h ${CMAKE_CURRENT_BINARY_DIR}/version.h ) set ( libswami_SOURCES builtin_enums.c SwamiContainer.c SwamiControl.c SwamiControlEvent.c SwamiControlFunc.c SwamiControlHub.c SwamiControlMidi.c SwamiControlProp.c SwamiControlQueue.c SwamiControlValue.c SwamiEvent_ipatch.c SwamiLock.c SwamiLog.c SwamiLoopFinder.c SwamiLoopResults.c SwamiMidiDevice.c SwamiMidiEvent.c SwamiObject.c SwamiParam.c SwamiPlugin.c SwamiPropTree.c SwamiRoot.c SwamiWavetbl.c libswami.c util.c value_transform.c ) set ( public_main_HEADER libswami.h ) link_directories ( ${GOBJECT_LIBDIR} ${GOBJECT_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ) add_definitions ( -DLOCALEDIR=\"${CMAKE_INSTALL_LOCALEDIR}\" -DG_LOG_DOMAIN=\"libswami\" ) set (DEFINITION_FILE "") # Options for Windows only if( MSVC ) # disable deprecation warnings add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) if (BUILD_SHARED_LIBS) # adding a module definition file for shared libs will export symbols and produce import library set (DEFINITION_FILE libswami.def) endif(BUILD_SHARED_LIBS) endif( MSVC ) add_library ( libswami ${CMAKE_CURRENT_BINARY_DIR}/marshals.c ${libswami_SOURCES} ${DEFINITION_FILE} ) # Version file configure_file ( ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h ) install ( FILES ${libswami_public_HEADERS} ${public_main_HEADER} DESTINATION include/swami/libswami ) find_program (GLIB2_MKENUMS glib-mkenums) add_custom_target (libswami-enums COMMAND ${GLIB2_MKENUMS} --fhead \"\#ifndef __SWAMI_BUILTIN_ENUMS_H__\\n\" --fhead \"\#define __SWAMI_BUILTIN_ENUMS_H__\\n\\n\" --fhead \"\#include \\n\\n\" --fhead \"G_BEGIN_DECLS\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\\n\" --vhead \"GType @enum_name@_get_type \(void\)\;\\n\" --vhead \"\#define SWAMI_TYPE_@ENUMSHORT@ \(@enum_name@_get_type\(\)\)\\n\" --ftail \"G_END_DECLS\\n\\n\" --ftail \"\#endif /* __SWAMI_BUILTIN_ENUMS_H__ */\" ${libswami_public_HEADERS} > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.h COMMAND ${GLIB2_MKENUMS} --fhead \"\#include \\"libswami.h\\"\\n\" --fhead \"\#include \\"swami_priv.h\\"\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\" --vhead \"static const G@Type@Value _@enum_name@_values[] = {\" --vprod \" { @VALUENAME@, \\"@VALUENAME@\\", \\"@valuenick@\\" },\" --vtail \" { 0, NULL, NULL }\\n}\;\\n\\n\" --vtail \"GType\\n@enum_name@_get_type \(void\)\\n{\\n\" --vtail \" static GType type = 0\;\\n\\n\" --vtail \" if \(G_UNLIKELY \(type == 0\)\)\\n\" --vtail \" type = g_\@type\@_register_static \(\\"@EnumName@\\", _@enum_name@_values\)\;\\n\\n\" --vtail \" return type\;\\n}\\n\\n\" ${libswami_public_HEADERS} > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${libswami_public_HEADERS} ) find_program (GLIB2_GENMARSHAL glib-genmarshal) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.c COMMAND ${GLIB2_GENMARSHAL} ARGS --body --prefix=swami_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list >${CMAKE_CURRENT_BINARY_DIR}/marshals.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ${CMAKE_CURRENT_BINARY_DIR}/marshals.h ) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.h COMMAND ${GLIB2_GENMARSHAL} ARGS --header --prefix=swami_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list >${CMAKE_CURRENT_BINARY_DIR}/marshals.h WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ) target_link_libraries ( libswami ${GOBJECT_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ${COREFOUNDATION} ) if (APPLE) set_property ( TARGET libswami APPEND PROPERTY INCLUDE_DIRECTORIES "${COREFOUNDATION}/Headers" ) endif (APPLE) if ( MACOSX_FRAMEWORK ) set_property ( SOURCE ${libswami_public_HEADERS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/libswami ) set_target_properties ( libswami PROPERTIES OUTPUT_NAME "libswami" FRAMEWORK TRUE PUBLIC_HEADER "${public_main_HEADER}" FRAMEWORK_VERSION "${LIB_VERSION_CURRENT}" INSTALL_NAME_DIR ${FRAMEWORK_INSTALL_DIR} VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif( MINGW OR WIN32 ) set_target_properties ( libswami PROPERTIES PREFIX "" OUTPUT_NAME "libswami" VERSION ${LIB_VERSION_INFO} ) else ( MINGW OR WIN32 ) set_target_properties ( libswami PROPERTIES PREFIX "lib" OUTPUT_NAME "swami" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) endif ( MACOSX_FRAMEWORK ) install ( TARGETS libswami RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} BUNDLE DESTINATION ${BUNDLE_INSTALL_DIR} ) swami-2.2.0/src/libswami/SwamiContainer.c000066400000000000000000000054661361104770400203360ustar00rootroot00000000000000/* * SwamiContainer.c - Root container for instrument patches * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "SwamiContainer.h" static void swami_container_class_init (SwamiContainerClass *klass); static const GType *swami_container_child_types (void); static gboolean swami_container_init_iter (IpatchContainer *container, IpatchIter *iter, GType type); static GType container_child_types[2] = { 0 }; GType swami_container_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (SwamiContainerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) swami_container_class_init, NULL, NULL, sizeof (SwamiContainer), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static (IPATCH_TYPE_CONTAINER, "SwamiContainer", &item_info, 0); } return (item_type); } static void swami_container_class_init (SwamiContainerClass *klass) { IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS (klass); container_class->child_types = swami_container_child_types; container_class->init_iter = swami_container_init_iter; container_child_types[0] = IPATCH_TYPE_BASE; } static const GType * swami_container_child_types (void) { return (container_child_types); } /* container is locked by caller */ static gboolean swami_container_init_iter (IpatchContainer *container, IpatchIter *iter, GType type) { SwamiContainer *scontainer = SWAMI_CONTAINER (container); if (type != IPATCH_TYPE_BASE) { g_critical ("Invalid child type '%s' for parent of type '%s'", g_type_name (type), g_type_name (G_OBJECT_TYPE (container))); return (FALSE); } ipatch_iter_GSList_init (iter, &scontainer->patch_list); return (TRUE); } /** * swami_container_new: * * Create a new Swami container object which is a toplevel container for * instrument patches. * * Returns: New Swami container object */ SwamiContainer * swami_container_new (void) { return SWAMI_CONTAINER (g_object_new (SWAMI_TYPE_CONTAINER, NULL)); } swami-2.2.0/src/libswami/SwamiContainer.h000066400000000000000000000037161361104770400203370ustar00rootroot00000000000000/* * SwamiContainer.h - Root container for instrument patches * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTAINER_H__ #define __SWAMI_CONTAINER_H__ #include #include #include typedef struct _SwamiContainer SwamiContainer; typedef struct _SwamiContainerClass SwamiContainerClass; #include #define SWAMI_TYPE_CONTAINER (swami_container_get_type ()) #define SWAMI_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTAINER, SwamiContainer)) #define SWAMI_CONTAINER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTAINER, SwamiContainerClass)) #define SWAMI_IS_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTAINER)) #define SWAMI_IS_CONTAINER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTAINER)) struct _SwamiContainer { IpatchContainer parent; /*< private >*/ GSList *patch_list; /* list of instrument patches */ SwamiRoot *root; /* root object owning this container */ }; struct _SwamiContainerClass { IpatchContainerClass parent_class; }; GType swami_container_get_type (void); SwamiContainer *swami_container_new (void); #endif swami-2.2.0/src/libswami/SwamiControl.c000066400000000000000000001641571361104770400200370ustar00rootroot00000000000000/* * SwamiControl.c - Swami control base object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiControl.h" #include "SwamiControlEvent.h" #include "SwamiControlQueue.h" #include "SwamiControlFunc.h" #include "SwamiControlProp.h" #include "SwamiLog.h" #include "SwamiParam.h" #include "swami_priv.h" #include "util.h" #include "marshals.h" #include "config.h" /* work around for bug in G_BREAKPOINT where SIGTRAP symbol is used on * some architectures without including signal.h header. */ #if DEBUG #include #endif /* max number of destination connections per control (for mem optimizations) */ #define MAX_DEST_CONNECTIONS 64 enum { CONNECT_SIGNAL, DISCONNECT_SIGNAL, SPEC_CHANGED_SIGNAL, SIGNAL_COUNT }; /* a structure defining an endpoint of a connection */ typedef struct _SwamiControlConn { guint flags; /* SwamiControlConnPriority | SwamiControlConnFlags */ SwamiControl *control; /* connection control */ /* for src -> dest connections only */ SwamiValueTransform trans; /* transform func */ GDestroyNotify destroy; /* function to call when connection is destroyed */ gpointer data; /* user data to pass to transform function */ } SwamiControlConn; #define swami_control_conn_new() g_slice_new0 (SwamiControlConn) #define swami_control_conn_free(conn) g_slice_free (SwamiControlConn, conn) /* bag used for transmitting values to destination controls */ typedef struct { SwamiControl *control; SwamiValueTransform trans; gpointer data; } CtrlUpdateBag; static void swami_control_class_init (SwamiControlClass *klass); static void swami_control_init (SwamiControl *control); static void swami_control_finalize (GObject *object); static void swami_control_connect_real (SwamiControl *src, SwamiControl *dest, SwamiValueTransform trans, gpointer data, GDestroyNotify destroy, guint flags); static gint GCompare_func_conn_priority (gconstpointer a, gconstpointer b); static void item_prop_value_transform (const GValue *src, GValue *dest, gpointer data); static void swami_control_real_disconnect (SwamiControl *c1, SwamiControl *c2, guint flags); static inline void swami_control_set_event_real (SwamiControl *control, SwamiControlEvent *event); static inline gboolean swami_control_loop_check (SwamiControl *control, SwamiControlEvent *event); /* a master list of all controls, used for doing periodic inactive event expiration cleanup */ G_LOCK_DEFINE_STATIC (control_list); static GList *control_list = NULL; static GObjectClass *parent_class = NULL; static guint control_signals[SIGNAL_COUNT] = { 0 }; /* debug flag for enabling display of control operations */ #if DEBUG gboolean swami_control_debug = FALSE; SwamiControl *swami_control_break = NULL; #define SWAMI_CONTROL_TEST_BREAK(a, b) \ if (swami_control_break && (a == swami_control_break || b == swami_control_break)) \ G_BREAKPOINT () /* generate a descriptive control description string, must be freed when finished */ static char * pretty_control (SwamiControl *ctrl) { char *s; if (!ctrl) return (g_strdup ("")); if (SWAMI_IS_CONTROL_FUNC (ctrl)) { SwamiControlFunc *fn = SWAMI_CONTROL_FUNC (ctrl); s = g_strdup_printf ("<%s>%p (get=%p, set=%p)", G_OBJECT_TYPE_NAME (fn), fn, fn->get_func, fn->set_func); } else if (SWAMI_IS_CONTROL_PROP (ctrl)) { SwamiControlProp *pc = SWAMI_CONTROL_PROP (ctrl); s = g_strdup_printf ("<%s>%p (object=<%s>%p, property='%s')", G_OBJECT_TYPE_NAME (pc), pc, pc->object ? G_OBJECT_TYPE_NAME (pc->object) : "", pc->object, pc->spec ? pc->spec->name : ""); } else s = g_strdup_printf ("<%s>%p", G_OBJECT_TYPE_NAME (ctrl), ctrl); return (s); } #endif GType swami_control_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiControlClass), NULL, NULL, (GClassInitFunc) swami_control_class_init, NULL, NULL, sizeof (SwamiControl), 0, (GInstanceInitFunc) swami_control_init }; obj_type = g_type_register_static (SWAMI_TYPE_LOCK, "SwamiControl", &obj_info, 0); } return (obj_type); } static void swami_control_class_init (SwamiControlClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_control_finalize; klass->connect = NULL; klass->disconnect = NULL; control_signals[CONNECT_SIGNAL] = g_signal_new ("connect", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (SwamiControlClass, connect), NULL, NULL, swami_marshal_VOID__OBJECT_UINT, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); control_signals[DISCONNECT_SIGNAL] = g_signal_new ("disconnect", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwamiControlClass, disconnect), NULL, NULL, swami_marshal_VOID__OBJECT_UINT, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); control_signals[SPEC_CHANGED_SIGNAL] = g_signal_new ("spec-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__PARAM, G_TYPE_NONE, 1, G_TYPE_PARAM); } /* creating an instance of SwamiControl creates a send only event control */ static void swami_control_init (SwamiControl *control) { control->flags = SWAMI_CONTROL_SENDS; /* prepend control to master list */ G_LOCK (control_list); control_list = g_list_prepend (control_list, control); G_UNLOCK (control_list); } static void swami_control_finalize (GObject *object) { SwamiControl *control = SWAMI_CONTROL (object); swami_control_disconnect_all (control); G_LOCK (control_list); control_list = g_list_remove (control_list, object); G_UNLOCK (control_list); } /** * swami_control_new: * * Create a new #SwamiControl instance. #SwamiControl is the base class for * other control types as well. Creating an instance of a #SwamiControl * will create a send only event control. * * Returns: New #SwamiControl object, the caller owns a reference. */ SwamiControl * swami_control_new (void) { return (SWAMI_CONTROL (g_object_new (SWAMI_TYPE_CONTROL, NULL))); } /** * swami_control_connect: * @src: Source control to connect (readable) * @dest: Destination control to connect (writable) * @flags: Flags for this connection (#SwamiControlConnFlags) * * Connect two controls (i.e., when the @src control's value changes * the @dest control is set to this value). Useful flags include the * #SWAMI_CONTROL_CONN_INIT flag which will cause @dest to be set to * the current value of @src and #SWAMI_CONTROL_CONN_BIDIR which will * cause a bi-directional connection to be made (as if 2 calls where * made to this function with the @src and @dest swapped the second * time). The connection priority can also be set via the flags field * by or-ing in #SwamiControlConnPriority values (assumes default * priority if not specified). The priority determines the order in * which connections are processed. The #SWAMI_CONTROL_CONN_SPEC flag will * cause the parameter spec of the @dest control to be set to that of @src. */ void swami_control_connect (SwamiControl *src, SwamiControl *dest, guint flags) { swami_control_connect_transform (src, dest, flags, NULL, NULL, NULL, NULL, NULL, NULL); } /** * swami_control_connect_transform: * @src: Source control to connect (readable) * @dest: Destination control to connect (writable) * @flags: Flags for this connection (#SwamiControlConnFlags). * @trans1: Value transform function from @src to @dest (or %NULL for no transform) * @trans2: Value transform function from @dest to @src * (#SWAMI_CONTROL_CONN_BIDIR only, %NULL for no transform). * @data1: User data to pass to @trans1 function. * @data2: User data to pass to @trans2 function. * (#SWAMI_CONTROL_CONN_BIDIR only, %NULL for no transform). * @destroy1: Optional callback to free @data1 when @trans1 is disconnected. * @destroy2: Optional callback to free @data2 when @trans2 is disconnected. * * Like swami_control_connect() but transform functions can be specified * during connect, rather than having to call swami_control_set_transform() * later. */ void swami_control_connect_transform (SwamiControl *src, SwamiControl *dest, guint flags, SwamiValueTransform trans1, SwamiValueTransform trans2, gpointer data1, gpointer data2, GDestroyNotify destroy1, GDestroyNotify destroy2) { guint flags2; g_return_if_fail (SWAMI_IS_CONTROL (src)); g_return_if_fail (SWAMI_IS_CONTROL (dest)); if (flags & SWAMI_CONTROL_CONN_BIDIR) { swami_control_connect_real (src, dest, trans1, data1, destroy1, flags); flags2 = flags & ~(SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); swami_control_connect_real (dest, src, trans2, data2, destroy2, flags2); } else swami_control_connect_real (src, dest, trans1, data1, destroy1, flags); #if DEBUG if (swami_control_debug) { char *s1, *s2; s1 = pretty_control (src); s2 = pretty_control (dest); g_message ("Connect: %s %s %s", s1, (flags & SWAMI_CONTROL_CONN_BIDIR) ? "<-->" : "-->", s2); g_free (s1); g_free (s2); SWAMI_CONTROL_TEST_BREAK (src, dest); } #endif } static void swami_control_connect_real (SwamiControl *src, SwamiControl *dest, SwamiValueTransform trans, gpointer data, GDestroyNotify destroy, guint flags) { SwamiControlConn *sconn, *dconn; GValue value = { 0 }, transval = { 0 }; /* allocate and init connections */ sconn = swami_control_conn_new (); sconn->flags = (flags & SWAMI_CONTROL_CONN_PRIORITY_MASK) | SWAMI_CONTROL_CONN_OUTPUT; sconn->control = dest; sconn->trans = trans; sconn->data = data; sconn->destroy = destroy; dconn = swami_control_conn_new (); dconn->flags = (flags & SWAMI_CONTROL_CONN_PRIORITY_MASK) | SWAMI_CONTROL_CONN_INPUT; dconn->control = src; /* add output connection to source control */ SWAMI_LOCK_WRITE (src); if (swami_log_if_fail (src->flags & SWAMI_CONTROL_SENDS)) { SWAMI_UNLOCK_WRITE (src); goto err_src; } if (g_slist_length (src->outputs) >= MAX_DEST_CONNECTIONS) { SWAMI_UNLOCK_WRITE (src); g_critical ("Maximum number of control connections reached!"); goto err_src; } /* add connection to list */ src->outputs = g_slist_insert_sorted (src->outputs, sconn, GCompare_func_conn_priority); g_object_ref (dest); /* ++ ref dest for source connection */ SWAMI_UNLOCK_WRITE (src); /* add input connection to destination control */ SWAMI_LOCK_WRITE (dest); if (swami_log_if_fail (dest->flags & SWAMI_CONTROL_RECVS)) { SWAMI_UNLOCK_WRITE (dest); goto err_dest; } dest->inputs = g_slist_prepend (dest->inputs, dconn); g_object_ref (src); /* ++ ref src for destination connection */ SWAMI_UNLOCK_WRITE (dest); /* check if connect parameter spec flag is set for src, and slave the parameter spec if so */ if (flags & SWAMI_CONTROL_CONN_SPEC) swami_control_sync_spec (dest, src, trans, data); /* initialize destination control from current source value? */ if (flags & SWAMI_CONTROL_CONN_INIT) { swami_control_get_value_native (src, &value); if (trans) /* transform function provided? */ { g_value_init (&transval, dest->value_type ? dest->value_type : G_VALUE_TYPE (&value)); trans (&value, &transval, data); swami_control_set_value (dest, &transval); g_value_unset (&transval); } else swami_control_set_value (dest, &value); g_value_unset (&value); } /* emit connect signals */ g_signal_emit (src, control_signals[CONNECT_SIGNAL], 0, dest, flags | SWAMI_CONTROL_CONN_OUTPUT); g_signal_emit (dest, control_signals[CONNECT_SIGNAL], 0, src, flags | SWAMI_CONTROL_CONN_INPUT); return; err_dest: /* error occured after src already connected, undo */ SWAMI_LOCK_WRITE (src); { GSList **list, *p, *prev = NULL; list = &src->outputs; p = *list; while (p) { if (p->data == sconn) { if (!prev) *list = p->next; else prev->next = p->next; g_slist_free_1 (p); g_object_unref (dest); /* -- unref dest from source connection */ break; } prev = p; p = g_slist_next (p); } } SWAMI_UNLOCK_WRITE (src); /* fall through */ err_src: /* OK to free connections unlocked since they aren't used anymore */ swami_control_conn_free (sconn); swami_control_conn_free (dconn); } /* a priority comparison function for lists of #SwamiControlConn objects */ static gint GCompare_func_conn_priority (gconstpointer a, gconstpointer b) { SwamiControlConn *aconn = (SwamiControlConn *)a; SwamiControlConn *bconn = (SwamiControlConn *)b; return ((aconn->flags & SWAMI_CONTROL_CONN_PRIORITY_MASK) - (bconn->flags & SWAMI_CONTROL_CONN_PRIORITY_MASK)); } /** * swami_control_connect_item_prop: * @dest: Destination control * @object: Object of source parameter to convert to user units * @pspec: Parameter spec of property of @object to connect to * * An ultra-convenience function to connect an existing control to a synthesis * property of an object (#IpatchItem property for example). The connection is * bi-directional and transform functions are used to convert between the two * unit types as needed. */ void swami_control_connect_item_prop (SwamiControl *dest, GObject *object, GParamSpec *pspec) { SwamiControl *src; gpointer data1, data2; guint src_unit, dest_unit; IpatchUnitInfo *info; GParamSpec *destspec; g_return_if_fail (SWAMI_IS_CONTROL (dest)); g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); /* get/create control for source item synthesis parameter */ src = swami_get_control_prop (object, pspec); /* ++ ref */ g_return_if_fail (src != NULL); /* get the synthesis unit type for this parameter */ ipatch_param_get (pspec, "unit-type", &src_unit, NULL); if (swami_log_if_fail (src_unit != 0)) /* error if no unit type */ { g_object_unref (src); /* -- unref */ return; } /* get the user unit type to convert to */ info = ipatch_unit_class_lookup_map (IPATCH_UNIT_CLASS_USER, src_unit); if (info) /* use the user unit type if found and not the same as src type */ { dest_unit = info->id; if (src_unit == dest_unit) dest_unit = 0; } else dest_unit = 0; if (dest_unit) { /* pass unit types to item_prop_value_transform */ data1 = GUINT_TO_POINTER (src_unit | (dest_unit << 16)); data2 = GUINT_TO_POINTER (dest_unit | (src_unit << 16)); /* transform the parameter spec if necessary */ destspec = swami_control_transform_spec (dest, src, /* !! floating ref */ item_prop_value_transform, data1); g_return_if_fail (destspec != NULL); ipatch_param_set (destspec, "unit-type", dest_unit, NULL); swami_control_set_spec (dest, destspec); swami_control_connect_transform (src, dest, SWAMI_CONTROL_CONN_BIDIR_INIT, item_prop_value_transform, item_prop_value_transform, data1, data2, NULL, NULL); } else swami_control_connect_transform (src, dest, SWAMI_CONTROL_CONN_BIDIR_SPEC_INIT, NULL, NULL, NULL, NULL, NULL, NULL); } /* value transform function for swami_control_connect_item_prop() */ static void item_prop_value_transform (const GValue *src, GValue *dest, gpointer data) { guint src_unit, dest_unit; src_unit = GPOINTER_TO_UINT (data); dest_unit = src_unit >> 16; src_unit &= 0xFFFF; /* do the unit conversion */ ipatch_unit_convert (src_unit, dest_unit, src, dest); } /** * swami_control_disconnect: * @src: Source control of an existing connection * @dest: Destination control of an existing connection * * Disconnects a connection specified by its @src and @dest controls. */ void swami_control_disconnect (SwamiControl *src, SwamiControl *dest) { guint flags = 0; GSList *p; g_return_if_fail (SWAMI_IS_CONTROL (src)); g_return_if_fail (SWAMI_IS_CONTROL (dest)); /* check and see if connection exists */ SWAMI_LOCK_READ (dest); p = dest->inputs; /* use single dest input list to simplify things */ while (p) /* look for matching connection */ { SwamiControlConn *conn = (SwamiControlConn *)(p->data); if (src == conn->control) { flags = conn->flags; /* get flags under lock */ break; } p = g_slist_next (p); } SWAMI_UNLOCK_READ (dest); /* adjust flags for src control connection */ flags &= ~SWAMI_CONTROL_CONN_INPUT; flags |= SWAMI_CONTROL_CONN_OUTPUT; if (p) /* connection found? - emit disconnect signal */ { g_signal_emit (src, control_signals[DISCONNECT_SIGNAL], 0, dest, flags); swami_control_real_disconnect (src, dest, flags); } } /** * swami_control_disconnect_all: * @control: Swami control object * * Disconnect all connections from a control. */ void swami_control_disconnect_all (SwamiControl *control) { SwamiControl *src, *dest = NULL; SwamiControlConn *conn; guint flags = 0; g_return_if_fail (SWAMI_IS_CONTROL (control)); do { /* look for a connection to disconnect */ SWAMI_LOCK_READ (control); if (control->inputs) /* any more input connections? */ { conn = (SwamiControlConn *)(control->inputs->data); src = g_object_ref (conn->control); /* ++ ref connection source */ dest = control; flags = conn->flags; } else if (control->outputs) /* any more direct output connections? */ { conn = (SwamiControlConn *)(control->outputs->data); dest = g_object_ref (conn->control); /* ++ ref connection dest */ src = control; flags = conn->flags; } else src = NULL; /* no more connections */ SWAMI_UNLOCK_READ (control); if (src) { /* adjust flags for src control connection */ flags &= ~SWAMI_CONTROL_CONN_INPUT; flags |= SWAMI_CONTROL_CONN_OUTPUT; g_signal_emit (src, control_signals[DISCONNECT_SIGNAL], 0, dest, flags); swami_control_real_disconnect (src, dest, flags); /* -- unref connection control */ if (control != src) g_object_unref (src); else g_object_unref (dest); } } while (src); } /** * swami_control_disconnect_unref: * @control: Swami control object * * A convenience function to disconnect all connections of a control and * unref it. This is often useful for GDestroyNotify callbacks where a * control's creator wishes to destroy the control. The control is only * destroyed if it is not referenced by anything else. */ void swami_control_disconnect_unref (SwamiControl *control) { g_return_if_fail (SWAMI_IS_CONTROL (control)); swami_control_disconnect_all (control); g_object_unref (control); } /* real disconnect routine, the default class method */ static void swami_control_real_disconnect (SwamiControl *c1, SwamiControl *c2, guint flags) { GSList **list, *p, *prev = NULL; GDestroyNotify destroy = NULL; gpointer data = NULL; SwamiControlConn *conn; SWAMI_LOCK_WRITE (c1); if (flags & SWAMI_CONTROL_CONN_OUTPUT) /* output conn (source control)? */ list = &c1->outputs; /* use output connection list */ else list = &c1->inputs; /* destination control - use input conn list */ p = *list; while (p) /* search for connection in list */ { conn = (SwamiControlConn *)(p->data); if (c2 == conn->control) /* connection found? destroy it */ { if (prev) prev->next = p->next; else *list = p->next; g_slist_free_1 (p); g_object_unref (conn->control); /* -- unref control from conn */ /* store destroy notify if output conn */ if (flags & SWAMI_CONTROL_CONN_OUTPUT) { destroy = conn->destroy; data = conn->data; } swami_control_conn_free (conn); /* free the connection */ break; } prev = p; p = g_slist_next (p); } SWAMI_UNLOCK_WRITE (c1); /* call the destroy notify for the transform user data if any */ if (destroy && data) destroy (data); /* chain disconnect signal to destination control (if source control) */ if (flags & SWAMI_CONTROL_CONN_OUTPUT) { #if DEBUG if (swami_control_debug) { char *s1, *s2; s1 = pretty_control (c1); s2 = pretty_control (c2); g_message ("Disconnect: %s =X= %s", s1, s2); g_free (s1); g_free (s2); } SWAMI_CONTROL_TEST_BREAK (c1, c2); #endif /* adjust flags for input connection (destination control) */ flags &= ~SWAMI_CONTROL_CONN_OUTPUT; flags |= SWAMI_CONTROL_CONN_INPUT; g_signal_emit (c2, control_signals[DISCONNECT_SIGNAL], 0, c1, flags); swami_control_real_disconnect (c2, c1, flags); } } /** * swami_control_get_connections: * @control: Control to get connections of * @dir: Direction of connections to get, #SWAMI_CONTROL_CONN_INPUT for incoming * connections, #SWAMI_CONTROL_CONN_OUTPUT for outgoing (values can be OR'd * to return all connections). * * Get a list of connections to a control. * * Returns: List of #SwamiControl objects connected to @control or %NULL if * none. Caller owns reference to returned list and should unref when done. */ IpatchList * swami_control_get_connections (SwamiControl *control, SwamiControlConnFlags dir) { SwamiControlConn *conn; IpatchList *listobj; GList *list = NULL; GSList *p; g_return_val_if_fail (SWAMI_IS_CONTROL (control), NULL); SWAMI_LOCK_READ (control); if (dir & SWAMI_CONTROL_CONN_INPUT) { for (p = control->inputs; p; p = g_slist_next (p)) { conn = (SwamiControlConn *)(p->data); list = g_list_prepend (list, g_object_ref (conn->control)); } } if (dir & SWAMI_CONTROL_CONN_OUTPUT) { for (p = control->outputs; p; p = g_slist_next (p)) { conn = (SwamiControlConn *)(p->data); list = g_list_prepend (list, g_object_ref (conn->control)); } } SWAMI_UNLOCK_READ (control); if (list) { listobj = ipatch_list_new (); /* ++ ref new list object */ listobj->items = g_list_reverse (list); /* reverse to priority order */ return (listobj); /* !! caller takes over reference */ } else return (NULL); } /** * swami_control_set_transform: * @src: Source control of the connection * @dest: Destination control of the connection * @trans: Transform function to assign to the connection (or %NULL for none) * @data: User data to pass to @trans function (or %NULL). * @destroy: Optional callback to free @data when @trans is disconnected. * * Assigns a value transform function to an existing control connection. The * connection is specified by @src and @dest controls. The transform function * will receive values from the @src control and should transform them * appropriately for the @dest. */ void swami_control_set_transform (SwamiControl *src, SwamiControl *dest, SwamiValueTransform trans, gpointer data, GDestroyNotify destroy) { gboolean conn_found = FALSE; GDestroyNotify oldnotify = NULL; gpointer olddata = NULL; GSList *p; g_return_if_fail (SWAMI_IS_CONTROL (src)); g_return_if_fail (SWAMI_IS_CONTROL (dest)); SWAMI_LOCK_READ (src); /* look for matching connection */ for (p = src->outputs; p; p = p->next) { SwamiControlConn *conn = (SwamiControlConn *)(p->data); if (dest == conn->control) { oldnotify = conn->destroy; olddata = conn->data; conn->trans = trans; conn->data = data; conn->destroy = destroy; conn_found = TRUE; break; } } SWAMI_UNLOCK_READ (src); /* if there already was a transform with destroy function, call it on the user data */ if (oldnotify && olddata) oldnotify (olddata); g_return_if_fail (conn_found); } /** * swami_control_get_transform: * @src: Source control of the connection * @dest: Destination control of the connection * @trans: Pointer to store transform function of the connection * * Gets the current value transform function to an existing control connection. * The connection is specified by @src and @dest controls. The value stored to * @trans may be %NULL if no transform function is assigned. */ void swami_control_get_transform (SwamiControl *src, SwamiControl *dest, SwamiValueTransform *trans) { gboolean conn_found = FALSE; GSList *p; g_return_if_fail (SWAMI_IS_CONTROL (src)); g_return_if_fail (SWAMI_IS_CONTROL (dest)); g_return_if_fail (trans != NULL); SWAMI_LOCK_READ (src); /* look for matching connection */ for (p = src->outputs; p; p = p->next) { SwamiControlConn *conn = (SwamiControlConn *)(p->data); if (dest == conn->control) { *trans = conn->trans; conn_found = TRUE; break; } } SWAMI_UNLOCK_READ (src); g_return_if_fail (conn_found); } /** * swami_control_set_flags: * @control: Control object * @flags: Value to assign to control flags (#SwamiControlFlags) * * Set flags of a control object. Flags can only be set for controls * that don't have any connections yet. */ void swami_control_set_flags (SwamiControl *control, int flags) { g_return_if_fail (SWAMI_IS_CONTROL (control)); SWAMI_LOCK_WRITE (control); if (swami_log_if_fail (!(control->inputs || control->outputs))) { SWAMI_UNLOCK_WRITE (control); return; } flags &= SWAMI_CONTROL_FLAGS_USER_MASK; control->flags &= ~SWAMI_CONTROL_FLAGS_USER_MASK; control->flags |= flags; SWAMI_UNLOCK_WRITE (control); } /** * swami_control_get_flags: * @control: Control object * * Get the flags from a control object. * * Returns: Flags value (#SwamiControlFlags) for @control */ int swami_control_get_flags (SwamiControl *control) { int flags; g_return_val_if_fail (SWAMI_IS_CONTROL (control), 0); SWAMI_LOCK_READ (control); flags = control->flags; SWAMI_UNLOCK_READ (control); return (flags); } /** * swami_control_set_queue: * @control: Control object * @queue: Queue to use for control or %NULL to disable queuing * * Set the queue used by a control object. When a control has a queue all * receive/set events are sent to the queue. This queue can then be processed * at a later time (a GUI thread for example). */ void swami_control_set_queue (SwamiControl *control, SwamiControlQueue *queue) { g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (!queue || SWAMI_IS_CONTROL_QUEUE (queue)); SWAMI_LOCK_WRITE (control); if (queue) g_object_ref (queue); /* ++ ref new queue */ if (control->queue) g_object_unref (control->queue); /* -- unref old queue */ control->queue = queue; SWAMI_UNLOCK_WRITE (control); } /** * swami_control_get_queue: * @control: Control object * * Get the queue used by a control object. * * Returns: The queue assigned to a control or %NULL if it does not have one. * The caller owns a reference to the returned queue object. */ SwamiControlQueue * swami_control_get_queue (SwamiControl *control) { SwamiControlQueue *queue = NULL; g_return_val_if_fail (SWAMI_IS_CONTROL (control), NULL); SWAMI_LOCK_READ (control); if (control->queue) queue = g_object_ref (control->queue); /* ++ ref queue */ SWAMI_UNLOCK_WRITE (control); return (queue); /* !! caller takes over reference */ } /** * swami_control_get_spec: * @control: Control object * * Get a control object's parameter specification. * * Returns: The parameter spec or %NULL on error or if the given * control is type independent. The returned spec is used internally * and should not be modified or freed, however the caller does own a * reference to it and should unref it with g_param_spec_unref when * finished. */ GParamSpec * swami_control_get_spec (SwamiControl *control) { SwamiControlClass *klass; GParamSpec *pspec = NULL; g_return_val_if_fail (SWAMI_IS_CONTROL (control), NULL); klass = SWAMI_CONTROL_GET_CLASS (control); g_return_val_if_fail (klass->get_spec != NULL, NULL); SWAMI_LOCK_READ (control); if (klass->get_spec) pspec = (*klass->get_spec)(control); if (pspec) g_param_spec_ref (pspec); SWAMI_UNLOCK_READ (control); return (pspec); } /** * swami_control_set_spec: * @control: Control object * @pspec: The parameter specification to assign (used directly) * * Set a control object's parameter specification. This function uses * the @pspec directly (its refcount is incremented and ownership is * taken). The parameter spec is not permitted to be of a different * type than the previous parameter spec. * * Returns: %TRUE if parameter specification successfully set, %FALSE * otherwise (in which case the @pspec is unreferenced). */ gboolean swami_control_set_spec (SwamiControl *control, GParamSpec *pspec) { SwamiControlClass *klass; GParamSpec *newspec; GType value_type; gboolean retval; g_return_val_if_fail (SWAMI_IS_CONTROL (control), FALSE); g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); klass = SWAMI_CONTROL_GET_CLASS (control); g_return_val_if_fail (klass->get_spec != NULL, FALSE); g_return_val_if_fail (klass->set_spec != NULL, FALSE); value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); /* use derived type if GBoxed or GObject type */ if (value_type == G_TYPE_BOXED || value_type == G_TYPE_OBJECT) value_type = pspec->value_type; /* if control's value type doesn't match the param spec value type and "no conversion" flag isn't set, then convert parameter spec */ if (control->value_type && control->value_type != value_type && !(control->flags & SWAMI_CONTROL_SPEC_NO_CONV)) { newspec = swami_param_convert_new (pspec, control->value_type); /* take control of the old pspec and then destroy it */ g_param_spec_ref (pspec); g_param_spec_sink (pspec); g_param_spec_unref (pspec); if (!newspec) return (FALSE); pspec = newspec; } SWAMI_LOCK_WRITE (control); retval = (*klass->set_spec)(control, pspec); SWAMI_UNLOCK_WRITE (control); if (retval) g_signal_emit (control, control_signals[SPEC_CHANGED_SIGNAL], 0, pspec); return (retval); } /** * swami_control_set_value_type: * @control: Control to set value type of * @type: Value type to assign to control * * Usually only called by #SwamiControl derived types from within * their instance init function. Sets the parameter spec value type * for @control and should be called once for specific type value * based controls before being used and cannot be changed * afterwards. If the type is not set then the control is a wild card * control (can handle any value). For GBoxed and GObject based values * use the specific GType for @type. */ void swami_control_set_value_type (SwamiControl *control, GType type) { g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (type != 0); SWAMI_LOCK_WRITE (control); /* make sure type is not already set */ if (control->value_type) { if (swami_log_if_fail (control->value_type == type)) { SWAMI_UNLOCK_WRITE (control); return; } } else control->value_type = type; SWAMI_UNLOCK_WRITE (control); } /** * swami_control_sync_spec: * @control: Control to set parameter spec control of * @source: The source control to get the parameter spec from * @trans: Optional value transform function * @data: User data to pass to transform function * * Synchronizes a @control object's parameter spec to the @source control. * The @source control must already have an assigned parameter spec and the * @control should have an assigned value type. The optional @trans parameter * can be used to specify a transform function, which is executed on the * min, max and default components of the parameter spec when creating the new * param spec. * * Returns: %TRUE on success, %FALSE otherwise (param spec conversion error) */ gboolean swami_control_sync_spec (SwamiControl *control, SwamiControl *source, SwamiValueTransform trans, gpointer data) { GParamSpec *pspec; int retval; g_return_val_if_fail (SWAMI_IS_CONTROL (control), FALSE); g_return_val_if_fail (SWAMI_IS_CONTROL (source), FALSE); if (trans) /* !! floating reference */ pspec = swami_control_transform_spec (control, source, trans, data); else pspec = swami_control_get_spec (source); /* ++ ref spec */ if (!pspec) { g_debug ("pspec == NULL"); return (FALSE); } /* set the param spec for the control */ retval = swami_control_set_spec (control, pspec); /* only unref if swami_control_get_spec() was used, was floating otherwise */ if (!trans) g_param_spec_unref (pspec); /* -- unref */ return (retval); } /** * swami_control_transform_spec: * @control: Destination control for the transformed parameter spec * @source: Source control with the parameter spec to transform * @trans: Transform function * @data: User defined data to pass to transform function * * Transforms a parameter spec from a @source control in preperation for * assignment to @control (but doesn't actually assign it). * * Returns: Transformed parameter spec or %NULL on error, the reference count * is 1 and floating, which means it should be taken over by calling * g_param_spec_ref() followed by g_param_spec_sink(). */ GParamSpec * swami_control_transform_spec (SwamiControl *control, SwamiControl *source, SwamiValueTransform trans, gpointer data) { GParamSpec *srcspec, *transform_spec; GType type; g_return_val_if_fail (SWAMI_IS_CONTROL (control), NULL); g_return_val_if_fail (SWAMI_IS_CONTROL (source), NULL); g_return_val_if_fail (trans != NULL, NULL); /* get the master control parameter spec */ srcspec = swami_control_get_spec (source); /* ++ ref spec */ g_return_val_if_fail (srcspec != NULL, NULL); type = control->value_type ? control->value_type : G_PARAM_SPEC_VALUE_TYPE (srcspec); /* !! floating ref, transform the parameter spec */ transform_spec = swami_param_transform_new (srcspec, type, trans, data); g_param_spec_unref (srcspec); /* -- unref */ g_return_val_if_fail (transform_spec != NULL, NULL); return (transform_spec); /* !! caller takes over floating reference */ } /** * swami_control_get_value: * @control: Control object of type #SWAMI_CONTROL_VALUE * @value: Caller supplied initialized GValue structure to store the value in. * * Get the current value of a value control object. The @control must * have the #SWAMI_CONTROL_SENDS flag set and be a value type control * with an assigned parameter spec. The @value parameter should be * initialized to the type of the control or a transformable type. */ void swami_control_get_value (SwamiControl *control, GValue *value) { SwamiControlClass *klass; GValue *get_value, tmp_value = { 0 }; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (G_IS_VALUE (value)); klass = SWAMI_CONTROL_GET_CLASS (control); /* some sanity checking */ g_return_if_fail (klass->get_value != NULL); g_return_if_fail (control->flags & SWAMI_CONTROL_SENDS); g_return_if_fail (control->value_type != 0); if (G_VALUE_HOLDS (value, control->value_type)) { /* same type, just reset value and use it */ g_value_reset (value); get_value = value; } else if (!g_value_type_transformable (control->value_type, G_VALUE_TYPE (value))) { g_critical ("%s: Failed to transform value type '%s' to type '%s'", G_STRLOC, g_type_name (control->value_type), g_type_name (G_VALUE_TYPE (value))); return; } else /* @value is not the same type, but is transformable */ { g_value_init (&tmp_value, control->value_type); get_value = &tmp_value; } /* get_value method responsible for locking, if needed */ (*klass->get_value)(control, get_value); if (get_value == &tmp_value) /* transform the value if needed */ { g_value_transform (get_value, value); g_value_unset (&tmp_value); } } /** * swami_control_get_value_native: * @control: Control object of type #SWAMI_CONTROL_VALUE * @value: Caller supplied uninitalized GValue structure to store the value in. * * Like swami_control_get_value() but forces @value to be the native type of * the control, rather than transforming the value to the initialized @value * type. Therefore @value should be un-initialized, contrary to * swami_control_get_value(). */ void swami_control_get_value_native (SwamiControl *control, GValue *value) { SwamiControlClass *klass; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (value != NULL); klass = SWAMI_CONTROL_GET_CLASS (control); /* some sanity checking */ g_return_if_fail (klass->get_value != NULL); g_return_if_fail (control->flags & SWAMI_CONTROL_SENDS); g_return_if_fail (control->value_type != 0); g_value_init (value, control->value_type); /* get_value method responsible for locking, if needed */ (*klass->get_value)(control, value); } /** * swami_control_set_value: * @control: Control object * @value: Value to set control to * * Sets/sends a value to a control object. If the control has a queue * assigned to it then the value is queued. The @value parameter * should be of a type transformable to the type used by @control (see * g_value_transformable). The @control must have the * #SWAMI_CONTROL_RECVS flag set. */ void swami_control_set_value (SwamiControl *control, const GValue *value) { SwamiControlEvent *event; SwamiControlQueue *queue; SwamiControlQueueTestFunc test_func; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (G_IS_VALUE (value)); event = swami_control_new_event (control, NULL, value); /* ++ ref new */ swami_control_event_active_ref (event); /* ++ active ref the event */ swami_control_event_ref (event); /* ++ ref event for control active list */ /* prepend the event to the active list */ SWAMI_LOCK_WRITE (control); control->active = g_list_prepend (control->active, event); SWAMI_UNLOCK_WRITE (control); queue = swami_control_get_queue (control); /* ++ ref queue */ if (queue) /* if queue, then add event to the queue */ { /* run queue test function (if any) */ test_func = queue->test_func; /* should be atomic */ if (!test_func || test_func (queue, control, event)) { swami_control_queue_add_event (queue, control, event); g_object_unref (queue); /* -- unref queue */ swami_control_event_active_unref (event); /* -- active unref event */ swami_control_event_unref (event); /* -- unref creator's ref */ return; } /* queue has a test function and it returned FALSE (no queue) */ g_object_unref (queue); /* -- unref queue */ } swami_control_set_event_real (control, event); swami_control_event_active_unref (event); /* -- active unref the event */ swami_control_event_unref (event); /* -- unref creator's ref */ } /** * swami_control_set_value_no_queue: * @control: Control object * @value: Value to set control to * * Sets/sends a value to a control object bypassing the control's * queue if it has one assigned to it. The @value parameter should be * of a type transformable to the type used by @control (see * g_value_transformable). The @control must have the * #SWAMI_CONTROL_RECVS flag set. Normally the swami_control_set_value() * function should be used instead to send a value to a control, use this * only if you know what you are doing since some controls are sensitive to * when they are processed (within a GUI thread for instance). */ void swami_control_set_value_no_queue (SwamiControl *control, const GValue *value) { SwamiControlEvent *event; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (G_IS_VALUE (value)); event = swami_control_new_event (control, NULL, value); /* ++ ref new */ swami_control_event_active_ref (event); /* ++ active ref the event */ swami_control_event_ref (event); /* ++ ref event for control active list */ /* prepend the event to the active list */ SWAMI_LOCK_WRITE (control); control->active = g_list_prepend (control->active, event); SWAMI_UNLOCK_WRITE (control); swami_control_set_event_real (control, event); swami_control_event_active_unref (event); /* -- active unref the event */ swami_control_event_unref (event); /* -- unref creator's ref */ } /** * swami_control_set_event: * @control: Control object * @event: Event to set control to * * Sets the value of a control object (value controls) or sends an * event (stream controls). This is like swami_control_set_value() but * uses an existing event, rather than creating a new one. The * @control must have the #SWAMI_CONTROL_RECVS flag set. If the * control has a queue then the event will be added to the queue. */ void swami_control_set_event (SwamiControl *control, SwamiControlEvent *event) { SwamiControlEvent *origin; SwamiControlQueue *queue; SwamiControlQueueTestFunc test_func; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); origin = event->origin ? event->origin : event; swami_control_event_active_ref (event); /* ++ active ref the event */ SWAMI_LOCK_WRITE (control); /* check for event looping (only if control can send) */ if (!swami_control_loop_check (control, event)) { SWAMI_UNLOCK_WRITE (control); swami_control_event_active_unref (event); /* -- decrement active ref */ return; } /* prepend the event origin to the active list */ control->active = g_list_prepend (control->active, origin); SWAMI_UNLOCK_WRITE (control); swami_control_event_ref (origin); /* ++ ref event for control active list */ queue = swami_control_get_queue (control); /* ++ ref queue */ if (queue) /* if queue, then add event to the queue */ { /* run queue test function (if any) */ test_func = queue->test_func; /* should be atomic */ if (!test_func || test_func (queue, control, event)) { swami_control_queue_add_event (queue, control, event); g_object_unref (queue); /* -- unref queue */ swami_control_event_active_unref (event); /* -- active unref */ return; } /* queue has a test function and it returned FALSE (no queue) */ g_object_unref (queue); /* -- unref queue */ } swami_control_set_event_real (control, event); swami_control_event_active_unref (event); /* -- decrement active ref */ } /** * swami_control_set_event_no_queue: * @control: Control object * @event: Event to set control to * * Like swami_control_set_event() but bypasses any queue that a * control might have. The @control must have the #SWAMI_CONTROL_RECVS * flag set. */ void swami_control_set_event_no_queue (SwamiControl *control, SwamiControlEvent *event) { SwamiControlEvent *origin; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); g_return_if_fail (event->active > 0); origin = event->origin ? event->origin : event; swami_control_event_active_ref (event); /* ++ active ref the event */ SWAMI_LOCK_WRITE (control); /* check for event looping (only if control can send) */ if (!swami_control_loop_check (control, event)) { SWAMI_UNLOCK_WRITE (control); swami_control_event_active_unref (event); /* -- decrement active ref */ return; } /* prepend the event origin to the active list */ control->active = g_list_prepend (control->active, origin); SWAMI_UNLOCK_WRITE (control); swami_control_event_ref (origin); /* ++ ref event for control active list */ swami_control_set_event_real (control, event); swami_control_event_active_unref (event); /* -- decrement active ref */ } /** * swami_control_set_event_no_queue_loop: * @control: Control object * @event: Event to set control to * * Like swami_control_set_event_no_queue() but doesn't do an event * loop check. This function is usually only used by * #SwamiControlQueue objects. The @control must have the * #SWAMI_CONTROL_RECVS flag set. */ void swami_control_set_event_no_queue_loop (SwamiControl *control, SwamiControlEvent *event) { g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); g_return_if_fail (event->active > 0); swami_control_event_active_ref (event); /* ++ active ref the event */ swami_control_set_event_real (control, event); swami_control_event_active_unref (event); /* -- decrement active ref */ } /* the real set event routine */ static inline void swami_control_set_event_real (SwamiControl *control, SwamiControlEvent *event) { SwamiControlClass *klass; GValue temp = { 0 }, *value; klass = SWAMI_CONTROL_GET_CLASS (control); /* some sanity checking */ g_return_if_fail (klass->set_value != NULL); g_return_if_fail (control->flags & SWAMI_CONTROL_RECVS); /* parameter conversion or specific type required? */ if (klass->get_spec && control->value_type != 0 && (!(control->flags & SWAMI_CONTROL_NO_CONV) || (control->flags & SWAMI_CONTROL_NATIVE))) { value = &event->value; if (control->flags & SWAMI_CONTROL_NATIVE) /* native type only? */ { if (!G_VALUE_HOLDS (value, control->value_type)) { g_critical ("%s: Control requires value type '%s' got '%s'", G_STRLOC, g_type_name (control->value_type), g_type_name (G_VALUE_TYPE (value))); return; } } /* transform the value if needed */ else if (!G_VALUE_HOLDS (value, control->value_type)) { g_value_init (&temp, control->value_type); if (!g_value_transform (value, &temp)) { g_value_unset (&temp); /* FIXME - probably should just set a flag or inc a counter */ g_critical ("%s: Failed to transform value type '%s' to" " type '%s'", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (control->value_type)); return; } value = &temp; } } else value = &event->value; /* No conversion necessary */ #if DEBUG if (swami_control_debug) { char *valstr = g_strdup_value_contents (value); char *s1 = pretty_control (control); g_message ("Set: %s EV:%p ORIGIN:%p VAL:<%s>='%s'", s1, event, event->origin, G_VALUE_TYPE_NAME (value), valstr); g_free (s1); g_free (valstr); SWAMI_CONTROL_TEST_BREAK (control, NULL); } #endif /* set_value method is responsible for locking, if needed */ (*klass->set_value)(control, event, value); if (value == &temp) g_value_unset (&temp); } /* Check if an event is already visited a control. Also purges old active events. Control must be locked by caller. Returns: TRUE if not looped, FALSE otherwise */ static inline gboolean swami_control_loop_check (SwamiControl *control, SwamiControlEvent *event) { SwamiControlEvent *ev, *origin; GList *p, *temp; /* if control only sends or only receives, don't do loop check. * FIXME - Is that right? */ if ((control->flags & SWAMI_CONTROL_SENDRECV) != SWAMI_CONTROL_SENDRECV) return (TRUE); origin = event->origin ? event->origin : event; /* look through active events to stop loops and to cleanup old entries */ p = control->active; while (p) { ev = (SwamiControlEvent *)(p->data); if (ev == origin) /* event loop catch */ { #if DEBUG if (swami_control_debug) { char *s1 = pretty_control (control); g_message ("Loop killer: %s EV:%p ORIGIN:%p", s1, event, origin); g_free (s1); } SWAMI_CONTROL_TEST_BREAK (control, NULL); #endif return (FALSE); /* return immediately, looped */ } if (!ev->active) /* event still active? */ { /* no, remove from list */ temp = p; p = g_list_next (p); control->active = g_list_delete_link (control->active, temp); swami_control_event_unref (ev); /* -- unref inactive event */ } else p = g_list_next (p); } return (TRUE); } /** * swami_control_transmit_value: * @control: Control object * @value: Value to transmit or %NULL (readable value controls only, sends * value changed event). * * Sends a value to all output connections of @control. Usually only * used by #SwamiControl object implementations. The @value is used if * not %NULL or the control's value is used (readable value controls * only). This is a convenience function that creates a * #SwamiControlEvent based on the new transmit value, use * swami_control_transmit_event() to send an existing event. */ void swami_control_transmit_value (SwamiControl *control, const GValue *value) { CtrlUpdateBag update_ctrls[MAX_DEST_CONNECTIONS + 1]; CtrlUpdateBag *bag; SwamiControlEvent *event, *transevent; SwamiControlConn *conn; GSList *p; int uc = 0; g_return_if_fail (SWAMI_IS_CONTROL (control)); event = swami_control_new_event (control, NULL, value); /* ++ ref new */ swami_control_event_active_ref (event); /* ++ active ref event */ swami_control_event_ref (event); /* ++ ref event for control active list */ SWAMI_LOCK_WRITE (control); /* prepend the event origin to the active list */ control->active = g_list_prepend (control->active, event); /* copy destination controls to an array under lock, which is then used outside of lock to avoid recursive dead locks */ /* loop over destination connections */ for (p = control->outputs; p; p = p->next) { conn = (SwamiControlConn *)(p->data); /* ++ ref dest control and add to array, copy transform func also */ update_ctrls[uc].control = g_object_ref (conn->control); update_ctrls[uc].trans = conn->trans; update_ctrls[uc++].data = conn->data; } update_ctrls[uc].control = NULL; SWAMI_UNLOCK_WRITE (control); #if DEBUG if (swami_control_debug) { char *s1 = pretty_control (control); g_message ("Transmit to %d dests: %s EV:%p", uc, s1, event); g_free (s1); } SWAMI_CONTROL_TEST_BREAK (control, NULL); #endif bag = update_ctrls; while (bag->control) /* send event to destination controls */ { if (bag->trans) { /* transform event using transform function */ transevent = swami_control_event_transform /* ++ ref */ (event, bag->control->value_type, bag->trans, bag->data); swami_control_set_event (bag->control, transevent); swami_control_event_unref (transevent); /* -- unref */ } else swami_control_set_event (bag->control, event); g_object_unref (bag->control); /* -- unref control from update array */ bag++; } swami_control_event_active_unref (event); /* -- active unref */ swami_control_event_unref (event); /* -- unref creator's reference */ } /** * swami_control_transmit_event: * @control: Control object * @event: Event to send to output connections of @control * * This function sends an event to all destination connected * controls. Usually only used by #SwamiControl object * implementations. */ void swami_control_transmit_event (SwamiControl *control, SwamiControlEvent *event) { CtrlUpdateBag update_ctrls[MAX_DEST_CONNECTIONS + 1]; SwamiControlEvent *transevent; CtrlUpdateBag *bag; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); swami_control_event_active_ref (event); /* ++ inc active ref count */ { /* recursive function, save on stack space */ SwamiControlConn *conn; SwamiControlEvent *origin; GSList *p; int uc = 0; origin = event->origin ? event->origin : event; swami_control_event_ref (origin); /* ++ ref event for active list */ SWAMI_LOCK_WRITE (control); /* check for event looping (only if control can send) */ if (!swami_control_loop_check (control, event)) { SWAMI_UNLOCK_WRITE (control); swami_control_event_active_unref (event); /* -- decrement active ref */ return; } control->active = g_list_prepend (control->active, origin); /* copy destination controls to an array under lock, which is then used outside of lock to avoid recursive dead locks */ /* loop over destination connections */ for (p = control->outputs; p; p = p->next) { conn = (SwamiControlConn *)(p->data); /* ++ ref dest control and add to array, copy transform func also */ update_ctrls[uc].control = g_object_ref (conn->control); update_ctrls[uc].trans = conn->trans; update_ctrls[uc++].data = conn->data; } update_ctrls[uc].control = NULL; SWAMI_UNLOCK_WRITE (control); } /* stack space saver */ #if DEBUG if (swami_control_debug) { char *s1 = pretty_control (control); g_message ("Transmit: %s EV:%p ORIGIN:%p", s1, event, event->origin); g_free (s1); } SWAMI_CONTROL_TEST_BREAK (control, NULL); #endif bag = update_ctrls; while (bag->control) /* send event to destination controls */ { if (bag->trans) { /* transform event using transform function */ transevent = swami_control_event_transform /* ++ ref */ (event, bag->control->value_type, bag->trans, bag->data); swami_control_set_event (bag->control, transevent); swami_control_event_unref (transevent); /* -- unref */ } else swami_control_set_event (bag->control, event); g_object_unref (bag->control); /* -- unref control from update array */ bag++; } swami_control_event_active_unref (event); /* -- decrement active ref */ } /** * swami_control_transmit_event_loop: * @control: Control object * @event: Event to send to output connections of @control * * Like swami_control_transmit_event() but doesn't do an event loop check. * This is useful for control implementations that receive an event and * want to re-transmit it (swami_control_transmit_event() would stop it, * since the event is already in the active list for the control). */ void swami_control_transmit_event_loop (SwamiControl *control, SwamiControlEvent *event) { CtrlUpdateBag update_ctrls[MAX_DEST_CONNECTIONS + 1]; SwamiControlEvent *transevent; CtrlUpdateBag *bag; g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); swami_control_event_active_ref (event); /* ++ inc active ref count */ { /* recursive function, save on stack space */ SwamiControlConn *conn; SwamiControlEvent *origin; GSList *p; int uc = 0; origin = event->origin ? event->origin : event; SWAMI_LOCK_WRITE (control); /* check for event in active list (only if control can send) */ if (swami_control_loop_check (control, event)) { /* not already in list, prepend the event origin to the active list */ control->active = g_list_prepend (control->active, origin); swami_control_event_ref (origin); /* ++ ref event for active list */ } /* copy destination controls to an array under lock, which is then used outside of lock to avoid recursive dead locks */ /* loop over destination connections */ for (p = control->outputs; p; p = p->next) { conn = (SwamiControlConn *)(p->data); /* ++ ref dest control and add to array, copy transform func also */ update_ctrls[uc].control = g_object_ref (conn->control); update_ctrls[uc].trans = conn->trans; update_ctrls[uc++].data = conn->data; } update_ctrls[uc].control = NULL; SWAMI_UNLOCK_WRITE (control); } /* stack space saver */ #if DEBUG if (swami_control_debug) { char *s1 = pretty_control (control); g_message ("Transmit: %s EV:%p ORIGIN:%p", s1, event, event->origin); g_free (s1); } SWAMI_CONTROL_TEST_BREAK (control, NULL); #endif bag = update_ctrls; while (bag->control) /* send event to destination controls */ { if (bag->trans) { /* transform event using transform function */ transevent = swami_control_event_transform /* ++ ref */ (event, bag->control->value_type, bag->trans, bag->data); swami_control_set_event (bag->control, transevent); swami_control_event_unref (transevent); /* -- unref */ } else swami_control_set_event (bag->control, event); g_object_unref (bag->control); /* -- unref control from update array */ bag++; } swami_control_event_active_unref (event); /* -- decrement active ref */ } /** * swami_control_do_event_expiration: * * Processes all controls in search of inactive expired events. This should * be called periodically to expire events in controls that don't receive any * events for a long period after previous activity. Note that currently this * is handled automatically if swami_init() is called. */ void swami_control_do_event_expiration (void) { GList *cp, *ep, *temp; SwamiControlEvent *ev; SwamiControl *control; G_LOCK (control_list); for (cp = control_list; cp; cp = cp->next) /* loop over controls */ { control = (SwamiControl *)(cp->data); SWAMI_LOCK_WRITE (control); ep = control->active; while (ep) /* loop over active event list */ { ev = (SwamiControlEvent *)(ep->data); if (!ev->active) /* event still active? */ { /* no, remove from list */ temp = ep; ep = g_list_next (ep); control->active = g_list_delete_link (control->active, temp); swami_control_event_unref (ev); /* -- unref inactive event */ } else ep = g_list_next (ep); } SWAMI_UNLOCK_WRITE (control); } G_UNLOCK (control_list); } /** * swami_control_new_event: * @control: Control object * @origin: Origin event or %NULL if new event is origin * @value: Value to use as event value or %NULL to use @control as the * value (a value change event) * * Create an event for @control. If @value is non %NULL then * it is used as the value of the event otherwise the @control is used as * the value of the event (a value changed event). * * Returns: New event with a refcount of 1 which the caller owns. Remember * that a #SwamiControlEvent is not a GObject and has its own reference * counting routines swami_control_event_ref() and * swami_control_event_unref(). */ SwamiControlEvent * swami_control_new_event (SwamiControl *control, SwamiControlEvent *origin, const GValue *value) { SwamiControlEvent *event; g_return_val_if_fail (SWAMI_IS_CONTROL (control), NULL); event = swami_control_event_new (TRUE); /* ++ ref new event */ if (origin) swami_control_event_set_origin (event, origin); if (value) /* if value supplied, use it */ { g_value_init (&event->value, G_VALUE_TYPE (value)); g_value_copy (value, &event->value); } else /* create a value change event */ { g_value_init (&event->value, G_TYPE_OBJECT); g_value_set_object (&event->value, control); } return (event); /* !! caller owns reference */ } swami-2.2.0/src/libswami/SwamiControl.h000066400000000000000000000253061361104770400200340ustar00rootroot00000000000000/* * SwamiControl.h - Swami control base object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_H__ #define __SWAMI_CONTROL_H__ typedef struct _SwamiControl SwamiControl; typedef struct _SwamiControlClass SwamiControlClass; //#include #include #include #include #include #include #include #include #define SWAMI_TYPE_CONTROL (swami_control_get_type ()) #define SWAMI_CONTROL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL, SwamiControl)) #define SWAMI_CONTROL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL, \ SwamiControlClass)) #define SWAMI_IS_CONTROL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL)) #define SWAMI_IS_CONTROL_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE ((obj), SWAMI_TYPE_CONTROL)) #define SWAMI_CONTROL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS (obj, SWAMI_TYPE_CONTROL, SwamiControlClass)) /* Swami control object */ struct _SwamiControl { SwamiLock parent_instance; /* derived from SwamiLock */ guint flags; /* flags field (SwamiControlFlags) */ GList *active; /* active event propagations */ SwamiControlQueue *queue; /* event queue or NULL if no queuing */ SwamiControl *master; /* control to slave parameter spec to or NULL */ GType value_type; /* control value type (or 0 for wildcard) */ /* lists of SwamiControlConn structures (defined in SwamiControl.c) */ GSList *inputs; /* list of input connections (readable) */ GSList *outputs; /* list of output connections (writable) */ }; /** * SwamiControlGetSpecFunc: * @control: Control to get parameter spec for * * A #SwamiControlClass optional method type to get the parameter spec * from a control. If this method is not used or it returns %NULL then * the control is assumed to accept control events of any type. * * Note: The control is locked while calling this method. Also, this * method should return the parameter spec quickly, to speed up the event * emission process. * * Returns: Should return the parameter specification for the control or * %NULL for type independent controls. Reference count should not be modified, * this is handled by the caller. */ typedef GParamSpec *(*SwamiControlGetSpecFunc)(SwamiControl *control); /** * SwamiControlSetSpecFunc: * @control: Control to get parameter spec for * @pspec: Parameter specification to assign * * A #SwamiControlClass optional method type to set the parameter spec * for a control. The method should call g_param_spec_ref() followed by * g_param_spec_sink () to take over the reference. * If a control has a specific value type and the * #SWAMI_CONTROL_SPEC_NO_CONV is not set then the parameter spec is * converted to the parameter spec type used for the given value type and the * min/max/default values of the parameter spec will be processed with the * set_trans value transform function if assigned, * otherwise the parameter spec is passed as is without conversion. * * Note: The control is locked while calling this method. * * Returns: Should return %TRUE if parameter spec change was allowed, * %FALSE otherwise. */ typedef gboolean (*SwamiControlSetSpecFunc)(SwamiControl *control, GParamSpec *pspec); /** * SwamiControlGetValueFunc: * @control: Control to get value from * @value: Caller supplied value to store control value in * * A #SwamiControlClass optional method type to get the value from a * value control that is readable (#SWAMI_CONTROL_SENDS flag must be * set). The @value has been initialized to the native type of the * control's parameter spec. If this method is used then * #SwamiControlGetSpecFunc must also be set. * * Note: The control is not locked when calling this method. */ typedef void (*SwamiControlGetValueFunc)(SwamiControl *control, GValue *value); /** * SwamiControlSetValueFunc: * @control: Control that is receiving an event * @event: Control event being received * @value: Value which is being received (possibly converted from original * event value depending on the control's settings) * * A #SwamiControlClass optional method type to receive control * values. If the #SWAMI_CONTROL_NO_CONV flag is not set for this * control and the #SwamiControlGetSpecFunc returns a parameter spec * then @value will be the result of the original event value * converted to the control's native type. * * This method gets called for events received via a control's inputs * or when swami_control_set_event() or swami_control_set_value() is called. * * Note: The control is not locked during this method call. */ typedef void (*SwamiControlSetValueFunc)(SwamiControl *control, SwamiControlEvent *event, const GValue *value); struct _SwamiControlClass { SwamiLockClass parent_class; /* signals */ void (*connect)(SwamiControl *c1, SwamiControl *c2, guint flags); void (*disconnect)(SwamiControl *c1, SwamiControl *c2, guint flags); /* methods */ SwamiControlGetSpecFunc get_spec; SwamiControlSetSpecFunc set_spec; SwamiControlGetValueFunc get_value; SwamiControlSetValueFunc set_value; }; typedef enum { SWAMI_CONTROL_SENDS = 1 << 0, /* control is readable/sends */ SWAMI_CONTROL_RECVS = 1 << 1, /* control is writable/receives */ SWAMI_CONTROL_NO_CONV = 1 << 2, /* don't convert incoming values */ SWAMI_CONTROL_NATIVE = 1 << 3, /* values of native value type only */ SWAMI_CONTROL_VALUE = 1 << 4, /* value control - queue optimization */ SWAMI_CONTROL_SPEC_NO_CONV = 1 << 5 /* don't convert parameter spec type */ } SwamiControlFlags; /* mask for user controlled flag bits */ #define SWAMI_CONTROL_FLAGS_USER_MASK 0x7F /* a convenience value for send/receive controls */ #define SWAMI_CONTROL_SENDRECV (SWAMI_CONTROL_SENDS | SWAMI_CONTROL_RECVS) /* 7 bits used, 5 reserved */ #define SWAMI_CONTROL_UNUSED_FLAG_SHIFT 12 /* connection priority ranking (first 2 bits of flags field) */ typedef enum { SWAMI_CONTROL_CONN_PRIORITY_DEFAULT = 0, SWAMI_CONTROL_CONN_PRIORITY_LOW = 1, SWAMI_CONTROL_CONN_PRIORITY_MEDIUM = 2, SWAMI_CONTROL_CONN_PRIORITY_HIGH = 3 } SwamiControlConnPriority; #define SWAMI_CONTROL_CONN_PRIORITY_MASK (0x2) #define SWAMI_CONTROL_CONN_DEFAULT_PRIORITY_VALUE \ (SWAMI_CONTROL_CONN_PRIORITY_MEDIUM) typedef enum /*< flags >*/ { SWAMI_CONTROL_CONN_INPUT = 1 << 2, /* set for inputs (used internally) */ SWAMI_CONTROL_CONN_OUTPUT = 1 << 3, /* set for outputs (used internally) */ SWAMI_CONTROL_CONN_INIT = 1 << 4, /* update value on connect */ SWAMI_CONTROL_CONN_BIDIR = 1 << 5, /* make a bi-directional connection */ SWAMI_CONTROL_CONN_SPEC = 1 << 6 /* synchronize the parameter spec on connect */ } SwamiControlConnFlags; /* convenience combo flags */ /* #SWAMI_CONTROL_CONN_BIDIR and #SWAMI_CONTROL_CONN_INIT */ #define SWAMI_CONTROL_CONN_BIDIR_INIT \ (SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT) /* #SWAMI_CONTROL_CONN_BIDIR, #SWAMI_CONTROL_CONN_SPEC and * #SWAMI_CONTROL_CONN_INIT */ #define SWAMI_CONTROL_CONN_BIDIR_SPEC_INIT \ (SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_SPEC | SWAMI_CONTROL_CONN_INIT) GType swami_control_get_type (void); SwamiControl *swami_control_new (void); void swami_control_connect (SwamiControl *src, SwamiControl *dest, guint flags); void swami_control_connect_transform (SwamiControl *src, SwamiControl *dest, guint flags, SwamiValueTransform trans1, SwamiValueTransform trans2, gpointer data1, gpointer data2, GDestroyNotify destroy1, GDestroyNotify destroy2); void swami_control_connect_item_prop (SwamiControl *dest, GObject *object, GParamSpec *pspec); void swami_control_disconnect (SwamiControl *src, SwamiControl *dest); void swami_control_disconnect_all (SwamiControl *control); void swami_control_disconnect_unref (SwamiControl *control); IpatchList *swami_control_get_connections (SwamiControl *control, SwamiControlConnFlags dir); void swami_control_set_transform (SwamiControl *src, SwamiControl *dest, SwamiValueTransform trans, gpointer data, GDestroyNotify destroy); void swami_control_get_transform (SwamiControl *src, SwamiControl *dest, SwamiValueTransform *trans); void swami_control_set_flags (SwamiControl *control, int flags); int swami_control_get_flags (SwamiControl *control); void swami_control_set_queue (SwamiControl *control, SwamiControlQueue *queue); SwamiControlQueue *swami_control_get_queue (SwamiControl *control); GParamSpec *swami_control_get_spec (SwamiControl *control); gboolean swami_control_set_spec (SwamiControl *control, GParamSpec *pspec); gboolean swami_control_sync_spec (SwamiControl *control, SwamiControl *source, SwamiValueTransform trans, gpointer data); GParamSpec * swami_control_transform_spec (SwamiControl *control, SwamiControl *source, SwamiValueTransform trans, gpointer data); void swami_control_set_value_type (SwamiControl *control, GType type); void swami_control_get_value (SwamiControl *control, GValue *value); void swami_control_get_value_native (SwamiControl *control, GValue *value); void swami_control_set_value (SwamiControl *control, const GValue *value); void swami_control_set_value_no_queue (SwamiControl *control, const GValue *value); void swami_control_set_event (SwamiControl *control, SwamiControlEvent *event); void swami_control_set_event_no_queue (SwamiControl *control, SwamiControlEvent *event); void swami_control_set_event_no_queue_loop (SwamiControl *control, SwamiControlEvent *event); void swami_control_transmit_value (SwamiControl *control, const GValue *value); void swami_control_transmit_event (SwamiControl *control, SwamiControlEvent *event); void swami_control_transmit_event_loop (SwamiControl *control, SwamiControlEvent *event); void swami_control_do_event_expiration (void); SwamiControlEvent *swami_control_new_event (SwamiControl *control, SwamiControlEvent *origin, const GValue *value); #endif swami-2.2.0/src/libswami/SwamiControlEvent.c000066400000000000000000000151521361104770400210270ustar00rootroot00000000000000/* * SwamiControlEvent.c - Swami event structure * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #ifdef WIN32 #include #include #endif #include "SwamiControlEvent.h" #include "swami_priv.h" GType swami_control_event_get_type (void) { static GType type = 0; if (!type) type = g_boxed_type_register_static ("SwamiControlEvent", (GBoxedCopyFunc)swami_control_event_duplicate, (GBoxedFreeFunc)swami_control_event_unref); return (type); } /** * swami_control_event_new: * @stamp: %TRUE to time stamp the new event (can be done later with * swami_control_event_stamp(). * * Create a new control event structure. * * Returns: New control event with a refcount of 1 which the caller owns. * Keep in mind that a SwamiControlEvent is not a GObject, so it does its * own refcounting with swami_control_event_ref() and * swami_control_event_unref(). */ SwamiControlEvent * swami_control_event_new (gboolean stamp) { SwamiControlEvent *event; event = g_slice_new0 (SwamiControlEvent); event->refcount = 1; if (stamp) { #ifndef WIN32 gettimeofday (&event->tick, NULL); #else static DWORD tick_start ; tick_start = GetTickCount(); event->tick.tv_sec = tick_start / 1000; event->tick.tv_usec = ( tick_start % 1000 ) * 1000; #endif } return (event); } /** * swami_control_event_free: * @event: Swami control event structure to free * * Frees a Swami control event structure. Normally this function should * not be used, swami_control_event_unref() should be used instead to allow * for multiple users of a SwamiControlEvent. Calling this function bypasses * reference counting, so make sure you know what you are doing if you use * this. */ void swami_control_event_free (SwamiControlEvent *event) { g_return_if_fail (event != NULL); if (event->origin) swami_control_event_unref (event->origin); g_value_unset (&event->value); g_slice_free (SwamiControlEvent, event); } /** * swami_control_event_duplicate: * @event: Swami control event structure * * Duplicate a control event. The refcount and active count are not duplicated, * but the tick, origin and value are. * * Returns: New duplicate control event */ SwamiControlEvent * swami_control_event_duplicate (const SwamiControlEvent *event) { SwamiControlEvent *dup; g_return_val_if_fail (event != NULL, NULL); dup = g_slice_new0 (SwamiControlEvent); dup->refcount = 1; dup->tick = event->tick; if (event->origin) dup->origin = swami_control_event_ref (event->origin); g_value_copy (&event->value, &dup->value); return (dup); } /** * swami_control_event_transform: * @event: Swami control event structure * @valtype: The type for the new value (or 0 to use the @event value type) * @trans: Value transform function * @data: User data passed to transform function * * Like swami_control_event_duplicate() but transforms the event's value * using the @trans function to the type indicated by @valtype. * * Returns: New transformed control event (caller owns creator's reference) */ SwamiControlEvent * swami_control_event_transform (SwamiControlEvent *event, GType valtype, SwamiValueTransform trans, gpointer data) { SwamiControlEvent *dup; g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (trans != NULL, NULL); dup = g_slice_new0 (SwamiControlEvent); dup->refcount = 1; dup->tick = event->tick; dup->origin = event->origin ? event->origin : event; swami_control_event_ref (dup->origin); g_value_init (&dup->value, valtype ? valtype : G_VALUE_TYPE (&event->value)); trans (&event->value, &dup->value, data); return (dup); } /** * swami_control_event_stamp: * @event: Event to stamp * * Stamps an event with the current tick count. */ void swami_control_event_stamp (SwamiControlEvent *event) { g_return_if_fail (event != NULL); #ifndef WIN32 gettimeofday (&event->tick, NULL); #else { static DWORD tick_start ; tick_start = GetTickCount(); event->tick.tv_sec = tick_start / 1000; event->tick.tv_usec = ( tick_start % 1000 ) * 1000; } #endif } /** * swami_control_event_set_origin: * @event: Event structure * @origin: Origin event or %NULL if @event is its own origin * * Set the origin of an event. Should only be set once since its * not multi-thread locked. */ void swami_control_event_set_origin (SwamiControlEvent *event, SwamiControlEvent *origin) { g_return_if_fail (event != NULL); g_return_if_fail (event->origin == NULL); if (origin) swami_control_event_ref (origin); event->origin = origin; } /** * swami_control_event_ref: * @event: Event structure * * Increment the reference count of an event. * * Returns: The same referenced @event as a convenience. */ SwamiControlEvent * swami_control_event_ref (SwamiControlEvent *event) { g_return_val_if_fail (event != NULL, NULL); event->refcount++; return (event); } /** * swami_control_event_unref: * @event: Event structure * * Decrement the reference count of an event. If the reference count * reaches 0 the event will be freed. */ void swami_control_event_unref (SwamiControlEvent *event) { g_return_if_fail (event != NULL); g_return_if_fail (event->refcount > 0); if (--event->refcount == 0) swami_control_event_free (event); } /** * swami_control_event_active_ref: * @event: Event structure * * Increment the active propagation reference count. */ void swami_control_event_active_ref (SwamiControlEvent *event) { g_return_if_fail (event != NULL); event->active++; } /** * swami_control_event_active_unref: * @event: Event object * * Decrement the active propagation reference count. */ void swami_control_event_active_unref (SwamiControlEvent *event) { g_return_if_fail (event != NULL); g_return_if_fail (event->active > 0); event->active--; } swami-2.2.0/src/libswami/SwamiControlEvent.h000066400000000000000000000050041361104770400210270ustar00rootroot00000000000000/* * SwamiControlEvent.h - Swami control event structure (not an object) * A structure that defines a control event. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_EVENT_H__ #define __SWAMI_CONTROL_EVENT_H__ #ifdef _WIN32 #include #else #include #endif #include #include #include typedef struct _SwamiControlEvent SwamiControlEvent; /* a boxed type for SwamiControlEvent */ #define SWAMI_TYPE_CONTROL_EVENT (swami_control_event_get_type ()) struct _SwamiControlEvent { struct timeval tick; /* tick time */ SwamiControlEvent *origin; /* origin event or %NULL if is origin */ GValue value; /* value for this event */ int active; /* active propagation count */ int refcount; /* reference count */ }; /* an accessor macro for the value field of an event */ #define SWAMI_CONTROL_EVENT_VALUE(event) (&((event)->value)) #include GType swami_control_event_get_type (void); SwamiControlEvent *swami_control_event_new (gboolean stamp); void swami_control_event_free (SwamiControlEvent *event); SwamiControlEvent * swami_control_event_duplicate (const SwamiControlEvent *event); SwamiControlEvent *swami_control_event_transform (SwamiControlEvent *event, GType valtype, SwamiValueTransform trans, gpointer data); void swami_control_event_stamp (SwamiControlEvent *event); void swami_control_event_set_origin (SwamiControlEvent *event, SwamiControlEvent *origin); SwamiControlEvent *swami_control_event_ref (SwamiControlEvent *event); void swami_control_event_unref (SwamiControlEvent *event); void swami_control_event_active_ref (SwamiControlEvent *event); void swami_control_event_active_unref (SwamiControlEvent *event); #endif swami-2.2.0/src/libswami/SwamiControlFunc.c000066400000000000000000000147651361104770400206520ustar00rootroot00000000000000/* * SwamiControlFunc.c - Swami function control object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiControlFunc.h" #include "SwamiControl.h" #include "SwamiLog.h" #include "swami_priv.h" #include "util.h" static void swami_control_func_class_init (SwamiControlFuncClass *klass); static void swami_control_func_finalize (GObject *object); static GParamSpec *control_func_get_spec_method (SwamiControl *control); static gboolean control_func_set_spec_method (SwamiControl *control, GParamSpec *spec); static void control_func_get_value_method (SwamiControl *control, GValue *value); static void control_func_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static GObjectClass *parent_class = NULL; GType swami_control_func_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiControlFuncClass), NULL, NULL, (GClassInitFunc) swami_control_func_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiControlFunc), 0, (GInstanceInitFunc) NULL }; otype = g_type_register_static (SWAMI_TYPE_CONTROL, "SwamiControlFunc", &type_info, 0); } return (otype); } static void swami_control_func_class_init (SwamiControlFuncClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_control_func_finalize; control_class->get_spec = control_func_get_spec_method; control_class->set_spec = control_func_set_spec_method; control_class->get_value = control_func_get_value_method; control_class->set_value = control_func_set_value_method; } static void swami_control_func_finalize (GObject *object) { SwamiControlFunc *ctrlfunc = SWAMI_CONTROL_FUNC (object); SWAMI_LOCK_WRITE (ctrlfunc); if (ctrlfunc->pspec) g_param_spec_unref (ctrlfunc->pspec); if (ctrlfunc->destroy_func) (*ctrlfunc->destroy_func)(ctrlfunc); ctrlfunc->get_func = NULL; ctrlfunc->set_func = NULL; ctrlfunc->destroy_func = NULL; SWAMI_UNLOCK_WRITE (ctrlfunc); parent_class->finalize (object); } /* control is locked by caller */ static GParamSpec * control_func_get_spec_method (SwamiControl *control) { SwamiControlFunc *ctrlfunc = SWAMI_CONTROL_FUNC (control); return (ctrlfunc->pspec); } /* control is locked by caller */ static gboolean control_func_set_spec_method (SwamiControl *control, GParamSpec *spec) { SwamiControlFunc *ctrlfunc = SWAMI_CONTROL_FUNC (control); if (ctrlfunc->pspec) g_param_spec_unref (ctrlfunc->pspec); ctrlfunc->pspec = g_param_spec_ref (spec); g_param_spec_sink (spec); /* take ownership of the parameter spec */ return (TRUE); } /* locking is up to user (not locked) */ static void control_func_get_value_method (SwamiControl *control, GValue *value) { SwamiControlGetValueFunc func = SWAMI_CONTROL_FUNC (control)->get_func; if (func) (*func)(control, value); } /* locking is up to user (not locked) */ static void control_func_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiControlSetValueFunc func = SWAMI_CONTROL_FUNC (control)->set_func; (*func)(control, event, value); } /** * swami_control_func_new: * * Create a new function callback control. Function controls are useful for * easily creating custom controls using callback functions. * * Returns: New function control with a refcount of 1 which the caller owns. */ SwamiControlFunc * swami_control_func_new (void) { return (SWAMI_CONTROL_FUNC (g_object_new (SWAMI_TYPE_CONTROL_FUNC, NULL))); } /** * swami_control_func_assign_funcs: * @ctrlfunc: Swami function control object * @get_func: Function to call to get control value or %NULL if not a readable * value control (may still send events) * @set_func: Function to call to set control value or %NULL if not a writable * value control * @destroy_func: Function to call when control is destroyed or function * callbacks are changed or %NULL if no cleanup is needed. * @user_data: User defined pointer (set in @ctrlfunc instance). * * Assigns callback functions to a Swami function control object. * These callback functions should handle the getting and setting of the * control's value. The value passed to these callback functions is initialized * to the type of the control's parameter spec and this type should not be * changed. The @destroy_func callback is called when the control is destroyed * or when the callback functions are changed and should do any needed cleanup. * The control is not locked for any of these callbacks and so must be done * in the callback if there are any thread sensitive operations. */ void swami_control_func_assign_funcs (SwamiControlFunc *ctrlfunc, SwamiControlGetValueFunc get_func, SwamiControlSetValueFunc set_func, SwamiControlFuncDestroy destroy_func, gpointer user_data) { SwamiControl *control; g_return_if_fail (SWAMI_IS_CONTROL_FUNC (ctrlfunc)); control = SWAMI_CONTROL (ctrlfunc); SWAMI_LOCK_WRITE (ctrlfunc); /* ensure input/output connections are still valid if changing functions */ if (control->inputs && !set_func) { g_critical ("%s: Invalid writable function control function change", G_STRLOC); SWAMI_UNLOCK_WRITE (ctrlfunc); return; } if (ctrlfunc->destroy_func) (*ctrlfunc->destroy_func)(ctrlfunc); control->flags = SWAMI_CONTROL_SENDS | (set_func ? SWAMI_CONTROL_RECVS : 0); ctrlfunc->get_func = get_func; ctrlfunc->set_func = set_func; ctrlfunc->destroy_func = destroy_func; ctrlfunc->user_data = user_data; SWAMI_UNLOCK_WRITE (ctrlfunc); } swami-2.2.0/src/libswami/SwamiControlFunc.h000066400000000000000000000061631361104770400206500ustar00rootroot00000000000000/* * SwamiControlFunc.h - Header for Swami control function object * A convenient control type that uses user defined callback routines. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_FUNC_H__ #define __SWAMI_CONTROL_FUNC_H__ #include #include #include typedef struct _SwamiControlFunc SwamiControlFunc; typedef struct _SwamiControlFuncClass SwamiControlFuncClass; #define SWAMI_TYPE_CONTROL_FUNC (swami_control_func_get_type ()) #define SWAMI_CONTROL_FUNC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_FUNC, \ SwamiControlFunc)) #define SWAMI_CONTROL_FUNC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_FUNC, \ SwamiControlFuncClass)) #define SWAMI_IS_CONTROL_FUNC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_FUNC)) #define SWAMI_IS_CONTROL_FUNC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTROL_FUNC)) /** * SwamiControlFuncDestroy: * @control: Function control object * * A function to call when the function control is destroyed or when the * function callbacks are changed. This function should handle all cleanup * for the callback functions. This function is called with @control * multi-thread write locked. */ typedef void (*SwamiControlFuncDestroy)(SwamiControlFunc *control); /* function control object */ struct _SwamiControlFunc { SwamiControl parent_instance; /* derived from SwamiControl */ SwamiControlGetValueFunc get_func; /* callback function to get value */ SwamiControlSetValueFunc set_func; /* callback function to set value */ SwamiControlFuncDestroy destroy_func; /* destroy function */ gpointer user_data; /* user data for callback functions */ GParamSpec *pspec; /* optional parameter specification for this control */ }; /* function control class */ struct _SwamiControlFuncClass { SwamiControlClass parent_class; }; /* macro to get user data field from a SwamiControlFunc */ #define SWAMI_CONTROL_FUNC_DATA(ctrl) (SWAMI_CONTROL_FUNC (ctrl)->user_data) GType swami_control_func_get_type (void); SwamiControlFunc *swami_control_func_new (void); void swami_control_func_assign_funcs (SwamiControlFunc *ctrlfunc, SwamiControlGetValueFunc get_func, SwamiControlSetValueFunc set_func, SwamiControlFuncDestroy destroy_func, gpointer user_data); #endif swami-2.2.0/src/libswami/SwamiControlHub.c000066400000000000000000000054041361104770400204630ustar00rootroot00000000000000/* * SwamiControlHub.c - Control hub object * Re-transmits any events it receives * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiControlHub.h" #include "SwamiControl.h" #include "SwamiLog.h" #include "swami_priv.h" #include "util.h" static void swami_control_hub_class_init (SwamiControlHubClass *klass); static void control_hub_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swami_control_hub_init (SwamiControlHub *ctrlhub); GType swami_control_hub_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiControlHubClass), NULL, NULL, (GClassInitFunc) swami_control_hub_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiControlHub), 0, (GInstanceInitFunc) swami_control_hub_init }; otype = g_type_register_static (SWAMI_TYPE_CONTROL, "SwamiControlHub", &type_info, 0); } return (otype); } static void swami_control_hub_class_init (SwamiControlHubClass *klass) { SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); control_class->set_value = control_hub_set_value_method; } static void control_hub_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { /* re-transmit the event (without loop check) */ swami_control_transmit_event_loop (control, event); } static void swami_control_hub_init (SwamiControlHub *ctrlhub) { /* hubs send and receive */ swami_control_set_flags (SWAMI_CONTROL (ctrlhub), SWAMI_CONTROL_SENDRECV); } /** * swami_control_hub_new: * * Create a new control hub. Control hubes re-transmit any events they * receive and are useful for connecting many controls together. * * Returns: New control hub with a refcount of 1 which the caller owns. */ SwamiControlHub * swami_control_hub_new (void) { return (SWAMI_CONTROL_HUB (g_object_new (SWAMI_TYPE_CONTROL_HUB, NULL))); } swami-2.2.0/src/libswami/SwamiControlHub.h000066400000000000000000000037161361104770400204740ustar00rootroot00000000000000/* * SwamiControlHub.h - Header for control hub object * Re-transmits any events that it receives * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_HUB_H__ #define __SWAMI_CONTROL_HUB_H__ #include #include #include typedef struct _SwamiControlHub SwamiControlHub; typedef struct _SwamiControlHubClass SwamiControlHubClass; #define SWAMI_TYPE_CONTROL_HUB (swami_control_hub_get_type ()) #define SWAMI_CONTROL_HUB(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_HUB, \ SwamiControlHub)) #define SWAMI_CONTROL_HUB_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_HUB, \ SwamiControlHubClass)) #define SWAMI_IS_CONTROL_HUB(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_HUB)) #define SWAMI_IS_CONTROL_HUB_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTROL_HUB)) /* control hub object */ struct _SwamiControlHub { SwamiControl parent_instance; /* derived from SwamiControl */ }; /* control hub class */ struct _SwamiControlHubClass { SwamiControlClass parent_class; }; GType swami_control_hub_get_type (void); SwamiControlHub *swami_control_hub_new (void); #endif swami-2.2.0/src/libswami/SwamiControlMidi.c000066400000000000000000000113361361104770400206300ustar00rootroot00000000000000/* * SwamiControlMidi.c - Swami MIDI control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiControlMidi.h" #include "SwamiControl.h" #include "SwamiLog.h" #include "swami_priv.h" #include "util.h" static void swami_control_midi_class_init (SwamiControlMidiClass *klass); static void swami_control_midi_init (SwamiControlMidi *ctrlmidi); GType swami_control_midi_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiControlMidiClass), NULL, NULL, (GClassInitFunc) swami_control_midi_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiControlMidi), 0, (GInstanceInitFunc) swami_control_midi_init }; otype = g_type_register_static (SWAMI_TYPE_CONTROL_FUNC, "SwamiControlMidi", &type_info, 0); } return (otype); } static void swami_control_midi_class_init (SwamiControlMidiClass *klass) { SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); control_class->set_spec = NULL; /* clear set_spec method */ } static void swami_control_midi_init (SwamiControlMidi *ctrlmidi) { swami_control_set_flags (SWAMI_CONTROL (ctrlmidi), SWAMI_CONTROL_SENDRECV | SWAMI_CONTROL_NO_CONV | SWAMI_CONTROL_NATIVE); } /** * swami_control_midi_new: * * Create a new MIDI control. * * Returns: New MIDI control with a refcount of 1 which the caller owns. */ SwamiControlMidi * swami_control_midi_new (void) { return (SWAMI_CONTROL_MIDI (g_object_new (SWAMI_TYPE_CONTROL_MIDI, NULL))); } /** * swami_control_midi_set_callback: * @midi: MIDI control * @callback: Function to callback when a new event is received or %NULL * @data: User defined data to pass to the callback * * Set a callback function for received events on a MIDI control. */ void swami_control_midi_set_callback (SwamiControlMidi *midi, SwamiControlSetValueFunc callback, gpointer data) { g_return_if_fail (SWAMI_IS_CONTROL_MIDI (midi)); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (midi), NULL, callback, NULL, data); } /** * swami_control_midi_send: * @midi: MIDI control * @type: MIDI event type * @channel: MIDI channel to send on * @param1: First parameter * @param2: Second parameter (only used with certain event types) * * A convenience function to send an event TO a MIDI control. One * could do the same by creating a #SwamiMidiEvent, calling * swami_midi_event_set() on it and then setting the * control with swami_control_set_value(). */ void swami_control_midi_send (SwamiControlMidi *midi, SwamiMidiEventType type, int channel, int param1, int param2) { SwamiMidiEvent *event; GValue value = { 0 }; g_return_if_fail (SWAMI_IS_CONTROL_MIDI (midi)); event = swami_midi_event_new (); swami_midi_event_set (event, type, channel, param1, param2); g_value_init (&value, SWAMI_TYPE_MIDI_EVENT); g_value_take_boxed (&value, event); swami_control_set_value (SWAMI_CONTROL (midi), &value); g_value_unset (&value); } /** * swami_control_midi_transmit: * @midi: MIDI control * @type: MIDI event type * @channel: MIDI channel to send on * @param1: First parameter * @param2: Second parameter (only used with certain event types) * * A convenience function to send an event FROM a MIDI control. One * could do the same by creating a #SwamiMidiEvent, calling * swami_midi_event_set() on it and then transmitting it from the * control with swami_control_transmit_value(). */ void swami_control_midi_transmit (SwamiControlMidi *midi, SwamiMidiEventType type, int channel, int param1, int param2) { SwamiMidiEvent *event; GValue value = { 0 }; g_return_if_fail (SWAMI_IS_CONTROL_MIDI (midi)); event = swami_midi_event_new (); swami_midi_event_set (event, type, channel, param1, param2); g_value_init (&value, SWAMI_TYPE_MIDI_EVENT); g_value_take_boxed (&value, event); swami_control_transmit_value (SWAMI_CONTROL (midi), &value); g_value_unset (&value); } swami-2.2.0/src/libswami/SwamiControlMidi.h000066400000000000000000000046451361104770400206420ustar00rootroot00000000000000/* * SwamiControlMidi.h - Header for Swami MIDI control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_MIDI_H__ #define __SWAMI_CONTROL_MIDI_H__ #include #include #include #include #include typedef struct _SwamiControlMidi SwamiControlMidi; typedef struct _SwamiControlMidiClass SwamiControlMidiClass; #define SWAMI_TYPE_CONTROL_MIDI (swami_control_midi_get_type ()) #define SWAMI_CONTROL_MIDI(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_MIDI, \ SwamiControlMidi)) #define SWAMI_CONTROL_MIDI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_MIDI, \ SwamiControlMidiClass)) #define SWAMI_IS_CONTROL_MIDI(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_MIDI)) #define SWAMI_IS_CONTROL_MIDI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTROL_MIDI)) /* MIDI control object */ struct _SwamiControlMidi { SwamiControlFunc parent_instance; /* derived from SwamiControlFunc */ }; /* MIDI control class */ struct _SwamiControlMidiClass { SwamiControlFuncClass parent_class; }; GType swami_control_midi_get_type (void); SwamiControlMidi *swami_control_midi_new (void); void swami_control_midi_set_callback (SwamiControlMidi *midi, SwamiControlSetValueFunc callback, gpointer data); void swami_control_midi_send (SwamiControlMidi *midi, SwamiMidiEventType type, int channel, int param1, int param2); void swami_control_midi_transmit (SwamiControlMidi *midi, SwamiMidiEventType type, int channel, int param1, int param2); #endif swami-2.2.0/src/libswami/SwamiControlProp.c000066400000000000000000000644021361104770400206700ustar00rootroot00000000000000/* * SwamiControlProp.c - GObject property control object * Special support for IpatchItem properties (don't use "notify") * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiControlProp.h" #include "libswami.h" #include "swami_priv.h" /* hash key used for cache of SwamiControlProp by Object:GParamSpec */ typedef struct { GObject *object; /* object, no ref is held, weak notify instead */ GParamSpec *pspec; /* property param spec of the control */ } ControlPropKey; /* defined in libswami.c */ extern void _swami_set_patch_prop_origin_event (SwamiControlEvent *origin); static void control_prop_weak_notify (gpointer data, GObject *was_object); static guint control_prop_hash_func (gconstpointer key); static gboolean control_prop_equal_func (gconstpointer a, gconstpointer b); static void control_prop_key_free_func (gpointer data); static void swami_control_prop_class_init (SwamiControlPropClass *klass); static void swami_control_prop_finalize (GObject *object); static GParamSpec *control_prop_get_spec_method (SwamiControl *control); static void control_prop_get_value_method (SwamiControl *control, GValue *value); static void control_prop_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swami_control_prop_item_cb_notify (IpatchItemPropNotify *notify); static void swami_control_prop_item_cb_notify_event (IpatchItemPropNotify *notify); static void swami_control_prop_cb_notify (GObject *object, GParamSpec *pspec, SwamiControlProp *ctrlprop); static void swami_control_prop_cb_notify_event (GObject *object, GParamSpec *pspec, SwamiControlProp *ctrlprop); static void control_prop_object_weak_notify (gpointer user_data, GObject *object); static GObjectClass *parent_class = NULL; /* hash of GObject:GParamSpec -> SwamiControlProp objects. Only 1 control needed for an object property. */ G_LOCK_DEFINE_STATIC (control_prop_hash); static GHashTable *control_prop_hash = NULL; /* reverse hash for quick lookup by control (on control removal) * SwamiControlProp -> ControlPropKey (uses key from control_prop_hash) */ static GHashTable *control_prop_reverse_hash = NULL; /* thread private variable for preventing IpatchItem property loops, stores current origin SwamiControlEvent for property changes during IpatchItem property set, NULL when not in use */ static GStaticPrivate prop_notify_origin = G_STATIC_PRIVATE_INIT; /** * swami_get_control_prop: * @object: Object to get property control from (%NULL for wildcard, * #IpatchItem only) * @pspec: Property parameter spec of @object to get control for (%NULL for * wildcard, #IpatchItem only) * * Gets the #SwamiControlProp object associated with an object's GObject * property by @pspec. If a control for the given @object and @pspec * does not yet exist, then it is created and returned. Passing * %NULL for @object and/or @pspec create's a wildcard control which receives * property change events for a specific property of all items (@object is * %NULL), any property of a specifc item (@pspec is %NULL) or all item property * changes (both are %NULL). Note that wildcard property controls only work * for #IpatchItem derived objects currently. * * Returns: The control associated with the @object and property @pspec. * Caller owns a reference to the returned object and should unref it when * finished. */ SwamiControl * swami_get_control_prop (GObject *object, GParamSpec *pspec) { ControlPropKey key, *newkey; SwamiControlProp *control, *beatus; key.object = object; key.pspec = pspec; G_LOCK (control_prop_hash); control = (SwamiControlProp *)g_hash_table_lookup (control_prop_hash, &key); if (control) g_object_ref (control); G_UNLOCK (control_prop_hash); if (!control) { /* ++ ref control (caller takes over reference) */ control = swami_control_prop_new (object, pspec); g_return_val_if_fail (control != NULL, NULL); newkey = g_slice_new (ControlPropKey); newkey->object = object; newkey->pspec = pspec; G_LOCK (control_prop_hash); /* double check that another thread didn't create the same control between * these two locks (beat us to it) */ beatus = (SwamiControlProp *)g_hash_table_lookup (control_prop_hash, &key); if (!beatus) { g_hash_table_insert (control_prop_hash, newkey, control); g_hash_table_insert (control_prop_reverse_hash, control, newkey); } else g_object_ref (beatus); G_UNLOCK (control_prop_hash); if (beatus) /* if another thread created control, cleanup properly */ { g_slice_free (ControlPropKey, newkey); g_object_unref (control); /* -- unref */ control = beatus; } else /* passively watch the control, to remove it from hash when destroyed */ g_object_weak_ref (G_OBJECT (control), control_prop_weak_notify, NULL); } return ((SwamiControl *)control); } /* weak notify to remove control from hash when it gets destroyed */ static void control_prop_weak_notify (gpointer data, GObject *was_object) { ControlPropKey *key; G_LOCK (control_prop_hash); /* lookup key in reverse hash */ key = g_hash_table_lookup (control_prop_reverse_hash, was_object); if (key) /* still in hash? (this func may be called multiple times) */ { g_hash_table_remove (control_prop_reverse_hash, was_object); g_hash_table_remove (control_prop_hash, key); } G_UNLOCK (control_prop_hash); } /** * swami_get_control_prop_by_name: * @object: Object to get property control from * @name: A property of @object to get the control for (or %NULL for wildcard, * #IpatchItem derived objects only) * * Like swami_get_control_prop() but takes a property name * instead. It is also therefore not possible to specify a wildcard @object * (%NULL). * * Returns: The control associated with the @object and property @name. * Caller owns a reference to the returned object and should unref it when * finished. */ SwamiControl * swami_get_control_prop_by_name (GObject *object, const char *name) { GParamSpec *pspec; GObjectClass *klass; g_return_val_if_fail (G_IS_OBJECT (object), NULL); if (name) { klass = G_OBJECT_GET_CLASS (object); g_return_val_if_fail (klass != NULL, NULL); pspec = g_object_class_find_property (klass, name); g_return_val_if_fail (pspec != NULL, NULL); } else pspec = NULL; return (swami_get_control_prop (object, pspec)); } /** * swami_control_prop_connect_objects: * @src: Source object * @propname1: Property of source object (and @dest if @propname2 is %NULL). * @dest: Destination object * @propname2: Property of destination object (%NULL to use @propname1). * @flags: Same flags as for swami_control_connect(). * * Connect the properties of two objects together using #SwamiControlProp * controls. */ void swami_control_prop_connect_objects (GObject *src, const char *propname1, GObject *dest, const char *propname2, guint flags) { SwamiControl *sctrl, *dctrl; g_return_if_fail (G_IS_OBJECT (src)); g_return_if_fail (propname1 != NULL); g_return_if_fail (G_IS_OBJECT (dest)); sctrl = swami_get_control_prop_by_name (src, propname1); /* ++ ref */ g_return_if_fail (sctrl != NULL); /* ++ ref */ dctrl = swami_get_control_prop_by_name (dest, propname2 ? propname2 : propname1); if (swami_log_if_fail (dctrl != NULL)) { g_object_unref (sctrl); /* -- unref */ return; } swami_control_connect (sctrl, dctrl, flags); g_object_unref (sctrl); /* -- unref */ g_object_unref (dctrl); /* -- unref */ } /** * swami_control_prop_connect_to_control: * @src: Object with property to connect as source * @propname: Property of @object to use as source control * @dest: Destination control to connect the object property to * @flags: Same flags as for swami_control_connect(). * * A convenience function to connect an object property as the source control * to another #SwamiControl. */ void swami_control_prop_connect_to_control (GObject *src, const char *propname, SwamiControl *dest, guint flags) { SwamiControl *sctrl; g_return_if_fail (G_IS_OBJECT (src)); g_return_if_fail (propname != NULL); g_return_if_fail (SWAMI_IS_CONTROL (dest)); sctrl = swami_get_control_prop_by_name (src, propname); /* ++ ref */ g_return_if_fail (sctrl != NULL); swami_control_connect (sctrl, dest, flags); g_object_unref (sctrl); /* -- unref */ } /** * swami_control_prop_connect_from_control: * @src: Source control to connect to an object property * @dest: Object with property to connect to * @propname: Property of @object to use as destination control * @flags: Same flags as for swami_control_connect(). * * A convenience function to connect a #SwamiControl to an object property as * the destination control. */ void swami_control_prop_connect_from_control (SwamiControl *src, GObject *dest, const char *propname, guint flags) { SwamiControl *dctrl; g_return_if_fail (SWAMI_IS_CONTROL (src)); g_return_if_fail (G_IS_OBJECT (dest)); g_return_if_fail (propname != NULL); dctrl = swami_get_control_prop_by_name (dest, propname); /* ++ ref */ g_return_if_fail (dctrl != NULL); swami_control_connect (src, dctrl, flags); g_object_unref (dctrl); /* -- unref */ } static guint control_prop_hash_func (gconstpointer key) { ControlPropKey *pkey = (ControlPropKey *)key; return (GPOINTER_TO_UINT (pkey->object) + GPOINTER_TO_UINT (pkey->pspec)); } static gboolean control_prop_equal_func (gconstpointer a, gconstpointer b) { ControlPropKey *akey = (ControlPropKey *)a; ControlPropKey *bkey = (ControlPropKey *)b; return (akey->object == bkey->object && akey->pspec == bkey->pspec); } static void control_prop_key_free_func (gpointer data) { g_slice_free (ControlPropKey, data); } GType swami_control_prop_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiControlPropClass), NULL, NULL, (GClassInitFunc) swami_control_prop_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiControlProp), 0, (GInstanceInitFunc) NULL }; control_prop_hash = g_hash_table_new_full (control_prop_hash_func, control_prop_equal_func, control_prop_key_free_func, NULL); control_prop_reverse_hash = g_hash_table_new (NULL, NULL); otype = g_type_register_static (SWAMI_TYPE_CONTROL, "SwamiControlProp", &type_info, 0); } return (otype); } static void swami_control_prop_class_init (SwamiControlPropClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_control_prop_finalize; control_class->get_spec = control_prop_get_spec_method; control_class->set_spec = NULL; control_class->get_value = control_prop_get_value_method; control_class->set_value = control_prop_set_value_method; } static void swami_control_prop_finalize (GObject *object) { SwamiControlProp *ctrlprop = SWAMI_CONTROL_PROP (object); SWAMI_LOCK_WRITE (ctrlprop); if (ctrlprop->object) { if (ctrlprop->item_handler_id) ipatch_item_prop_disconnect (ctrlprop->item_handler_id); else if (g_signal_handler_is_connected (ctrlprop->object, ctrlprop->notify_handler_id)) g_signal_handler_disconnect (ctrlprop->object, ctrlprop->notify_handler_id); g_object_weak_unref (ctrlprop->object, control_prop_object_weak_notify, ctrlprop); ctrlprop->object = NULL; } if (ctrlprop->spec) g_param_spec_unref (ctrlprop->spec); SWAMI_UNLOCK_WRITE (ctrlprop); if (parent_class->finalize) parent_class->finalize (object); } /* control is locked by caller */ static GParamSpec * control_prop_get_spec_method (SwamiControl *control) { SwamiControlProp *ctrlprop = SWAMI_CONTROL_PROP (control); return (ctrlprop->spec); } /* NOT locked by caller */ static void control_prop_get_value_method (SwamiControl *control, GValue *value) { SwamiControlProp *ctrlprop = SWAMI_CONTROL_PROP (control); GObject *object; GParamSpec *spec; SWAMI_LOCK_READ (ctrlprop); if (!ctrlprop->object || !ctrlprop->spec) { SWAMI_UNLOCK_READ (ctrlprop); return; } spec = ctrlprop->spec; /* no need to ref, since owner object ref'd */ object = g_object_ref (ctrlprop->object); /* ++ ref object */ SWAMI_UNLOCK_READ (ctrlprop); /* OPTME - Faster, but doesn't work for overridden properties (wrong param_id) */ /* klass->get_property (object, SWAMI_PARAM_SPEC_ID (spec), value, spec); */ g_object_get_property (object, spec->name, value); g_object_unref (object); /* -- unref object */ } /* NOT locked by caller */ static void control_prop_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiControlProp *ctrlprop = SWAMI_CONTROL_PROP (control); guint notify_handler_id, item_handler_id; GParamSpec *spec; GObject *object; SWAMI_LOCK_READ (ctrlprop); if (!ctrlprop->object || !ctrlprop->spec) { SWAMI_UNLOCK_READ (ctrlprop); return; } object = g_object_ref (ctrlprop->object); /* ++ ref object */ spec = ctrlprop->spec; /* no need to ref since we ref'd owner obj */ item_handler_id = ctrlprop->item_handler_id; notify_handler_id = ctrlprop->notify_handler_id; SWAMI_UNLOCK_READ (ctrlprop); if (item_handler_id) /* IpatchItem object? */ { /* set current thread IpatchItem origin event to prevent event loops */ g_static_private_set (&prop_notify_origin, event->origin ? event->origin : event, NULL); /* OPTME - Faster but can't use for overridden properties (wrong param_id) */ // klass->set_property (object, SWAMI_PARAM_SPEC_ID (spec), value, spec); g_object_set_property (object, spec->name, value); /* IpatchItem set property no longer active for this thread */ g_static_private_set (&prop_notify_origin, NULL, NULL); } else /* non IpatchItem object */ { /* block handler to avoid property set/notify loop (object "notify") */ g_signal_handler_block (object, notify_handler_id); /* OPTME - Faster but can't use for overridden properties (wrong param_id) */ // klass->set_property (object, SWAMI_PARAM_SPEC_ID (spec), value, spec); g_object_set_property (object, spec->name, value); g_signal_handler_unblock (object, notify_handler_id); } g_object_unref (object); /* -- unref the object */ /* propagate to outputs - FIXME: Should all controls do this? */ swami_control_transmit_event_loop (control, event); } /** * swami_control_prop_new: * @object: Object with property to control or %NULL for wildcard * @pspec: Parameter spec of property to control or %NULL for wildcard * * Create a new GObject property control. Note that swami_get_control_prop() * is likely more desireable to use, since it will return an existing control * if one already exists for the given @object and @pspec. * * If one of @object or @pspec is %NULL then it acts as a wildcard and the * control will send only (transmit changes for matching properties). If both * are %NULL however, the control has no active property to control (use * swami_get_control_prop() if a #IpatchItem entirely wildcard callback is * desired). If @object and/or @pspec is wildcard then the control will use * #SwamiEventPropChange events instead of just the property value. * * Returns: New GObject property control with a refcount of 1 which the caller * owns. */ SwamiControlProp * swami_control_prop_new (GObject *object, GParamSpec *pspec) { SwamiControlProp *ctrlprop; ctrlprop = g_object_new (SWAMI_TYPE_CONTROL_PROP, NULL); swami_control_prop_assign (ctrlprop, object, pspec, (!object || !pspec) && (object || pspec)); return (ctrlprop); } /** * swami_control_prop_assign: * @ctrlprop: Swami property control object * @object: Object containing the property to control (%NULL = wildcard) * @pspec: Parameter spec of the property of @object to control (%NULL = wildcard) * @send_events: Set to %TRUE to send/receive #SwamiEventPropChange events * for the @ctrlprop, %FALSE will use just the property value. * * Assign the object property to control for a #SwamiControlProp object. * If a property is already set for the control the new object property must * honor existing input/output connections by being writable/readable * respectively and have the same value type. * * If one of @object or @pspec is %NULL then it acts as a wildcard and the * control will send only (transmit changes for matching properties). If both * are %NULL however, the control has no active property to control. */ void swami_control_prop_assign (SwamiControlProp *ctrlprop, GObject *object, GParamSpec *pspec, gboolean send_events) { SwamiControl *control; GType value_type; g_return_if_fail (SWAMI_IS_CONTROL_PROP (ctrlprop)); g_return_if_fail (!object || pspec || IPATCH_IS_ITEM (object)); control = SWAMI_CONTROL (ctrlprop); if (pspec && !send_events) { value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); /* use derived type if GBoxed or GObject parameter */ if (value_type == G_TYPE_BOXED || value_type == G_TYPE_OBJECT) value_type = pspec->value_type; } else value_type = SWAMI_TYPE_EVENT_PROP_CHANGE; /* set control value type */ swami_control_set_value_type (control, value_type); g_return_if_fail (control->value_type == value_type); SWAMI_LOCK_WRITE (ctrlprop); /* spec must be supplied and be writable if control has input connections */ if (control->inputs && !(pspec && (pspec->flags & G_PARAM_WRITABLE))) { g_critical ("%s: Invalid writable property control object change", G_STRLOC); SWAMI_UNLOCK_WRITE (ctrlprop); return; } /* spec can be wildcard or be readable if control has output connections */ if (control->outputs && (pspec && !(pspec->flags & G_PARAM_READABLE))) { g_critical ("%s: Invalid readable property control object change", G_STRLOC); SWAMI_UNLOCK_WRITE (ctrlprop); return; } if (ctrlprop->object) { if (ctrlprop->item_handler_id) ipatch_item_prop_disconnect (ctrlprop->item_handler_id); else if (g_signal_handler_is_connected (ctrlprop->object, ctrlprop->notify_handler_id)) g_signal_handler_disconnect (ctrlprop->object, ctrlprop->notify_handler_id); ctrlprop->item_handler_id = 0; ctrlprop->notify_handler_id = 0; g_object_weak_unref (ctrlprop->object, control_prop_object_weak_notify, ctrlprop); } if (ctrlprop->spec) g_param_spec_unref (ctrlprop->spec); ctrlprop->object = object; ctrlprop->spec = pspec; ctrlprop->send_events = send_events; if (object) g_object_weak_ref (object, control_prop_object_weak_notify, ctrlprop); if (pspec) g_param_spec_ref (pspec); /* set readable/writable control flags to reflect new object property */ if (pspec && (pspec->flags & G_PARAM_WRITABLE)) control->flags |= SWAMI_CONTROL_RECVS; else control->flags &= ~SWAMI_CONTROL_RECVS; if (!pspec || (pspec->flags & G_PARAM_READABLE)) control->flags |= SWAMI_CONTROL_SENDS; else control->flags &= ~SWAMI_CONTROL_SENDS; /* IpatchItems are handled differently, wildcard is #IpatchItem only */ if (!object || !pspec || IPATCH_IS_ITEM (object)) { /* add a IpatchItem change callback for the given property */ ctrlprop->item_handler_id = ipatch_item_prop_connect ((IpatchItem *)object, pspec, send_events ? swami_control_prop_item_cb_notify_event : swami_control_prop_item_cb_notify, NULL, /* disconnect func */ ctrlprop); } else /* regular object (not IpatchItem) */ { /* connect signal to property change notify */ char *s = g_strconcat ("notify::", pspec->name, NULL); ctrlprop->notify_handler_id = g_signal_connect (object, s, G_CALLBACK (send_events ? swami_control_prop_cb_notify_event : swami_control_prop_cb_notify), ctrlprop); g_free (s); } SWAMI_UNLOCK_WRITE (ctrlprop); } /** * swami_control_prop_assign_by_name: * @ctrlprop: Swami property control object * @object: Object containing the property to control (or %NULL to un-assign) * @prop_name: Name of property to assign to property control (or %NULL for * wildcard, #IpatchItem types only, or to un-assign if @object is also %NULL) * * Like swami_control_prop_assign() but accepts a name of a property instead * of the param spec. Note also that @object may not be wildcard, contrary * to the other function. */ void swami_control_prop_assign_by_name (SwamiControlProp *ctrlprop, GObject *object, const char *prop_name) { GParamSpec *pspec = NULL; g_return_if_fail (SWAMI_IS_CONTROL_PROP (ctrlprop)); g_return_if_fail (prop_name == NULL || object != NULL); if (prop_name) { pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), prop_name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRLOC, G_OBJECT_TYPE_NAME (object), prop_name); return; } } swami_control_prop_assign (ctrlprop, object, pspec, object && !prop_name); } /* IpatchItem property change notify callback */ static void swami_control_prop_item_cb_notify (IpatchItemPropNotify *notify) { SwamiControl *ctrlprop = (SwamiControl *)(notify->user_data); SwamiControlEvent *ctrlevent, *origin; /* copy changed value to a new event */ ctrlevent = swami_control_event_new (TRUE); /* ++ ref new event */ g_value_init (&ctrlevent->value, G_VALUE_TYPE (notify->new_value)); g_value_copy (notify->new_value, &ctrlevent->value); /* IpatchItem property loop prevention, get current IpatchItem property origin event for this thread (if any) */ if ((origin = g_static_private_get (&prop_notify_origin))) swami_control_event_set_origin (ctrlevent, origin); /* transmit the new event to the controls destinations */ swami_control_transmit_event (ctrlprop, ctrlevent); swami_control_event_unref (ctrlevent); /* -- unref creator's ref */ } /* used instead of swami_control_prop_item_cb_notify() to send the value as * a SwamiEventPropChange event. */ static void swami_control_prop_item_cb_notify_event (IpatchItemPropNotify *notify) { SwamiControl *ctrlprop = (SwamiControl *)(notify->user_data); SwamiControlEvent *ctrlevent, *origin; SwamiEventPropChange *propevent; propevent = swami_event_prop_change_new (); /* create prop change event */ /* load values of property change structure */ propevent->object = (GObject *)g_object_ref (notify->item); propevent->pspec = g_param_spec_ref (notify->pspec); g_value_init (&propevent->value, G_VALUE_TYPE (notify->new_value)); g_value_copy (notify->new_value, &propevent->value); /* create the control event */ ctrlevent = swami_control_event_new (TRUE); /* ++ ref new event */ g_value_init (&ctrlevent->value, SWAMI_TYPE_EVENT_PROP_CHANGE); g_value_take_boxed (&ctrlevent->value, propevent); /* IpatchItem property loop prevention, get current IpatchItem property origin event for this thread (if any) */ if ((origin = g_static_private_get (&prop_notify_origin))) swami_control_event_set_origin (ctrlevent, origin); /* transmit the new event to the controls destinations */ swami_control_transmit_event (ctrlprop, ctrlevent); swami_control_event_unref (ctrlevent); /* -- unref creator's ref */ } /* property change notify signal callback */ static void swami_control_prop_cb_notify (GObject *object, GParamSpec *pspec, SwamiControlProp *ctrlprop) { GValue value = { 0 }; g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); /* OPTME - Faster, but doesn't work with overriden properties */ /* klass->get_property (object, SWAMI_PARAM_SPEC_ID (spec), &value, ctrlprop->spec); */ g_object_get_property (object, pspec->name, &value); swami_control_transmit_value ((SwamiControl *)ctrlprop, &value); g_value_unset (&value); } /* property change notify signal callback (sends event instead of prop value) */ static void swami_control_prop_cb_notify_event (GObject *object, GParamSpec *pspec, SwamiControlProp *ctrlprop) { SwamiEventPropChange *event; GValue value = { 0 }; /* create property change event structure and load fields */ event = swami_event_prop_change_new (); event->object = g_object_ref (object); event->pspec = g_param_spec_ref (pspec); g_value_init (&event->value, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_object_get_property (object, pspec->name, &event->value); /* init value and assign prop change event */ g_value_init (&value, SWAMI_TYPE_EVENT_PROP_CHANGE); g_value_take_boxed (&value, event); swami_control_transmit_value ((SwamiControl *)ctrlprop, &value); g_value_unset (&value); } /* catches object finalization passively */ static void control_prop_object_weak_notify (gpointer user_data, GObject *object) { SwamiControlProp *ctrlprop = SWAMI_CONTROL_PROP (user_data); ControlPropKey key; SWAMI_LOCK_WRITE (ctrlprop); key.object = object; key.pspec = ctrlprop->spec; if (ctrlprop->item_handler_id) ipatch_item_prop_disconnect (ctrlprop->item_handler_id); ctrlprop->item_handler_id = 0; ctrlprop->notify_handler_id = 0; ctrlprop->object = NULL; if (ctrlprop->spec) g_param_spec_unref (ctrlprop->spec); ctrlprop->spec = NULL; SWAMI_UNLOCK_WRITE (ctrlprop); /* remove control prop hash entry if any */ G_LOCK (control_prop_hash); g_hash_table_remove (control_prop_reverse_hash, ctrlprop); g_hash_table_remove (control_prop_hash, &key); G_UNLOCK (control_prop_hash); } swami-2.2.0/src/libswami/SwamiControlProp.h000066400000000000000000000061531361104770400206740ustar00rootroot00000000000000/* * SwamiControlProp.h - Header for Swami GObject property control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_PROP_H__ #define __SWAMI_CONTROL_PROP_H__ #include #include #include typedef struct _SwamiControlProp SwamiControlProp; typedef struct _SwamiControlPropClass SwamiControlPropClass; #define SWAMI_TYPE_CONTROL_PROP (swami_control_prop_get_type ()) #define SWAMI_CONTROL_PROP(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_PROP, \ SwamiControlProp)) #define SWAMI_CONTROL_PROP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_PROP, \ SwamiControlPropClass)) #define SWAMI_IS_CONTROL_PROP(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_PROP)) #define SWAMI_IS_CONTROL_PROP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTROL_PROP)) /* Property control object */ struct _SwamiControlProp { SwamiControl parent_instance; /* derived from SwamiControl */ GObject *object; /* object being controlled */ GParamSpec *spec; /* parameter spec of the property being controlled */ gulong notify_handler_id; /* ID of object "notify" signal handler */ guint item_handler_id; /* IpatchItem property callback handler ID */ gboolean send_events; /* when TRUE control uses SwamiEventPropChange events */ }; /* Property control class */ struct _SwamiControlPropClass { SwamiControlClass parent_class; }; SwamiControl *swami_get_control_prop (GObject *object, GParamSpec *pspec); SwamiControl *swami_get_control_prop_by_name (GObject *object, const char *name); void swami_control_prop_connect_objects (GObject *src, const char *propname1, GObject *dest, const char *propname2, guint flags); void swami_control_prop_connect_to_control (GObject *src, const char *propname, SwamiControl *dest, guint flags); void swami_control_prop_connect_from_control (SwamiControl *src, GObject *dest, const char *propname, guint flags); GType swami_control_prop_get_type (void); SwamiControlProp *swami_control_prop_new (GObject *object, GParamSpec *pspec); void swami_control_prop_assign (SwamiControlProp *ctrlprop, GObject *object, GParamSpec *pspec, gboolean send_events); void swami_control_prop_assign_by_name (SwamiControlProp *ctrlprop, GObject *object, const char *prop_name); #endif swami-2.2.0/src/libswami/SwamiControlQueue.c000066400000000000000000000112541361104770400210310ustar00rootroot00000000000000/* * SwamiControlQueue.c - Swami control event queue * For queuing SwamiControl events * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiControlQueue.h" #include "swami_priv.h" /* queue item bag */ typedef struct { SwamiControl *control; SwamiControlEvent *event; } QueueItem; GType swami_control_queue_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiControlQueueClass), NULL, NULL, (GClassInitFunc) NULL, NULL, NULL, sizeof (SwamiControlQueue), 0, (GInstanceInitFunc) NULL }; obj_type = g_type_register_static (SWAMI_TYPE_LOCK, "SwamiControlQueue", &obj_info, 0); } return (obj_type); } /** * swami_control_queue_new: * * Create a new control queue object. These are used to queue control events * which can then be run at a later time (within a GUI thread for example). * * Returns: New control queue with a ref count of 1 that the caller owns. */ SwamiControlQueue * swami_control_queue_new (void) { return (SWAMI_CONTROL_QUEUE (g_object_new (SWAMI_TYPE_CONTROL_QUEUE, NULL))); } /** * swami_control_queue_add_event: * @queue: Swami control queue object * @control: Control to queue an event for * @event: Control event to queue * * Adds a control event to a queue. Does not run queue test function this is * the responsibility of the caller (for added performance). */ void swami_control_queue_add_event (SwamiControlQueue *queue, SwamiControl *control, SwamiControlEvent *event) { QueueItem *item; g_return_if_fail (SWAMI_IS_CONTROL_QUEUE (queue)); g_return_if_fail (SWAMI_IS_CONTROL (control)); g_return_if_fail (event != NULL); item = g_slice_new (QueueItem); item->control = g_object_ref (control); /* ++ ref control */ item->event = swami_control_event_ref (event); /* ++ ref event */ /* ++ increment active reference, gets removed in swami_control_queue_run */ swami_control_event_active_ref (event); SWAMI_LOCK_WRITE (queue); queue->list = g_list_prepend (queue->list, item); if (!queue->tail) queue->tail = queue->list; SWAMI_UNLOCK_WRITE (queue); } /** * swami_control_queue_run: * @queue: Swami control queue object * * Process a control event queue by sending queued events to controls. */ void swami_control_queue_run (SwamiControlQueue *queue) { GList *list, *p, *temp; QueueItem *item; g_return_if_fail (SWAMI_IS_CONTROL_QUEUE (queue)); SWAMI_LOCK_WRITE (queue); list = queue->tail; /* take over the list */ queue->list = NULL; queue->tail = NULL; SWAMI_UNLOCK_WRITE (queue); /* process queue in reverse (since we prepended) */ p = list; while (p) { item = (QueueItem *)(p->data); swami_control_set_event_no_queue_loop (item->control, item->event); g_object_unref (item->control); /* -- unref control */ swami_control_event_active_unref (item->event); /* -- unref active ref */ swami_control_event_unref (item->event); /* -- unref event */ g_slice_free (QueueItem, item); temp = p; p = p->prev; temp = g_list_delete_link (temp, temp); /* assign to prevent gcc warning */ } } /** * swami_control_queue_set_test_func: * @queue: Control queue object * @test_func: Test function callback (function should return %TRUE to queue * an event or %FALSE to send immediately), can be %NULL in which case all * events are queued (the default). * * Set the queue test function which is called for each event added (and should * therefore be fast) to determine if the event should be queued or sent * immediately. Note that swami_control_queue_add_event() doesn't run the * test function, that is up to the caller (for increased performance). */ void swami_control_queue_set_test_func (SwamiControlQueue *queue, SwamiControlQueueTestFunc test_func) { g_return_if_fail (SWAMI_IS_CONTROL_QUEUE (queue)); queue->test_func = test_func; } swami-2.2.0/src/libswami/SwamiControlQueue.h000066400000000000000000000061151361104770400210360ustar00rootroot00000000000000/* * SwamiControlQueue.h - Swami control event queue * For queuing SwamiControl events * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_QUEUE_H__ #define __SWAMI_CONTROL_QUEUE_H__ typedef struct _SwamiControlQueue SwamiControlQueue; typedef struct _SwamiControlQueueClass SwamiControlQueueClass; #include #include #include #include #include #define SWAMI_TYPE_CONTROL_QUEUE (swami_control_queue_get_type ()) #define SWAMI_CONTROL_QUEUE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_QUEUE, \ SwamiControlQueue)) #define SWAMI_CONTROL_QUEUE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_QUEUE, \ SwamiControlQueueClass)) #define SWAMI_IS_CONTROL_QUEUE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_QUEUE)) #define SWAMI_IS_CONTROL_QUEUE_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE ((obj), SWAMI_TYPE_CONTROL_QUEUE)) /** * SwamiControlQueueTestFunc: * @queue: The queue object * @control: The control * @event: The control event * * A callback function type used to test if an event should be added to a queue. * An example of its usage would be a GUI queue which could test to see if the * event is being sent within the GUI thread or not. * * Returns: Function should return %TRUE if event should be queued, %FALSE to * send event immediately. */ typedef gboolean (* SwamiControlQueueTestFunc)(SwamiControlQueue *queue, SwamiControl *control, SwamiControlEvent *event); /* control event queue */ struct _SwamiControlQueue { SwamiLock parent_instance; /* derived from SwamiLock */ SwamiControlQueueTestFunc test_func; GList *list; /* list of queued events (struct in SwamiControlQueue.c) */ GList *tail; /* tail of the list */ }; /* control value change queue class */ struct _SwamiControlQueueClass { SwamiLockClass parent_class; }; GType swami_control_queue_get_type (void); SwamiControlQueue *swami_control_queue_new (void); void swami_control_queue_add_event (SwamiControlQueue *queue, SwamiControl *control, SwamiControlEvent *event); void swami_control_queue_run (SwamiControlQueue *queue); void swami_control_queue_set_test_func (SwamiControlQueue *queue, SwamiControlQueueTestFunc test_func); #endif swami-2.2.0/src/libswami/SwamiControlValue.c000066400000000000000000000161741361104770400210270ustar00rootroot00000000000000/* * SwamiControlValue.c - Swami GValue control object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiControlValue.h" #include "SwamiControl.h" #include "SwamiLog.h" #include "swami_priv.h" #include "util.h" static void swami_control_value_class_init (SwamiControlValueClass *klass); static void swami_control_value_init (SwamiControlValue *ctrlvalue); static void swami_control_value_finalize (GObject *object); static GParamSpec *control_value_get_spec_method (SwamiControl *control); static gboolean control_value_set_spec_method (SwamiControl *control, GParamSpec *pspec); static void control_value_get_value_method (SwamiControl *control, GValue *value); static void control_value_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static GObjectClass *parent_class = NULL; GType swami_control_value_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiControlValueClass), NULL, NULL, (GClassInitFunc) swami_control_value_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiControlValue), 0, (GInstanceInitFunc) swami_control_value_init }; otype = g_type_register_static (SWAMI_TYPE_CONTROL, "SwamiControlValue", &type_info, 0); } return (otype); } static void swami_control_value_class_init (SwamiControlValueClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_control_value_finalize; control_class->get_spec = control_value_get_spec_method; control_class->set_spec = control_value_set_spec_method; control_class->get_value = control_value_get_value_method; control_class->set_value = control_value_set_value_method; } static void swami_control_value_init (SwamiControlValue *ctrlvalue) { swami_control_set_flags (SWAMI_CONTROL (ctrlvalue), SWAMI_CONTROL_SENDRECV); } static void swami_control_value_finalize (GObject *object) { SwamiControlValue *ctrlvalue = SWAMI_CONTROL_VALUE (object); if (ctrlvalue->destroy && ctrlvalue->value) (*ctrlvalue->destroy)(ctrlvalue->value); if (ctrlvalue->pspec) g_param_spec_unref (ctrlvalue->pspec); parent_class->finalize (object); } /* control is locked by caller */ static GParamSpec * control_value_get_spec_method (SwamiControl *control) { SwamiControlValue *ctrlvalue = SWAMI_CONTROL_VALUE (control); return (ctrlvalue->pspec); } /* control is locked by caller */ static gboolean control_value_set_spec_method (SwamiControl *control, GParamSpec *pspec) { SwamiControlValue *ctrlvalue = SWAMI_CONTROL_VALUE (control); if (ctrlvalue->pspec) g_param_spec_unref (ctrlvalue->pspec); ctrlvalue->pspec = g_param_spec_ref (pspec); g_param_spec_sink (pspec); return (TRUE); } static void control_value_get_value_method (SwamiControl *control, GValue *value) { SwamiControlValue *ctrlvalue = SWAMI_CONTROL_VALUE (control); g_value_copy (ctrlvalue->value, value); } static void control_value_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiControlValue *ctrlvalue = SWAMI_CONTROL_VALUE (control); g_value_copy (value, ctrlvalue->value); } /** * swami_control_value_new: * * Create a new GValue control. * * Returns: New GValue control with a refcount of 1 which the caller owns. */ SwamiControlValue * swami_control_value_new (void) { return (SWAMI_CONTROL_VALUE (g_object_new (SWAMI_TYPE_CONTROL_VALUE, NULL))); } /** * swami_control_value_assign_value: * @ctrlvalue: Swami GValue control object * @value: Value to be controlled * @destroy: A function to destroy @value or %NULL * * Assigns a GValue to be controlled by a Swami GValue control object. * If @destroy is set it will be called on the @value when it is no longer * being used. The @ctrlvalue should already have a parameter specification. * If the @value type is the same as the GParamSpec value type it will be * used as is otherwise it will be initialized to the parameter spec type. */ void swami_control_value_assign_value (SwamiControlValue *ctrlvalue, GValue *value, GDestroyNotify destroy) { GValue *destroy_value = NULL; GDestroyNotify destroy_func = NULL; g_return_if_fail (SWAMI_IS_CONTROL_VALUE (ctrlvalue)); g_return_if_fail (value != NULL); SWAMI_LOCK_WRITE (ctrlvalue); if (swami_log_if_fail (ctrlvalue->pspec != NULL)) { SWAMI_UNLOCK_WRITE (ctrlvalue); return; } if (ctrlvalue->destroy && ctrlvalue->value) { destroy_value = ctrlvalue->value; destroy_func = ctrlvalue->destroy; } ctrlvalue->value = value; ctrlvalue->destroy = destroy; /* ensure existing value is set to the type specified by spec */ if (G_VALUE_TYPE (ctrlvalue->value) != G_PARAM_SPEC_VALUE_TYPE (ctrlvalue->pspec)) { g_value_unset (ctrlvalue->value); g_value_init (ctrlvalue->value, G_PARAM_SPEC_VALUE_TYPE (ctrlvalue->pspec)); } SWAMI_UNLOCK_WRITE (ctrlvalue); /* destroy value outside of lock (if any) */ if (destroy_value && destroy_func) (*destroy_func)(destroy_value); } /** * swami_control_value_alloc_value: * @ctrlvalue: Swami GValue control object * * Allocate a GValue and assign it to the @ctrlvalue object. See * swami_control_value_assign_value() to assign an existing GValue to * a Swami GValue control object. The @ctrlvalue should already have an * assigned parameter spec. */ void swami_control_value_alloc_value (SwamiControlValue *ctrlvalue) { GValue *destroy_value = NULL; GDestroyNotify destroy_func; GValue *value; g_return_if_fail (SWAMI_IS_CONTROL_VALUE (ctrlvalue)); SWAMI_LOCK_WRITE (ctrlvalue); if (swami_log_if_fail (ctrlvalue->pspec != NULL)) { SWAMI_UNLOCK_WRITE (ctrlvalue); return; } if (ctrlvalue->destroy && ctrlvalue->value) { destroy_value = ctrlvalue->value; destroy_func = ctrlvalue->destroy; } value = swami_util_new_value (); g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (ctrlvalue->pspec)); ctrlvalue->value = value; ctrlvalue->destroy = (GDestroyNotify)swami_util_free_value; SWAMI_UNLOCK_WRITE (ctrlvalue); /* destroy value outside of lock (if any) */ if (destroy_value) (*destroy_func)(destroy_value); } swami-2.2.0/src/libswami/SwamiControlValue.h000066400000000000000000000045241361104770400210300ustar00rootroot00000000000000/* * SwamiControlValue.h - Header for Swami GValue control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_VALUE_H__ #define __SWAMI_CONTROL_VALUE_H__ #include #include #include typedef struct _SwamiControlValue SwamiControlValue; typedef struct _SwamiControlValueClass SwamiControlValueClass; #define SWAMI_TYPE_CONTROL_VALUE (swami_control_value_get_type ()) #define SWAMI_CONTROL_VALUE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL_VALUE, \ SwamiControlValue)) #define SWAMI_CONTROL_VALUE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL_VALUE, \ SwamiControlValueClass)) #define SWAMI_IS_CONTROL_VALUE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL_VALUE)) #define SWAMI_IS_CONTROL_VALUE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_CONTROL_VALUE)) /* Value control object */ struct _SwamiControlValue { SwamiControl parent_instance; /* derived from SwamiControl */ GValue *value; /* value being controlled */ GDestroyNotify destroy; /* function to call to destroy value or NULL */ GParamSpec *pspec; /* created param spec for controlled value */ }; /* Value control class */ struct _SwamiControlValueClass { SwamiControlClass parent_class; }; GType swami_control_value_get_type (void); SwamiControlValue *swami_control_value_new (void); void swami_control_value_assign_value (SwamiControlValue *ctrlvalue, GValue *value, GDestroyNotify destroy); void swami_control_value_alloc_value (SwamiControlValue *ctrlvalue); #endif swami-2.2.0/src/libswami/SwamiEvent_ipatch.c000066400000000000000000000117121361104770400210140ustar00rootroot00000000000000/* * SwamiEvent_ipatch.c - libInstPatch SwamiControl event types * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiEvent_ipatch.h" #include "swami_priv.h" GType swami_event_item_add_get_type (void) { static GType item_type = 0; if (!item_type) { item_type = g_boxed_type_register_static ("SwamiEventItemAdd", (GBoxedCopyFunc) swami_event_item_add_copy, (GBoxedFreeFunc) swami_event_item_add_free); } return (item_type); } GType swami_event_item_remove_get_type (void) { static GType item_type = 0; if (!item_type) { item_type = g_boxed_type_register_static ("SwamiEventItemRemove", (GBoxedCopyFunc) swami_event_item_remove_copy, (GBoxedFreeFunc) swami_event_item_remove_free); } return (item_type); } GType swami_event_prop_change_get_type (void) { static GType item_type = 0; if (!item_type) { item_type = g_boxed_type_register_static ("SwamiEventPropChange", (GBoxedCopyFunc) swami_event_prop_change_copy, (GBoxedFreeFunc) swami_event_prop_change_free); } return (item_type); } /** * swami_event_item_add_copy: * @item_add: Patch item add event to copy * * Copies a patch item add event (an IpatchItem pointer really). * * Returns: New duplicated patch item add event. */ SwamiEventItemAdd * swami_event_item_add_copy (SwamiEventItemAdd *item_add) { return (g_object_ref (item_add)); } /** * swami_event_item_add_free: * @item_add: Patch item add event to free * * Free a patch item add event (an IpatchItem pointer really). */ void swami_event_item_add_free (SwamiEventItemAdd *item_add) { g_object_unref (item_add); } /** * swami_event_item_remove_new: * * Allocate a new patch item remove event structure. * * Returns: Newly allocated patch item remove event structure. */ SwamiEventItemRemove * swami_event_item_remove_new (void) { return (g_slice_new0 (SwamiEventItemRemove)); } /** * swami_event_item_remove_copy: * @item_remove: Patch item remove event to copy * * Copies a patch item remove event structure. * * Returns: New duplicated patch item remove event structure. */ SwamiEventItemRemove * swami_event_item_remove_copy (SwamiEventItemRemove *item_remove) { SwamiEventItemRemove *new_event; new_event = g_slice_new (SwamiEventItemRemove); new_event->item = g_object_ref (item_remove->item); new_event->parent = g_object_ref (item_remove->parent); return (new_event); } /** * swami_event_item_remove_free: * @item_remove: Patch item remove event to free * * Free a patch item remove event structure. */ void swami_event_item_remove_free (SwamiEventItemRemove *item_remove) { g_object_unref (item_remove->item); g_object_unref (item_remove->parent); g_slice_free (SwamiEventItemRemove, item_remove); } /** * swami_event_prop_change_new: * * Allocate a new patch property change event structure. * * Returns: Newly allocated patch property change event structure. */ SwamiEventPropChange * swami_event_prop_change_new (void) { return (g_slice_new0 (SwamiEventPropChange)); } /** * swami_event_prop_change_copy: * @prop_change: Patch property change event to copy * * Copies a patch property change event structure. * * Returns: New duplicated patch property change event structure. */ SwamiEventPropChange * swami_event_prop_change_copy (SwamiEventPropChange *prop_change) { SwamiEventPropChange *new_event; new_event = g_slice_new (SwamiEventPropChange); new_event->object = g_object_ref (prop_change->object); new_event->pspec = g_param_spec_ref (prop_change->pspec); if (G_IS_VALUE (&prop_change->value)) g_value_copy (&prop_change->value, &new_event->value); else memset (&prop_change->value, 0, sizeof (GValue)); return (new_event); } /** * swami_event_prop_change_free: * @prop_change: Patch property change event to free * * Free a patch property change event structure. */ void swami_event_prop_change_free (SwamiEventPropChange *prop_change) { g_object_unref (prop_change->object); g_param_spec_unref (prop_change->pspec); if (G_IS_VALUE (&prop_change->value)) g_value_unset (&prop_change->value); g_slice_free (SwamiEventPropChange, prop_change); } swami-2.2.0/src/libswami/SwamiEvent_ipatch.h000066400000000000000000000053411361104770400210220ustar00rootroot00000000000000/* * SwamiEvent_ipatch.h - libInstPatch SwamiControl event types * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_CONTROL_IPATCH_H__ #define __SWAMI_CONTROL_IPATCH_H__ #include /* libInstPatch event box types */ #define SWAMI_TYPE_EVENT_ITEM_ADD (swami_event_item_add_get_type ()) #define SWAMI_VALUE_HOLDS_EVENT_ITEM_ADD(value) \ (G_TYPE_CHECK_VALUE_TYPE ((value), SWAMI_TYPE_EVENT_ITEM_ADD)) #define SWAMI_TYPE_EVENT_ITEM_REMOVE (swami_event_item_remove_get_type ()) #define SWAMI_VALUE_HOLDS_EVENT_ITEM_REMOVE(value) \ (G_TYPE_CHECK_VALUE_TYPE ((value), SWAMI_TYPE_EVENT_ITEM_REMOVE)) #define SWAMI_TYPE_EVENT_PROP_CHANGE (swami_event_prop_change_get_type ()) #define SWAMI_VALUE_HOLDS_EVENT_PROP_CHANGE(value) \ (G_TYPE_CHECK_VALUE_TYPE ((value), SWAMI_TYPE_EVENT_PROP_CHANGE)) /* item add just uses IpatchItem pointer */ typedef IpatchItem SwamiEventItemAdd; typedef struct _SwamiEventItemRemove { IpatchItem *item; /* item removed or to be removed */ IpatchItem *parent; /* parent of item */ } SwamiEventItemRemove; typedef struct _SwamiEventPropChange { GObject *object; /* object whose property changed */ GParamSpec *pspec; /* property parameter spec */ GValue value; /* new value */ } SwamiEventPropChange; GType swami_event_item_add_get_type (void); GType swami_event_item_remove_get_type (void); GType swami_event_prop_change_get_type (void); SwamiEventItemAdd *swami_event_item_add_copy (SwamiEventItemAdd *item_add); void swami_event_item_add_free (SwamiEventItemAdd *item_add); SwamiEventItemRemove *swami_event_item_remove_new (void); SwamiEventItemRemove * swami_event_item_remove_copy (SwamiEventItemRemove *item_remove); void swami_event_item_remove_free (SwamiEventItemRemove *item_remove); SwamiEventPropChange *swami_event_prop_change_new (void); SwamiEventPropChange * swami_event_prop_change_copy (SwamiEventPropChange *prop_change); void swami_event_prop_change_free (SwamiEventPropChange *prop_change); #endif swami-2.2.0/src/libswami/SwamiLock.c000066400000000000000000000075331361104770400173010ustar00rootroot00000000000000/* * SwamiLock.c - Base Swami multi-thread locked object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include "SwamiLock.h" /* --- private function prototypes --- */ static void swami_lock_class_init (SwamiLockClass *klass); static void swami_lock_init (SwamiLock *lock); static void swami_lock_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (SwamiLock, swami_lock, G_TYPE_OBJECT); /* --- functions --- */ static void swami_lock_class_init (SwamiLockClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = swami_lock_finalize; } static void swami_lock_init (SwamiLock *lock) { g_static_rec_mutex_init (&lock->mutex); } static void swami_lock_finalize (GObject *object) { SwamiLock *lock = SWAMI_LOCK (object); g_static_rec_mutex_free (&lock->mutex); if (G_OBJECT_CLASS (swami_lock_parent_class)->finalize) G_OBJECT_CLASS (swami_lock_parent_class)->finalize (object); } /** * swami_lock_set_atomic: * @lock: SwamiLock derived object to set properties of * @first_property_name: Name of first property * @Varargs: Variable list of arguments that should start with the value to * set @first_property_name to, followed by property name/value pairs. List is * terminated with a %NULL property name. * * Sets properties on a Swami lock item atomically (i.e. item is * multi-thread locked while all properties are set). This avoids * critical parameter sync problems when multiple threads are * accessing the same item. See g_object_set() for more information on * setting properties. This function is rarely needed, only useful for cases * where multiple properties depend on each other. */ void swami_lock_set_atomic (gpointer lock, const char *first_property_name, ...) { va_list args; g_return_if_fail (SWAMI_IS_LOCK (lock)); va_start (args, first_property_name); SWAMI_LOCK_WRITE (lock); g_object_set_valist (G_OBJECT (lock), first_property_name, args); SWAMI_UNLOCK_WRITE (lock); va_end (args); } /** * swami_lock_get_atomic: * @lock: SwamiLock derived object to get properties from * @first_property_name: Name of first property * @Varargs: Variable list of arguments that should start with a * pointer to store the value from @first_property_name, followed by * property name/value pointer pairs. List is terminated with a %NULL * property name. * * Gets properties from a Swami lock item atomically (i.e. item is * multi-thread locked while all properties are retrieved). This * avoids critical parameter sync problems when multiple threads are * accessing the same item. See g_object_get() for more information on * getting properties. This function is rarely needed, only useful when * multiple properties depend on each other. */ void swami_lock_get_atomic (gpointer lock, const char *first_property_name, ...) { va_list args; g_return_if_fail (SWAMI_IS_LOCK (lock)); va_start (args, first_property_name); SWAMI_LOCK_WRITE (lock); g_object_get_valist (G_OBJECT (lock), first_property_name, args); SWAMI_UNLOCK_WRITE (lock); va_end (args); } swami-2.2.0/src/libswami/SwamiLock.h000066400000000000000000000043641361104770400173050ustar00rootroot00000000000000/* * SwamiLock.h - Header for Swami multi-threaded locked base object class * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_LOCK_H__ #define __SWAMI_LOCK_H__ #include #include typedef struct _SwamiLock SwamiLock; typedef struct _SwamiLockClass SwamiLockClass; #define SWAMI_TYPE_LOCK (swami_lock_get_type ()) #define SWAMI_LOCK(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_LOCK, SwamiLock)) #define SWAMI_LOCK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_LOCK, SwamiLockClass)) #define SWAMI_IS_LOCK(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_LOCK)) #define SWAMI_IS_LOCK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_LOCK)) struct _SwamiLock { GObject parent_instance; GStaticRecMutex mutex; }; struct _SwamiLockClass { GObjectClass parent_class; }; /* Multi-thread locking macros. For now there is no distinction between write and read locking since GStaticRWLock is not recursive. */ #define SWAMI_LOCK_WRITE(lock) \ g_static_rec_mutex_lock (&((SwamiLock *)(lock))->mutex) #define SWAMI_UNLOCK_WRITE(lock) \ g_static_rec_mutex_unlock (&((SwamiLock *)(lock))->mutex) #define SWAMI_LOCK_READ(lock) SWAMI_LOCK_WRITE(lock) #define SWAMI_UNLOCK_READ(lock) SWAMI_UNLOCK_WRITE(lock) GType swami_lock_get_type (void); void swami_lock_set_atomic (gpointer lock, const char *first_property_name, ...); void swami_lock_get_atomic (gpointer lock, const char *first_property_name, ...); #endif swami-2.2.0/src/libswami/SwamiLog.c000066400000000000000000000034641361104770400171310ustar00rootroot00000000000000/* * SwamiLog.c - Message logging and debugging functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net */ #include #include #include GQuark swami_error_quark (void) { static GQuark q = 0; if (q == 0) q = g_quark_from_static_string ("libswami-error-quark"); return (q); } int _swami_ret_g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...) { va_list args; va_start (args, format); g_logv (log_domain, log_level, format, args); va_end (args); return (TRUE); } void _swami_pretty_log_handler (GLogLevelFlags level, char *file, char *function, int line, char *format, ...) { va_list args; char *s, *s2; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); s2 = g_strdup_printf ("file %s: line %d (%s): %s", file, line, function, s); g_free (s); g_log (NULL, level, "%s", s2); g_free (s2); } swami-2.2.0/src/libswami/SwamiLog.h000066400000000000000000000056371361104770400171420ustar00rootroot00000000000000/* * SwamiLog.h - Message logging and debugging functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_LOG_H__ #define __SWAMI_LOG_H__ #include /* Swami domain for g_set_error */ #define SWAMI_ERROR swami_error_quark() typedef enum { SWAMI_ERROR_FAIL, /* general failure */ SWAMI_ERROR_INVALID, /* invalid parameter/setting/etc */ SWAMI_ERROR_CANCELED, /* an operation was canceled (SwamiLoopFinder) */ SWAMI_ERROR_UNSUPPORTED, /* an unsupported feature or unhandled operation */ SWAMI_ERROR_IO /* I/O related error */ } SwamiError; GQuark swami_error_quark (void); #ifdef __GNUC__ #define swami_log_if_fail(expr) (!(expr) && \ _swami_ret_g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ "file %s: line %d (%s): assertion `%s' failed.", \ __FILE__, __LINE__, __PRETTY_FUNCTION__, \ #expr)) #else /* !GNUC */ #define swami_log_if_fail(expr) (!(expr) && \ _swami_ret_g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ "file %s: line %d: assertion `%s' failed.", \ __FILE__, __LINE__, \ #expr)) #endif int _swami_ret_g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...); #ifndef _WIN32 extern void _swami_pretty_log_handler (GLogLevelFlags flags, char *file, char *function, int line, char *format, ...); #ifdef SWAMI_DEBUG_ENABLED #define SWAMI_DEBUG(format, args...) \ _swami_pretty_log_handler (G_LOG_LEVEL_DEBUG, \ __FILE__, __PRETTY_FUNCTION__, __LINE__, \ format, ## args); #else #define SWAMI_DEBUG(format, args...) #endif #define SWAMI_INFO(format, args...) \ _swami_pretty_log_handler (G_LOG_LEVEL_INFO, \ __FILE__, __PRETTY_FUNCTION__, __LINE__, \ format, ## args); #define SWAMI_PARAM_ERROR(param) \ _swami_pretty_log_handler (G_LOG_LEVEL_CRITICAL, \ __FILE__, __PRETTY_FUNCTION__, __LINE__, \ "Invalid function parameter value for '%s'.", \ param); #define SWAMI_CRITICAL(format, args...) \ _swami_pretty_log_handler (G_LOG_LEVEL_CRITICAL, \ __FILE__, __PRETTY_FUNCTION__, __LINE__, \ format, ## args); #endif #endif /* __SWAMI_LOG_H__ */ swami-2.2.0/src/libswami/SwamiLoopFinder.c000066400000000000000000000756301361104770400204550ustar00rootroot00000000000000/* * SwamiLoopFinder.c - Sample loop finder object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /* * Thanks to Luis Garrido for the original loop finder algorithm code and his * interest in creating this feature for Swami. * * The new loop finder algorithm was re-written and is described below: * * A multiplication analysis "window" array of floats is created which is * analysis_size in length and contains a peak value in the center of the * window, each point away from the center is half the value of its closer * neighbor and all values in the window add up to 0.5. 0.5 was chosen * because the maximum difference between two sample points is 2 (1 - -1 = 2), * so this results in a maximum "quality" value of 1.0 (worst quality). * * The two search windows are exhaustively compared with two loops, one * embedded in the other. For each loop start/end candidate a quality * factor is calculated. The quality value is calculated from the sum of * the absolute differences of the sample points surrounding the loop * points (analysis window size) multiplied individually by values in the * analysis_window[] array. */ #include #include #include "SwamiLoopFinder.h" #include "SwamiLog.h" #include "i18n.h" #define DEFAULT_MAX_RESULTS 200 #define MAX_MAX_RESULTS 4000 #define DEFAULT_ANALYSIS_WINDOW 17 #define DEFAULT_MIN_LOOP_SIZE 10 #define DEFAULT_GROUP_POS_DIFF 20 #define DEFAULT_GROUP_SIZE_DIFF 5 /* Sample format used by loop finder */ #define SAMPLE_FORMAT IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_ENDIAN_HOST enum { PROP_0, PROP_RESULTS, /* SwamiLoopResults object */ PROP_ACTIVE, /* TRUE if find is in progress */ PROP_CANCEL, /* set to TRUE to cancel in progress find */ PROP_PROGRESS, /* current progress of find (0.0 - 1.0) */ PROP_SAMPLE, /* sample data assigned to loop finder */ PROP_MAX_RESULTS, /* max results to return */ PROP_ANALYSIS_WINDOW, /* size in samples of analysis window */ PROP_MIN_LOOP_SIZE, /* minimum loop size */ PROP_WINDOW1_START, /* window1 start position in samples */ PROP_WINDOW1_END, /* window1 end position in samples */ PROP_WINDOW2_START, /* window2 start position in samples */ PROP_WINDOW2_END, /* window2 end position in samples */ PROP_GROUP_POS_DIFF, /* min pos diff of loops for separate groups */ PROP_GROUP_SIZE_DIFF, /* min size diff of loops for separate groups */ PROP_EXEC_TIME /* execution time in milliseconds of find */ }; static void swami_loop_finder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swami_loop_finder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swami_loop_finder_init (SwamiLoopFinder *editor); static void swami_loop_finder_finalize (GObject *object); static void swami_loop_finder_real_set_sample (SwamiLoopFinder *finder, IpatchSample *sample); static void find_loop (SwamiLoopFinder *finder, SwamiLoopMatch *matches); G_DEFINE_TYPE (SwamiLoopFinder, swami_loop_finder, SWAMI_TYPE_LOCK); static void swami_loop_finder_class_init (SwamiLoopFinderClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = swami_loop_finder_set_property; obj_class->get_property = swami_loop_finder_get_property; obj_class->finalize = swami_loop_finder_finalize; g_object_class_install_property (obj_class, PROP_RESULTS, g_param_spec_object ("results", _("Results"), _("Loop results object"), SWAMI_TYPE_LOOP_RESULTS, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_ACTIVE, g_param_spec_boolean ("active", _("Active"), _("Active"), FALSE, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_CANCEL, g_param_spec_boolean ("cancel", _("Cancel"), _("Cancel"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PROGRESS, g_param_spec_float ("progress", _("Progress"), _("Progress"), 0.0, 1.0, 0.0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE, g_param_spec_object ("sample", _("Sample"), _("Sample data object"), IPATCH_TYPE_SAMPLE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MAX_RESULTS, g_param_spec_int ("max-results", _("Max results"), _("Max results"), 1, MAX_MAX_RESULTS, DEFAULT_MAX_RESULTS, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ANALYSIS_WINDOW, g_param_spec_int ("analysis-window", _("Analysis window"), _("Size of analysis window"), 1, G_MAXINT, DEFAULT_ANALYSIS_WINDOW, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MIN_LOOP_SIZE, g_param_spec_int ("min-loop-size", _("Min loop size"), _("Minimum size of matching loops"), 1, G_MAXINT, DEFAULT_MIN_LOOP_SIZE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WINDOW1_START, g_param_spec_int ("window1-start", _("First window start position"), _("First window start position"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WINDOW1_END, g_param_spec_int ("window1-end", _("First window end position"), _("First window end position"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WINDOW2_START, g_param_spec_int ("window2-start", _("Second window start position"), _("Second window start position"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WINDOW2_END, g_param_spec_int ("window2-end", _("Second window end position"), _("Second window end position"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_GROUP_POS_DIFF, g_param_spec_int ("group-pos-diff", _("Group pos diff"), _("Min difference of loop position of separate groups"), 0, G_MAXINT, DEFAULT_GROUP_POS_DIFF, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_GROUP_SIZE_DIFF, g_param_spec_int ("group-size-diff", _("Group size diff"), _("Min difference of loop size of separate groups"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_EXEC_TIME, g_param_spec_uint ("exec-time", _("Exec time"), _("Execution time in milliseconds"), 0, G_MAXUINT, 0, G_PARAM_READABLE)); } static void swami_loop_finder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiLoopFinder *finder = SWAMI_LOOP_FINDER (object); GObject *obj; switch (property_id) { case PROP_CANCEL: if (g_value_get_boolean (value)) finder->cancel = TRUE; break; case PROP_PROGRESS: finder->progress = g_value_get_float (value); break; case PROP_SAMPLE: obj = g_value_get_object (value); swami_loop_finder_real_set_sample (finder, obj ? IPATCH_SAMPLE (obj) : NULL); break; case PROP_MAX_RESULTS: finder->max_results = g_value_get_int (value); break; case PROP_ANALYSIS_WINDOW: finder->analysis_window = g_value_get_int (value); break; case PROP_MIN_LOOP_SIZE: finder->min_loop_size = g_value_get_int (value); break; case PROP_WINDOW1_START: finder->window1_start = g_value_get_int (value); break; case PROP_WINDOW1_END: finder->window1_end = g_value_get_int (value); break; case PROP_WINDOW2_START: finder->window2_start = g_value_get_int (value); break; case PROP_WINDOW2_END: finder->window2_end = g_value_get_int (value); break; case PROP_GROUP_POS_DIFF: finder->group_pos_diff = g_value_get_int (value); break; case PROP_GROUP_SIZE_DIFF: finder->group_size_diff = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_loop_finder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiLoopFinder *finder = SWAMI_LOOP_FINDER (object); switch (property_id) { case PROP_RESULTS: SWAMI_LOCK_READ (finder); g_value_set_object (value, finder->results); SWAMI_UNLOCK_READ (finder); break; case PROP_ACTIVE: g_value_set_boolean (value, finder->active); break; case PROP_CANCEL: g_value_set_boolean (value, finder->cancel); break; case PROP_PROGRESS: g_value_set_float (value, finder->progress); break; case PROP_SAMPLE: g_value_set_object (value, finder->sample); break; case PROP_MAX_RESULTS: g_value_set_int (value, finder->max_results); break; case PROP_ANALYSIS_WINDOW: g_value_set_int (value, finder->analysis_window); break; case PROP_MIN_LOOP_SIZE: g_value_set_int (value, finder->min_loop_size); break; case PROP_WINDOW1_START: g_value_set_int (value, finder->window1_start); break; case PROP_WINDOW1_END: g_value_set_int (value, finder->window1_end); break; case PROP_WINDOW2_START: g_value_set_int (value, finder->window2_start); break; case PROP_WINDOW2_END: g_value_set_int (value, finder->window2_end); break; case PROP_EXEC_TIME: g_value_set_uint (value, finder->exectime); break; case PROP_GROUP_POS_DIFF: g_value_set_int (value, finder->group_pos_diff); break; case PROP_GROUP_SIZE_DIFF: g_value_set_int (value, finder->group_size_diff); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_loop_finder_init (SwamiLoopFinder *finder) { finder->max_results = DEFAULT_MAX_RESULTS; finder->analysis_window = DEFAULT_ANALYSIS_WINDOW; finder->min_loop_size = DEFAULT_MIN_LOOP_SIZE; finder->group_pos_diff = DEFAULT_GROUP_POS_DIFF; finder->group_size_diff = DEFAULT_GROUP_SIZE_DIFF; } static void swami_loop_finder_finalize (GObject *object) { SwamiLoopFinder *finder = SWAMI_LOOP_FINDER (object); if (finder->sample) g_object_unref (finder->sample); if (finder->sample_handle) /* Close and free old cached sample handle if any */ { ipatch_sample_handle_close (finder->sample_handle); g_slice_free (IpatchSampleHandle, finder->sample_handle); } if (finder->results) g_object_unref (finder->results); if (G_OBJECT_CLASS (swami_loop_finder_parent_class)->finalize) G_OBJECT_CLASS (swami_loop_finder_parent_class)->finalize (object); } /** * swami_loop_finder_new: * * Create a new sample loop finder object. * * Returns: New object of type #SwamiLoopFinder */ SwamiLoopFinder * swami_loop_finder_new (void) { return (SWAMI_LOOP_FINDER (g_object_new (SWAMI_TYPE_LOOP_FINDER, NULL))); } static void swami_loop_finder_real_set_sample (SwamiLoopFinder *finder, IpatchSample *sample) { IpatchSampleData *sampledata; IpatchSampleHandle *old_handle; gpointer old_sample; guint new_sample_size = 0; if (sample == finder->sample) return; if (sample) { g_object_get (sample, "sample-data", &sampledata, NULL); /* ++ ref sample data */ if (sampledata) { g_object_get (sample, "sample-size", &new_sample_size, NULL); g_object_ref (sample); /* ++ ref for loop finder */ g_object_unref (sampledata); /* -- unref sample data */ } else sample = NULL; /* no sample-data property? - No dice */ } SWAMI_LOCK_WRITE (finder); old_sample = finder->sample; old_handle = finder->sample_handle; finder->sample = sample; finder->sample_handle = NULL; finder->sample_size = new_sample_size; finder->sample_data = NULL; SWAMI_UNLOCK_WRITE (finder); if (sample) swami_loop_finder_full_search (finder); if (old_sample) g_object_unref (old_sample); /* -- unref old sample (if any) */ if (old_handle) /* Close and free old cached sample handle if any */ { ipatch_sample_handle_close (old_handle); g_slice_free (IpatchSampleHandle, old_handle); } } /** * swami_loop_finder_full_search: * @finder: Loop finder object * * Configures a loop finder to do a full loop search of the assigned sample. * Note that a sample must have already been assigned and the "analysis-window" * and "min-loop-size" parameters should be valid for it. This means: * (finder->analysis_window <= finder->sample_size) && * (finder->sample_size - finder->analysis_window) >= finder->min_loop_size. */ void swami_loop_finder_full_search (SwamiLoopFinder *finder) { int max_loop_size; guint analysis_window; g_return_if_fail (SWAMI_IS_LOOP_FINDER (finder)); g_return_if_fail (finder->sample != NULL); analysis_window = finder->analysis_window; max_loop_size = finder->sample_size - analysis_window; /* silently fail if analysis window or loop size is out of wack */ if (analysis_window > finder->sample_size || max_loop_size < finder->min_loop_size) return; finder->window1_start = finder->analysis_window / 2; finder->window1_end = finder->window1_start + finder->sample_size - finder->analysis_window; finder->window2_start = finder->window1_start; finder->window2_end = finder->window1_end; g_object_notify (G_OBJECT (finder), "window1-start"); g_object_notify (G_OBJECT (finder), "window1-end"); g_object_notify (G_OBJECT (finder), "window2-start"); g_object_notify (G_OBJECT (finder), "window2-end"); } /** * swami_loop_finder_verify_params: * @finder: Loop finder object * @nudge: %TRUE to nudge start and end loop search windows into compliance * @err: Location to store error message or %NULL to ignore * * Verify a loop finder's parameters. If the @nudge parameter is %TRUE then * corrections will be made to start and end loop search windows if necessary * (changed to fit within sample and analysis window and swapped if * windows are backwards). * * Note that the other parameters will not be corrected and will still cause * errors if wacked. Nudged values are only changed if %TRUE is returned. * * Returns: %TRUE on success, %FALSE if there is a problem with the loop * finder parameters (error may be stored in @err). */ gboolean swami_loop_finder_verify_params (SwamiLoopFinder *finder, gboolean nudge, GError **err) { int sample_size; int halfwin, ohalfwin; int win1start, win1end; /* stores window1 start/end */ int win2start, win2end; /* stores window2 start/end */ int tmp; g_return_val_if_fail (SWAMI_IS_LOOP_FINDER (finder), FALSE); g_return_val_if_fail (!err || !*err, FALSE); /* Swap start/ends as needed */ win1start = MIN (finder->window1_start, finder->window1_end); win1end = MAX (finder->window1_start, finder->window1_end); win2start = MIN (finder->window2_start, finder->window2_end); win2end = MAX (finder->window2_start, finder->window2_end); /* Swap ranges if needed, so that window1 is start of loop */ if (win1start > win2start) { tmp = win1start; win1start = win2start; win2start = tmp; tmp = win1end; win1end = win2end; win2end = tmp; } /* calculate first and second half of analysis window */ sample_size = finder->sample_size; halfwin = finder->analysis_window / 2; ohalfwin = finder->analysis_window - halfwin; /* other half */ /* analysis window is sane? */ if (finder->analysis_window > sample_size) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_INVALID, _("Analysis window is too large for sample")); return (FALSE); } /* nudge loop search windows if needed */ if (nudge) { if (win1start < halfwin) win1start = halfwin; if (win1end > (sample_size - ohalfwin)) win1end = sample_size - ohalfwin; if (win2start < halfwin) win2start = halfwin; if (win2end > (sample_size - ohalfwin)) win2end = sample_size - ohalfwin; } else { /* window1 is valid? */ if (win1start < halfwin || win1end > (sample_size - ohalfwin)) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_INVALID, _("Loop start search window is invalid")); return (FALSE); } /* loop end search window is valid? */ if (win2start < halfwin || win2end > (sample_size - ohalfwin)) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_INVALID, _("Loop end search window is invalid")); return (FALSE); } } /* make sure min_loop_size isn't impossible to satisfy */ if (win2end - win1start + 1 < finder->min_loop_size) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_INVALID, _("Impossible to satisfy minimum loop size")); return (FALSE); } if (nudge) /* modify parameters if they have been nudged */ { if (win1start != finder->window1_start) g_object_set (finder, "window1-start", win1start, NULL); if (win1end != finder->window1_end) g_object_set (finder, "window1-end", win1end, NULL); if (win2start != finder->window2_start) g_object_set (finder, "window2-start", win2start, NULL); if (win2end != finder->window2_end) g_object_set (finder, "window2-end", win2end, NULL); } return (TRUE); } /** * swami_loop_finder_find: * @finder: Loop finder object * @err: Location to store error info or %NULL to ignore * * Execute the loop find operation. This function blocks until operation is * finished or the "cancel" property is assigned %TRUE. The "progress" * parameter is updated periodically with the current progress value (between * 0.0 and 1.0). * * Note: Not thread safe. Properties should be assigned, this function * called and the results retrieved with swami_loop_finder_get_results() in * a serialized fashion. As long as this is ensured, creating a separate * thread to call this function will be OK. The results can only be accessed * up until the next call to this function. * * Returns: TRUE on success, FALSE if the find parameter values are invalid * or operation was cancelled (in which case @err may be set). */ gboolean swami_loop_finder_find (SwamiLoopFinder *finder, GError **err) { IpatchSampleData *sampledata; IpatchSampleStore *store; SwamiLoopResults *results; SwamiLoopMatch *matches; GTimeVal start, end; int i; /* make sure parameter are sane */ if (!swami_loop_finder_verify_params (finder, FALSE, err)) return (FALSE); /* change active state */ finder->active = TRUE; g_object_notify (G_OBJECT (finder), "active"); /* sample data converted to float yet? */ if (!finder->sample_data) { g_object_get (finder->sample, "sample-data", &sampledata, NULL); /* ++ ref sample data */ g_return_val_if_fail (sampledata != NULL, FALSE); /* ++ ref sample store in floating point format */ store = ipatch_sample_data_get_cache_sample (sampledata, SAMPLE_FORMAT, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err); if (!store) { g_object_unref (sampledata); /* -- unref sample data */ return (FALSE); } g_object_unref (sampledata); /* -- unref sample data */ /* FIXME - What to do about stereo? */ /* Allocate a sample handle and open the store */ finder->sample_handle = g_slice_new (IpatchSampleHandle); if (!ipatch_sample_handle_open (IPATCH_SAMPLE (store), finder->sample_handle, 'r', 0, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { g_object_unref (store); /* -- unref store */ return (FALSE); } finder->sample_data = ((IpatchSampleStoreCache *)store)->location; g_object_unref (store); /* -- unref store */ } matches = g_new (SwamiLoopMatch, finder->max_results); g_get_current_time (&start); /* run the loop finder processing algorithm */ find_loop (finder, matches); g_get_current_time (&end); finder->exectime = (end.tv_sec - start.tv_sec) * 1000; finder->exectime += (end.tv_usec - start.tv_usec + 500) / 1000; /* quit_it: */ if (finder->cancel) /* find was canceled? */ { g_free (matches); SWAMI_LOCK_WRITE (finder); if (finder->results) { g_object_unref (finder->results); finder->results = NULL; } SWAMI_UNLOCK_WRITE (finder); finder->cancel = FALSE; g_object_notify (G_OBJECT (finder), "cancel"); finder->active = FALSE; g_object_notify (G_OBJECT (finder), "active"); g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_CANCELED, _("Find operation canceled")); return (FALSE); } /* search for first uninitialized result, to calculate result count */ for (i = 0; i < finder->max_results; i++) if (matches[i].start == 0 && matches[i].end == 0) break; if (i > 0) { results = swami_loop_results_new (); /* ++ ref (taken by finder) */ results->count = i; results->values = matches; } else results = NULL; SWAMI_LOCK_WRITE (finder); if (finder->results) g_object_unref (finder->results); finder->results = results; SWAMI_UNLOCK_WRITE (finder); g_object_notify (G_OBJECT (finder), "results"); finder->active = FALSE; g_object_notify (G_OBJECT (finder), "active"); return (TRUE); } /** * swami_loop_finder_get_results: * @finder: Loop finder object * * Get the loop results object from a loop finder. * * Returns: Loop results object or %NULL if none (if swami_loop_finder_find() * has not yet been called or was canceled). The caller owns a reference * to the returned object and should unref it when completed. */ SwamiLoopResults * swami_loop_finder_get_results (SwamiLoopFinder *finder) { SwamiLoopResults *results; g_return_val_if_fail (SWAMI_IS_LOOP_FINDER (finder), NULL); SWAMI_LOCK_READ (finder); results = finder->results ? g_object_ref (finder->results) : NULL; SWAMI_UNLOCK_READ (finder); return (results); } /* the loop finder algorithm, parameters should be varified before calling. */ static void find_loop (SwamiLoopFinder *finder, SwamiLoopMatch *matches) { float *sample_data = finder->sample_data; /* Pointer to sample data */ int max_results = finder->max_results; /* Maximum results to return */ int analysis_window = finder->analysis_window; /* Analysis window size */ int min_loop_size = finder->min_loop_size; /* Minimum loop size */ int win1start, win1end, win1size, win2start, win2end, win2size; /* Search window parameters */ int group_pos_diff = finder->group_pos_diff; /* Minimum result group position diff */ int group_size_diff = finder->group_size_diff; /* Minimum result group size diff */ int half_window = analysis_window / 2; /* First half of analysis window */ guint64 progress_step, progress_count; /* Progress update vars */ SwamiLoopMatch *match, *cmpmatch; float *anwin_factors; GList *match_list = NULL; GList *match_list_last = NULL; int match_list_size = 0; float match_list_worst = 1.0; int win1, win2; int startpos, endpos; int pos_diff, size_diff, loop_diff; float quality, diff; GList *p, *link, *insert, *tmp; int fract, pow2; int i; /* Swap start/ends as needed */ win1start = MIN (finder->window1_start, finder->window1_end); win1end = MAX (finder->window1_start, finder->window1_end); win2start = MIN (finder->window2_start, finder->window2_end); win2end = MAX (finder->window2_start, finder->window2_end); /* Swap ranges if needed, so that window1 is loop start search */ if (win1start > win2start) { int tmp; tmp = win1start; win1start = win2start; win2start = tmp; tmp = win1end; win1end = win2end; win2end = tmp; } win1size = win1end - win1start + 1; win2size = win2end - win2start + 1; /* Control of progress update */ progress_step = (guint64)(((float)win1size * (float)win2size) / 1000.0); /* Max. 1000 progress callbacks */ progress_count = progress_step; finder->progress = 0.0; g_object_notify ((GObject *)finder, "progress"); /* Create analysis window factors array. All values in array add up to * 0.5 which when multiplied times maximum sample value difference of * 2.0 (1 - -1), gives a maximum quality value (worse quality) of 1.0. * Each neighboring factor towards the center point is twice the value of * it's outer neighbor. */ anwin_factors = g_new (float, analysis_window); /* Calculate fraction divisor */ for (i = 0, fract = 0, pow2 = 1; i <= half_window; i++, pow2 *= 2) { fract += pow2; if (i < half_window) fract += pow2; } /* Even windows are asymetrical, subtract 1 */ if (!(analysis_window & 1)) fract--; /* Calculate values for 1st half of window and center of window */ for (i = 0, pow2 = 1; i <= half_window; i++, pow2 *= 2) anwin_factors[i] = (float)(pow2 * 0.5 / fract); /* Copy values for 2nd half of window */ for (i = 0; half_window + i + 1 < analysis_window; i++) anwin_factors[half_window + i + 1] = anwin_factors[half_window - i - 1]; for (win1 = 0; win1 < win1size; win1++) { startpos = win1start + win1; for (win2 = 0; win2 < win2size; win2++) { endpos = win2start + win2; if (finder->cancel) /* if cancel flag has been set, return */ { for (p = match_list; p; p = g_list_delete_link (p, p)) g_slice_free (SwamiLoopMatch, p->data); g_free (anwin_factors); return; } /* progress management */ progress_count--; if (progress_count == 0) { progress_count = progress_step; finder->progress = ((float)win1 * win2size + win2) / ((float)win1size * win2size); g_object_notify ((GObject *)finder, "progress"); } if (startpos >= endpos || endpos - startpos + 1 < min_loop_size) continue; for (i = 0, quality = 0.0; i < analysis_window; i++) { diff = sample_data[startpos + i - half_window] - sample_data[endpos + i - half_window]; if (diff < 0) diff = -diff; quality += diff * anwin_factors[i]; } /* Skip if worse than the worst and result list already full */ if (quality >= match_list_worst && match_list_size == max_results) continue; loop_diff = endpos - startpos; insert = NULL; link = NULL; /* Look through existing matches for insert position, check if new * match is a part of an existing group and discard new match if worse * than existing group match or remove old group matches if worse quality */ for (p = match_list; p; ) { cmpmatch = (SwamiLoopMatch *)(p->data); /* Calculate position and size differences of new match and cmpmatch */ pos_diff = startpos - cmpmatch->start; size_diff = loop_diff - (cmpmatch->end - cmpmatch->start); if (pos_diff < 0) pos_diff = -pos_diff; if (size_diff < 0) size_diff = -size_diff; /* Same match group? */ if (pos_diff < group_pos_diff && size_diff < group_size_diff) { /* New match is worse? - Discard new */ if (quality >= cmpmatch->quality) break; /* New match is better - Discard old */ if (p == match_list_last) { match_list_last = p->prev; if (match_list_last) { match = (SwamiLoopMatch *)(match_list_last->data); match_list_worst = match->quality; } } if (!link) { /* Re-use list nodes */ link = p; p = p->next; match_list = g_list_remove_link (match_list, link); } else { tmp = p; p = p->next; g_slice_free (SwamiLoopMatch, tmp->data); match_list = g_list_delete_link (match_list, tmp); } match_list_size--; continue; } if (!insert && quality < cmpmatch->quality) insert = p; p = p->next; } /* Discard new match? */ if (p) continue; /* max results reached? */ if (match_list_size == max_results) { if (insert == match_list_last) insert = NULL; if (!link) { /* Re-use list nodes */ link = match_list_last; match_list_last = match_list_last->prev; match_list = g_list_remove_link (match_list, link); } else { tmp = match_list_last; match_list_last = match_list_last->prev; g_slice_free (SwamiLoopMatch, tmp->data); match_list = g_list_delete_link (match_list, tmp); } match = (SwamiLoopMatch *)(match_list_last->data); match_list_worst = match->quality; match_list_size--; } if (!link) { match = g_slice_new (SwamiLoopMatch); link = g_list_append (NULL, match); } else match = link->data; match_list_size++; match->start = startpos; match->end = endpos; match->quality = quality; if (insert) { link->prev = insert->prev; link->next = insert; if (insert->prev) insert->prev->next = link; else match_list = link; insert->prev = link; } else /* Append */ { if (match_list_last) { match_list_last->next = link; link->prev = match_list_last; } else match_list = link; match_list_last = link; match = (SwamiLoopMatch *)(match_list_last->data); match_list_worst = match->quality; } } } for (p = match_list, i = 0; p; p = g_list_delete_link (p, p), i++) { match = (SwamiLoopMatch *)(p->data); #if 0 // Debugging output of results printf ("Quality: %0.4f start: %d end: %d\n", match->quality, match->start, match->end); for (i2 = 0; i2 < analysis_window; i2++) { float f; diff = sample_data[match->start - half_window + i2] - sample_data[match->end - half_window + i2]; if (diff < 0.0) diff = -diff; f = (float)diff * anwin_factors[i2]; printf (" %d diff:%0.8f * factor:%0.8f = %0.8f\n", i2, diff, anwin_factors[i2], f); } #endif matches[i].start = match->start; matches[i].end = match->end; matches[i].quality = match->quality; g_slice_free (SwamiLoopMatch, match); } for (; i < max_results; i++) { matches[i].start = 0; matches[i].end = 0; matches[i].quality = 1.0; } finder->progress = 1.0; g_object_notify ((GObject *)finder, "progress"); g_free (anwin_factors); } swami-2.2.0/src/libswami/SwamiLoopFinder.h000066400000000000000000000061331361104770400204520ustar00rootroot00000000000000/* * SwamiLoopFinder.h - Sample loop finder object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_LOOP_FINDER_H__ #define __SWAMI_LOOP_FINDER_H__ #include #include #include typedef struct _SwamiLoopFinder SwamiLoopFinder; typedef struct _SwamiLoopFinderClass SwamiLoopFinderClass; #define SWAMI_TYPE_LOOP_FINDER (swami_loop_finder_get_type ()) #define SWAMI_LOOP_FINDER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_LOOP_FINDER, \ SwamiLoopFinder)) #define SWAMI_IS_LOOP_FINDER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_LOOP_FINDER)) /* Loop finder object */ struct _SwamiLoopFinder { SwamiLock parent_instance; IpatchSample *sample; /* sample assigned to loop finder */ IpatchSampleHandle *sample_handle; /* Open handle to cached sample data (allocated) */ guint sample_size; /* size of sample */ float *sample_data; /* converted sample data */ gboolean active; /* TRUE if find is currently active */ gboolean cancel; /* set to TRUE to cancel current find */ float progress; /* if active is TRUE, progress 0.0 - 1.0 */ int max_results; /* max SwamiLoopMatch result entries */ int analysis_window; /* width in samples of analysis window */ int min_loop_size; /* minimum loop size */ int window1_start; /* sample start position of window1 search */ int window1_end; /* sample end position of window1 search */ int window2_start; /* sample start position of window1 search */ int window2_end; /* sample end position of window1 search */ int group_pos_diff; /* min pos diff of loops for separate groups */ int group_size_diff; /* min size diff of loops for separate groups */ guint exectime; /* execution time in milliseconds */ SwamiLoopResults *results; /* results object */ }; /* Loop finder class */ struct _SwamiLoopFinderClass { SwamiLockClass parent_class; }; GType swami_loop_finder_get_type (void); SwamiLoopFinder *swami_loop_finder_new (void); void swami_loop_finder_full_search (SwamiLoopFinder *finder); gboolean swami_loop_finder_verify_params (SwamiLoopFinder *finder, gboolean nudge, GError **err); gboolean swami_loop_finder_find (SwamiLoopFinder *finder, GError **err); SwamiLoopResults *swami_loop_finder_get_results (SwamiLoopFinder *finder); #endif swami-2.2.0/src/libswami/SwamiLoopResults.c000066400000000000000000000054151361104770400207010ustar00rootroot00000000000000/* * SwamiLoopResults.c - Sample loop finder results object * * Swami * Copyright (C) 1999-2014 Element Green * * Thanks to Luis Garrido for the loop finder algorithm code and his * interest in creating this feature for Swami. * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include "SwamiLoopResults.h" static void swami_loop_results_init (SwamiLoopResults *results); static void swami_loop_results_finalize (GObject *object); G_DEFINE_TYPE (SwamiLoopResults, swami_loop_results, G_TYPE_OBJECT); static void swami_loop_results_class_init (SwamiLoopResultsClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = swami_loop_results_finalize; } static void swami_loop_results_init (SwamiLoopResults *results) { } static void swami_loop_results_finalize (GObject *object) { SwamiLoopResults *results = SWAMI_LOOP_RESULTS (object); g_free (results->values); if (G_OBJECT_CLASS (swami_loop_results_parent_class)->finalize) G_OBJECT_CLASS (swami_loop_results_parent_class)->finalize (object); } /** * swami_loop_results_new: * * Create a new sample loop finder results object. Used for storing results * of a #SwamiLoopFinder find operation. * * Returns: New object of type #SwamiLoopResults */ SwamiLoopResults * swami_loop_results_new (void) { return (SWAMI_LOOP_RESULTS (g_object_new (SWAMI_TYPE_LOOP_RESULTS, NULL))); } /** * swami_loop_results_get_values: * @results: Loop finder results object * @length: Output: location to store length of returned values array * * Get the loop match values array from a loop results object. * * Returns: Array of loop results or %NULL if none. The array is internal, * should not be modified or freed and should be used only up to the point * where @results is destroyed (no more references held). */ SwamiLoopMatch * swami_loop_results_get_values (SwamiLoopResults *results, guint *count) { g_return_val_if_fail (SWAMI_IS_LOOP_RESULTS (results), NULL); g_return_val_if_fail (count != NULL, NULL); *count = results->count; return (results->values); } swami-2.2.0/src/libswami/SwamiLoopResults.h000066400000000000000000000037111361104770400207030ustar00rootroot00000000000000/* * SwamiLoopResults.h - Sample loop finder results object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_LOOP_RESULTS_H__ #define __SWAMI_LOOP_RESULTS_H__ #include #include typedef struct _SwamiLoopResults SwamiLoopResults; typedef struct _SwamiLoopResultsClass SwamiLoopResultsClass; #define SWAMI_TYPE_LOOP_RESULTS (swami_loop_results_get_type ()) #define SWAMI_LOOP_RESULTS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_LOOP_RESULTS, \ SwamiLoopResults)) #define SWAMI_IS_LOOP_RESULTS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_LOOP_RESULTS)) /* loop match structure */ typedef struct { guint start; guint end; float quality; } SwamiLoopMatch; /* Loop results object */ struct _SwamiLoopResults { GObject parent_instance; /*< public >*/ SwamiLoopMatch *values; /* loop match result values */ int count; /* number of entries in values */ }; /* Loop finder class */ struct _SwamiLoopResultsClass { GObjectClass parent_class; }; GType swami_loop_results_get_type (void); SwamiLoopResults *swami_loop_results_new (void); SwamiLoopMatch *swami_loop_results_get_values (SwamiLoopResults *results, guint *count); #endif swami-2.2.0/src/libswami/SwamiMidiDevice.c000066400000000000000000000105171361104770400204070ustar00rootroot00000000000000/* * SwamiMidiDevice.c - MIDI device base class * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include "SwamiMidiDevice.h" #include "SwamiLog.h" /* --- signals and properties --- */ enum { LAST_SIGNAL }; enum { PROP_0, }; /* --- private function prototypes --- */ static void swami_midi_device_class_init (SwamiMidiDeviceClass *klass); static void swami_midi_device_init (SwamiMidiDevice *device); /* --- private data --- */ // static guint midi_signals[LAST_SIGNAL] = { 0 }; /* --- functions --- */ GType swami_midi_device_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (SwamiMidiDeviceClass), NULL, NULL, (GClassInitFunc) swami_midi_device_class_init, NULL, NULL, sizeof (SwamiMidiDevice), 0, (GInstanceInitFunc) swami_midi_device_init, }; item_type = g_type_register_static (SWAMI_TYPE_LOCK, "SwamiMidiDevice", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void swami_midi_device_class_init (SwamiMidiDeviceClass *klass) { } static void swami_midi_device_init (SwamiMidiDevice *midi) { midi->active = FALSE; } SwamiMidiDevice * swami_midi_device_new (void) { return SWAMI_MIDI_DEVICE (g_object_new (SWAMI_TYPE_MIDI_DEVICE, NULL)); } /** * swami_midi_device_open: * @device: Swami MIDI device * @err: Location to store error information or %NULL * * Open a MIDI device. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_midi_device_open (SwamiMidiDevice *device, GError **err) { SwamiMidiDeviceClass *oclass; int retval = TRUE; g_return_val_if_fail (SWAMI_IS_MIDI_DEVICE (device), FALSE); g_return_val_if_fail (!err || !*err, FALSE); oclass = SWAMI_MIDI_DEVICE_CLASS (G_OBJECT_GET_CLASS (device)); SWAMI_LOCK_WRITE (device); if (!device->active) { if (oclass->open) retval = (*oclass->open) (device, err); if (retval) device->active = TRUE; } SWAMI_UNLOCK_WRITE (device); return (retval); } /** * swami_midi_device_close: * @device: MIDI device object * * Close an active MIDI device. */ void swami_midi_device_close (SwamiMidiDevice *device) { SwamiMidiDeviceClass *oclass; g_return_if_fail (SWAMI_IS_MIDI_DEVICE (device)); oclass = SWAMI_MIDI_DEVICE_CLASS (G_OBJECT_GET_CLASS (device)); SWAMI_LOCK_WRITE (device); if (device->active) { if (oclass->close) (*oclass->close) (device); device->active = FALSE; } SWAMI_UNLOCK_WRITE (device); } /** * swami_midi_device_get_control: * @device: MIDI device object * @index: Index of control * * Get a MIDI control object from a MIDI device. A MIDI device may have * multiple MIDI control interface channels (if supporting more than 16 * MIDI channels for example), so index can be used to iterate over them. * The MIDI device does NOT need to be active when calling this function. * * Returns: The MIDI control for the given @index, or %NULL if no control * with that index. The reference count of the returned control has been * incremented and is owned by the caller, remember to unref it when finished. */ SwamiControl * swami_midi_device_get_control (SwamiMidiDevice *device, int index) { SwamiMidiDeviceClass *oclass; SwamiControl *control = NULL; g_return_val_if_fail (SWAMI_IS_MIDI_DEVICE (device), NULL); oclass = SWAMI_MIDI_DEVICE_CLASS (G_OBJECT_GET_CLASS (device)); if (oclass->get_control) { control = (*oclass->get_control)(device, index); if (control) g_object_ref (control); /* ++ ref for caller */ } return (control); } swami-2.2.0/src/libswami/SwamiMidiDevice.h000066400000000000000000000046331361104770400204160ustar00rootroot00000000000000/* * SwamiMidiDevice.h - Swami MIDI object header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_MIDI_DEVICE_H__ #define __SWAMI_MIDI_DEVICE_H__ #include #include #include #include #include typedef struct _SwamiMidiDevice SwamiMidiDevice; typedef struct _SwamiMidiDeviceClass SwamiMidiDeviceClass; #define SWAMI_TYPE_MIDI_DEVICE (swami_midi_device_get_type ()) #define SWAMI_MIDI_DEVICE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_MIDI_DEVICE, SwamiMidiDevice)) #define SWAMI_MIDI_DEVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_MIDI_DEVICE, SwamiMidiDeviceClass)) #define SWAMI_IS_MIDI_DEVICE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_MIDI_DEVICE)) #define SWAMI_IS_MIDI_DEVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_MIDI_DEVICE)) /* Swami MIDI device object */ struct _SwamiMidiDevice { SwamiLock parent_instance; /* derived from GObject */ /*< private >*/ gboolean active; /* driver is active? */ }; /* Swami MIDI device class */ struct _SwamiMidiDeviceClass { SwamiLockClass parent_class; /*< public >*/ /* methods */ gboolean (*open) (SwamiMidiDevice *device, GError **err); void (*close) (SwamiMidiDevice *device); SwamiControl * (*get_control) (SwamiMidiDevice *device, int index); }; GType swami_midi_device_get_type (void); gboolean swami_midi_device_open (SwamiMidiDevice *device, GError **err); void swami_midi_device_close (SwamiMidiDevice *device); SwamiControl *swami_midi_device_get_control (SwamiMidiDevice *device, int index); #endif swami-2.2.0/src/libswami/SwamiMidiEvent.c000066400000000000000000000237411361104770400202740ustar00rootroot00000000000000/* * SwamiMidiEvent.c - MIDI event object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiMidiEvent.h" #include "swami_priv.h" GType swami_midi_event_get_type (void) { static GType item_type = 0; if (!item_type) item_type = g_boxed_type_register_static ("SwamiMidiEvent", (GBoxedCopyFunc)swami_midi_event_copy, (GBoxedFreeFunc)swami_midi_event_free); return (item_type); } /** * swami_midi_event_new: * * Create a new MIDI event structure. * * Returns: New MIDI event structure. Free it with swami_midi_event_free() * when finished. */ SwamiMidiEvent * swami_midi_event_new (void) { SwamiMidiEvent *event; event = g_slice_new0 (SwamiMidiEvent); event->type = SWAMI_MIDI_NONE; return (event); } /** * swami_midi_event_free: * @event: MIDI event structure to free * * Free a MIDI event previously allocated by swami_midi_event_new(). */ void swami_midi_event_free (SwamiMidiEvent *event) { g_slice_free (SwamiMidiEvent, event); } /** * swami_midi_event_copy: * @event: MIDI event structure to duplicate * * Duplicate a MIDI event structure. * * Returns: New duplicate MIDI event. Free it with swami_midi_event_free() * when finished. */ SwamiMidiEvent * swami_midi_event_copy (SwamiMidiEvent *event) { SwamiMidiEvent *new_event; new_event = swami_midi_event_new (); *new_event = *event; return (new_event); } /** * swami_midi_event_set: * @event: MIDI event structure * @type: Type of event to assign * @channel: MIDI channel to send to * @param1: First parameter of event @type * @param2: Second parameter of event @type (only for some types) * * A single entry point for all event types if one does not care to use * the event type specific functions. */ void swami_midi_event_set (SwamiMidiEvent *event, SwamiMidiEventType type, int channel, int param1, int param2) { SwamiMidiEventNote *note; SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); note = &event->data.note; ctrl = &event->data.control; event->type = type; event->channel = channel; switch (type) { case SWAMI_MIDI_NOTE_ON: if (param2 == 0) event->type = SWAMI_MIDI_NOTE_OFF; /* velocity is 0? */ /* fall through */ case SWAMI_MIDI_NOTE_OFF: case SWAMI_MIDI_KEY_PRESSURE: note->note = param1; note->velocity = param2; break; case SWAMI_MIDI_PITCH_BEND: ctrl->param = param1; ctrl->value = param2; break; case SWAMI_MIDI_PROGRAM_CHANGE: ctrl->value = param1; break; case SWAMI_MIDI_CONTROL: case SWAMI_MIDI_CONTROL14: ctrl->param = param1; ctrl->value = param2; break; case SWAMI_MIDI_CHAN_PRESSURE: ctrl->value = param1; break; case SWAMI_MIDI_RPN: case SWAMI_MIDI_NRPN: ctrl->param = param1; ctrl->value = param2; break; /* these are handled by other event types, convenience only */ case SWAMI_MIDI_BEND_RANGE: event->type = SWAMI_MIDI_RPN; ctrl->param = SWAMI_MIDI_RPN_BEND_RANGE; ctrl->value = param1; break; case SWAMI_MIDI_BANK_SELECT: event->type = SWAMI_MIDI_CONTROL14; ctrl->param = SWAMI_MIDI_CC_BANK_MSB; ctrl->value = param1; break; default: g_warning ("Unknown MIDI event type"); event->type = SWAMI_MIDI_NONE; break; } } /** * swami_midi_event_note_on: * @event: MIDI event structure * @channel: MIDI channel to send note on event to * @note: MIDI note number * @velocity: MIDI note velocity * * Make a MIDI event structure a note on event. */ void swami_midi_event_note_on (SwamiMidiEvent *event, int channel, int note, int velocity) { SwamiMidiEventNote *n; g_return_if_fail (event != NULL); event->type = (velocity != 0) ? SWAMI_MIDI_NOTE_ON : SWAMI_MIDI_NOTE_OFF; event->channel = channel; n = &event->data.note; n->note = note; n->velocity = velocity; } /** * swami_midi_event_note_off: * @event: MIDI event structure * @channel: MIDI channel to send note off event to * @note: MIDI note number * @velocity: MIDI note velocity * * Make a MIDI event structure a note off event. */ void swami_midi_event_note_off (SwamiMidiEvent *event, int channel, int note, int velocity) { SwamiMidiEventNote *n; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_NOTE_OFF; event->channel = channel; n = &event->data.note; n->note = note; n->velocity = velocity; } /** * swami_midi_event_bank_select: * @event: MIDI event structure * @channel: MIDI channel to send bank select to * @bank: Bank number to change to * * Make a MIDI event structure a bank select event. A convenience function * since a bank select is just a controller event, and is sent as such. */ void swami_midi_event_bank_select (SwamiMidiEvent *event, int channel, int bank) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_CONTROL14; event->channel = channel; ctrl = &event->data.control; ctrl->param = SWAMI_MIDI_CC_BANK_MSB; ctrl->value = bank; } /** * swami_midi_event_program_change: * @event: MIDI event structure * @channel: MIDI channel to send program change to * @program: Program number to change to * * Set a MIDI event structure to a program change event. */ void swami_midi_event_set_program (SwamiMidiEvent *event, int channel, int program) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_PROGRAM_CHANGE; event->channel = channel; ctrl = &event->data.control; ctrl->value = program; } /** * swami_midi_event_bend_range: * @event: MIDI event structure * @channel: MIDI channel to send event to * @cents: Bend range in cents (100th of a semitone) * * Make a MIDI event structure a bend range event. A convenience since one * could also use the swami_midi_event_rpn() function. */ void swami_midi_event_set_bend_range (SwamiMidiEvent *event, int channel, int cents) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_RPN; event->channel = channel; ctrl = &event->data.control; ctrl->param = SWAMI_MIDI_RPN_BEND_RANGE; ctrl->value = cents; } /** * swami_midi_event_pitch_bend: * @event: MIDI event structure * @channel: MIDI channel to send event to * @value: MIDI pitch bend value (-8192 - 8191, 0 = center) * * Make a MIDI event structure a pitch bend event. */ void swami_midi_event_pitch_bend (SwamiMidiEvent *event, int channel, int value) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_PITCH_BEND; event->channel = channel; ctrl = &event->data.control; ctrl->value = value; } /** * swami_midi_event_control: * @event: MIDI event structure * @channel: MIDI channel to send event on * @ctrlnum: Control number * @value: 7 bit value to set control to * * Make a MIDI event structure a 7 bit controller event. */ void swami_midi_event_control (SwamiMidiEvent *event, int channel, int ctrlnum, int value) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_CONTROL; event->channel = channel; ctrl = &event->data.control; ctrl->param = ctrlnum; ctrl->value = value; } /** * swami_midi_event_control14: * @event: MIDI event structure * @channel: MIDI channel to send event on * @ctrlnum: Control number * @value: 14 bit value to set control to * * Make a MIDI event structure a 14 bit controller event. The ctrlnum should * be one of the first 32 MIDI controllers that have MSB and LSB controllers * for sending 14 bit values. Either the MSB or LSB controller number may * be used (example: either 0 or 32 can be used for setting 14 bit bank * number). Internally the MSB controller numbers are used (0-31). */ void swami_midi_event_control14 (SwamiMidiEvent *event, int channel, int ctrlnum, int value) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); g_return_if_fail (ctrlnum >= 0 && ctrlnum <= 63); if (ctrlnum > 31) ctrlnum -= 32; event->type = SWAMI_MIDI_CONTROL14; event->channel = channel; ctrl = &event->data.control; ctrl->param = ctrlnum; ctrl->value = value; } /** * swami_midi_event_rpn: * @event: MIDI event structure * @channel: MIDI channel to send event to * @paramnum: RPN parameter number * @value: 14 bit value to set parameter to * * Make a MIDI event structure a RPN (registered parameter number) event. */ void swami_midi_event_rpn (SwamiMidiEvent *event, int channel, int paramnum, int value) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_RPN; event->channel = channel; ctrl = &event->data.control; ctrl->param = paramnum; ctrl->value = value; } /** * swami_midi_event_nrpn: * @event: MIDI event structure * @channel: MIDI channel to send event to * @paramnum: NRPN parameter number * @value: 14 bit value to set parameter to * * Make a MIDI event structure a NRPN (non-registered parameter number) event. */ void swami_midi_event_nrpn (SwamiMidiEvent *event, int channel, int paramnum, int value) { SwamiMidiEventControl *ctrl; g_return_if_fail (event != NULL); event->type = SWAMI_MIDI_NRPN; event->channel = channel; ctrl = &event->data.control; ctrl->param = paramnum; ctrl->value = value; } swami-2.2.0/src/libswami/SwamiMidiEvent.h000066400000000000000000000107631361104770400203010ustar00rootroot00000000000000/* * SwamiMidiEvent.h - Header for MIDI event object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_MIDI_EVENT_H__ #define __SWAMI_MIDI_EVENT_H__ #include #include typedef struct _SwamiMidiEvent SwamiMidiEvent; typedef struct _SwamiMidiEventNote SwamiMidiEventNote; typedef struct _SwamiMidiEventControl SwamiMidiEventControl; #define SWAMI_TYPE_MIDI_EVENT (swami_midi_event_get_type ()) /* MIDI event type (in comments Control = SwamiMidiEventControl and Note = SwamiMidiEventNote) */ typedef enum { SWAMI_MIDI_NONE, /* NULL event */ SWAMI_MIDI_NOTE, /* a note interval (Note) */ SWAMI_MIDI_NOTE_ON, /* note on event (Note) */ SWAMI_MIDI_NOTE_OFF, /* note off event (Note) */ SWAMI_MIDI_KEY_PRESSURE, /* key pressure (Note) */ SWAMI_MIDI_PITCH_BEND, /* pitch bend event -8192 - 8191 (Control) */ SWAMI_MIDI_PROGRAM_CHANGE, /* program change (Control) */ SWAMI_MIDI_CONTROL, /* 7 bit controller (Control) */ SWAMI_MIDI_CONTROL14, /* 14 bit controller (Control) */ SWAMI_MIDI_CHAN_PRESSURE, /* channel pressure (Control) */ SWAMI_MIDI_RPN, /* registered param (Control) */ SWAMI_MIDI_NRPN, /* non registered param (Control) */ /* these are used as a convenience for swami_midi_event_set() but they should not appear in the event type field, they are handled by other events above */ SWAMI_MIDI_BEND_RANGE, SWAMI_MIDI_BANK_SELECT } SwamiMidiEventType; /* structure defining parameters of a note event */ struct _SwamiMidiEventNote { guint8 note; guint8 velocity; /* _NOTE_ON, _NOTE_OFF, _KEY_PRESSURE, or _NOTE events */ guint8 off_velocity; /* _NOTE event only */ guint8 unused; guint duration; /* for SWAMI_MIDI_NOTE event only */ }; /* some standard General MIDI custom controllers */ #define SWAMI_MIDI_CC_BANK_MSB 0 #define SWAMI_MIDI_CC_MODULATION 1 #define SWAMI_MIDI_CC_VOLUME 7 #define SWAMI_MIDI_CC_PAN 10 #define SWAMI_MIDI_CC_EXPRESSION 11 #define SWAMI_MIDI_CC_BANK_LSB 32 #define SWAMI_MIDI_CC_SUSTAIN 64 #define SWAMI_MIDI_CC_REVERB 91 #define SWAMI_MIDI_CC_CHORUS 93 /* standard registered parameter numbers */ #define SWAMI_MIDI_RPN_BEND_RANGE 0 #define SWAMI_MIDI_RPN_MASTER_TUNE 1 struct _SwamiMidiEventControl { guint param; /* control number */ int value; /* control value */ }; struct _SwamiMidiEvent { SwamiMidiEventType type; int channel; /* most events send on a specific MIDI channel */ union { SwamiMidiEventNote note; SwamiMidiEventControl control; } data; }; GType swami_midi_event_get_type (void); SwamiMidiEvent *swami_midi_event_new (void); void swami_midi_event_free (SwamiMidiEvent *event); SwamiMidiEvent *swami_midi_event_copy (SwamiMidiEvent *event); void swami_midi_event_set (SwamiMidiEvent *event, SwamiMidiEventType type, int channel, int param1, int param2); void swami_midi_event_note_on (SwamiMidiEvent *event, int channel, int note, int velocity); void swami_midi_event_note_off (SwamiMidiEvent *event, int channel, int note, int velocity); void swami_midi_event_bank_select (SwamiMidiEvent *event, int channel, int bank); void swami_midi_event_program_change (SwamiMidiEvent *event, int channel, int program); void swami_midi_event_bend_range (SwamiMidiEvent *event, int channel, int cents); void swami_midi_event_pitch_bend (SwamiMidiEvent *event, int channel, int value); void swami_midi_event_control (SwamiMidiEvent *event, int channel, int ctrlnum, int value); void swami_midi_event_control14 (SwamiMidiEvent *event, int channel, int ctrlnum, int value); void swami_midi_event_rpn (SwamiMidiEvent *event, int channel, int paramnum, int value); void swami_midi_event_nrpn (SwamiMidiEvent *event, int channel, int paramnum, int value); #endif swami-2.2.0/src/libswami/SwamiObject.c000066400000000000000000000767231361104770400176260ustar00rootroot00000000000000/* * SwamiObject.c - Child object properties and type rank system * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiObject.h" #include "SwamiRoot.h" #include "builtin_enums.h" #include "i18n.h" #include "swami_priv.h" #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id) #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id)) /* Swami child object properties */ enum { OBJ_PROP_0, OBJ_PROP_NAME, OBJ_PROP_RANK, OBJ_PROP_FLAGS, OBJ_PROP_ROOT }; /* --- private function prototypes --- */ static gint swami_gcompare_typerank_by_rank (gconstpointer a, gconstpointer b); static gint swami_gcompare_typerank_by_type (gconstpointer a, gconstpointer b); static void swami_type_recurse_make_list (GType type, GList **list); static gboolean node_traverse_match_name (GNode *node, gpointer data); static gboolean node_traverse_match_type (GNode *node, gpointer data); static gint swami_gcompare_object_rank (gconstpointer a, gconstpointer b); static void swami_install_object_property (guint property_id, GParamSpec *pspec); static void object_property_notify_dispatcher (GObject *object, guint n_pspecs, GParamSpec **pspecs); static inline SwamiObjectPropBag *object_new_propbag (GObject *object); static void free_propbag (gpointer data); static void object_get_property (GObject *object, GParamSpec *pspec, GValue *value); static void object_set_property (GObject *object, GParamSpec *pspec, const GValue *value, GObjectNotifyQueue *nqueue); GQuark swami_object_propbag_quark; /* object qdata quark for property bag */ /* global lock for all Swami object property bags (only used for name property currently) */ G_LOCK_DEFINE_STATIC (SwamiObjectPropBag); static GParamSpecPool *object_property_pool; /* Swami property pool */ static GObjectNotifyContext *object_property_notify_context; /* structure used to rank GTypes */ typedef struct { GType type; int rank; } TypeRank; G_LOCK_DEFINE_STATIC (typerank); /* a lock for typerank system */ static GHashTable *typerank_hash = NULL; /* signal id of SwamiRoot PROP_NOTIFY signal (looked up by name) */ static guint root_prop_notify_signal_id; /* initialization for swami object properties and type rank system */ void _swami_object_init (void) { static GObjectNotifyContext opn_context = { 0, NULL, NULL }; root_prop_notify_signal_id = g_signal_lookup ("swami-prop-notify", SWAMI_TYPE_ROOT); typerank_hash = g_hash_table_new (NULL, NULL); object_property_pool = g_param_spec_pool_new (FALSE); opn_context.quark_notify_queue = g_quark_from_static_string ("Swami-object-property-notify-queue"); opn_context.dispatcher = object_property_notify_dispatcher; object_property_notify_context = &opn_context; /* get a quark for property bag object qdata quark */ swami_object_propbag_quark = g_quark_from_static_string ("_SwamiPropBag"); /* install Swami object properties */ swami_install_object_property (OBJ_PROP_NAME, g_param_spec_string ("name", _("Name"), _("Name"), NULL, G_PARAM_READWRITE)); swami_install_object_property (OBJ_PROP_RANK, g_param_spec_int ("rank", _("Rank"), _("Rank"), 1, 100, SWAMI_RANK_NORMAL, G_PARAM_READWRITE)); swami_install_object_property (OBJ_PROP_FLAGS, g_param_spec_flags ("flags", _("Flags"), _("Flags"), SWAMI_TYPE_OBJECT_FLAGS, 0, G_PARAM_READWRITE)); swami_install_object_property (OBJ_PROP_ROOT, g_param_spec_object ("root", _("Root"), _("Root object"), SWAMI_TYPE_ROOT, G_PARAM_READWRITE)); } /** * swami_type_set_rank: * @type: Type to set rank value of * @group_type: An ancestor type of @type * @rank: Rank value (see #SwamiRank enum). * * Sets the ranking of instance GTypes derived from an ancestor @group_type. * This is useful to set a default instance type or to define order to types. * An example of use would be to set a higher rank on a plugin wavetable type * to cause it to be used when creating a default wavetable instance. */ void swami_type_set_rank (GType type, GType group_type, int rank) { GSList *grplist, *p; TypeRank *typerank; g_return_if_fail (g_type_is_a (type, group_type)); rank = CLAMP (rank, 1, 100); G_LOCK (typerank); grplist = g_hash_table_lookup (typerank_hash, GSIZE_TO_POINTER (group_type)); if (grplist) { p = grplist; while (p) /* remove existing TypeRank for @type (if any) */ { if (((TypeRank *)(p->data))->type == type) { g_slice_free (TypeRank, p->data); grplist = g_slist_delete_link (grplist, p); break; } p = g_slist_next (p); } } if (rank != SWAMI_RANK_NORMAL) { typerank = g_slice_new (TypeRank); typerank->type = type; typerank->rank = rank; grplist = g_slist_insert_sorted (grplist, typerank, swami_gcompare_typerank_by_rank); } g_hash_table_insert (typerank_hash, GSIZE_TO_POINTER (group_type), grplist); G_UNLOCK (typerank); } static gint swami_gcompare_typerank_by_rank (gconstpointer a, gconstpointer b) { TypeRank *atyperank = (TypeRank *)a, *btyperank = (TypeRank *)b; return (atyperank->rank - btyperank->rank); } static gint swami_gcompare_typerank_by_type (gconstpointer a, gconstpointer b) { TypeRank *atyperank = (TypeRank *)a, *btyperank = (TypeRank *)b; return (atyperank->type - btyperank->type); } /** * swami_type_get_rank: * @type: Instance type to get rank of * @group_type: Ancestor group type * * Gets the instance rank of the given @type derived from a @group_type. * See swami_type_set_rank() for more info. * * Returns: Rank value (#SWAMI_RANK_NORMAL if not explicitly set). */ int swami_type_get_rank (GType type, GType group_type) { GSList *grplist, *p; TypeRank find; int rank = SWAMI_RANK_NORMAL; g_return_val_if_fail (g_type_is_a (type, group_type), 0); find.type = type; G_LOCK (typerank); grplist = g_hash_table_lookup (typerank_hash, GSIZE_TO_POINTER (group_type)); p = g_slist_find_custom (grplist, &find, swami_gcompare_typerank_by_type); if (p) rank = ((TypeRank *)(p->data))->rank; G_UNLOCK (typerank); return (rank); } /** * swami_type_get_children: * @group_type: Group type to get derived types from * * Gets an array of types that are derived from @group_type, sorted by rank. * For types that have not had their rank explicitly set #SWAMI_RANK_NORMAL * will be assumed. * * Returns: Newly allocated and zero terminated array of GTypes sorted by rank * or %NULL if no types found. Should be freed when finished. */ GType * swami_type_get_children (GType group_type) { GList *typelist = NULL, *sortlist = NULL, *normal = NULL, *tp; GSList *grplist, *p; TypeRank *typerank; GType *retarray, *ap; g_return_val_if_fail (group_type != 0, NULL); /* create list of type tree under (and including) group_type */ swami_type_recurse_make_list (group_type, &typelist); if (!typelist) return (NULL); G_LOCK (typerank); /* sort typelist */ grplist = g_hash_table_lookup (typerank_hash, GSIZE_TO_POINTER (group_type)); p = grplist; while (p) { typerank = (TypeRank *)(p->data); tp = g_list_find (typelist, GSIZE_TO_POINTER (typerank->type)); if (tp) { typelist = g_list_remove_link (typelist, tp); sortlist = g_list_concat (tp, sortlist); /* finds the node to insert SWAMI_RANK_NORMAL types at */ if (typerank->rank > SWAMI_RANK_NORMAL) normal = tp; } p = g_slist_next (p); } G_UNLOCK (typerank); /* add remaining SWAMI_RANK_NORMAL types (not in hash) */ if (typelist) { if (normal) /* found node to insert SWAMI_RANK_NORMAL types before? */ { GList *last = g_list_last (typelist); /* link SWAMI_RANK_NORMAL types into sortlist */ if (normal->prev) normal->prev->next = typelist; typelist->prev = normal->prev; normal->prev = last; last->next = normal; } /* all ranks in hash below or equal to SWAMI_RANK_NORMAL, append */ else sortlist = g_list_concat (sortlist, typelist); } tp = sortlist = g_list_reverse (sortlist); ap = retarray = g_malloc ((g_list_length (sortlist) + 1) * sizeof (GType)); while (tp) { *ap = GPOINTER_TO_SIZE (tp->data); /* store GType into array element */ ap++; tp = g_list_delete_link (tp, tp); /* delete and iterate */ } *ap = 0; return (retarray); } /* recursive function to find non-abstract types derived from initial type */ static void swami_type_recurse_make_list (GType type, GList **list) { GType *children, *t; if (!G_TYPE_IS_ABSTRACT (type)) /* only add non-abstract types */ *list = g_list_prepend (*list, GSIZE_TO_POINTER (type)); children = g_type_children (type, NULL); t = children; while (*t) { swami_type_recurse_make_list (*t, list); t++; } g_free (children); } /** * swami_type_get_default: * @group_type: Group type to get default (highest ranked) instance type from * * Gets the default (highest ranked) type derived from a group_type. * Like swami_type_get_children() but returns first type instead of an array. * * Returns: GType of default type or 0 if no types derived from @group_type. */ GType swami_type_get_default (GType group_type) { GType *types, rettype = 0; types = swami_type_get_children (group_type); if (types) { rettype = *types; g_free (types); } return (rettype); } /** * swami_object_set_default: * @object: Object to set as default * @type: A type that @object is derived from (or its type) * * Set an @object as default amongst its peers of the same @type. Uses * the Swami rank system (see "rank" Swami property) to elect a default * object. All other object's registered to the same #SwamiRoot and of * the same @type (or derived thereof) are set to #SWAMI_RANK_NORMAL and the * @object's rank is set higher. */ void swami_object_set_default (GObject *object, GType type) { IpatchList *list; IpatchIter iter; GObject *pobj; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, type)); list = swami_object_find_by_type (object, g_type_name (type));/* ++ ref */ g_return_if_fail (list != NULL); ipatch_list_init_iter (list, &iter); pobj = ipatch_iter_first (&iter); while (pobj) { swami_object_set (pobj, "rank", (pobj == object) ? SWAMI_RANK_DEFAULT : SWAMI_RANK_NORMAL); pobj = ipatch_iter_next (&iter); } g_object_unref (list); /* -- unref list */ } /* a bag for swami_object_get_by_name() */ typedef struct { const char *name; GObject *match; } ObjectFindNameBag; /** * swami_object_get_by_name: * @object: Swami root object (#SwamiRoot) or an object added to one * @name: Name to look for * * Finds an object by its Swami name property, searches recursively * through object tree. The @object can be either a #SwamiRoot or, as * an added convenience, an object registered to one. * * Returns: Object that matched @name or %NULL if not found. Remember to * unref it when finished. */ GObject * swami_object_get_by_name (GObject *object, const char *name) { ObjectFindNameBag findbag; SwamiObjectPropBag *propbag; SwamiRoot *root; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (name != NULL, NULL); if (!SWAMI_IS_ROOT (object)) { propbag = g_object_get_qdata (object, swami_object_propbag_quark); g_return_val_if_fail (propbag != NULL, NULL); g_return_val_if_fail (SWAMI_IS_ROOT (propbag->root), NULL); root = propbag->root; } else root = SWAMI_ROOT (object); findbag.name = name; findbag.match = NULL; SWAMI_LOCK_READ (root->proptree); g_node_traverse (root->proptree->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_traverse_match_name, &findbag); SWAMI_UNLOCK_READ (root->proptree); return (findbag.match); } static gboolean node_traverse_match_name (GNode *node, gpointer data) { ObjectFindNameBag *findbag = (ObjectFindNameBag *)data; SwamiPropTreeNode *propnode; SwamiObjectPropBag *propbag; propnode = (SwamiPropTreeNode *)(node->data); propbag = g_object_get_qdata (G_OBJECT (propnode->object), swami_object_propbag_quark); if (propbag && propbag->name && strcmp (findbag->name, propbag->name) == 0) { findbag->match = propnode->object; g_object_ref (findbag->match); return (TRUE); /* stop this madness */ } return (FALSE); /* continue until found */ } /* a bag for swami_object_find_by_type() */ typedef struct { GType type; IpatchList *list; } ObjectFindTypeBag; /** * swami_object_find_by_type: * @object: Swami root object (#SwamiRoot) or an object added to one * @type_name: Name of GObject derived GType of objects to search for, objects * derived from this type will also match. * * Lookup all objects by @type_name or derived from @type_name * recursively. The @object can be either a #SwamiRoot or, as an * added convenience, an object registered to one. The returned * iterator list is sorted by Swami rank (see "rank" Swami property). * * Returns: New list of objects of @type_name and/or derived * from it or NULL if no matches to that type. The returned list has a * reference count of 1 which the caller owns, remember to unref it when * finished. */ IpatchList * swami_object_find_by_type (GObject *object, const char *type_name) { ObjectFindTypeBag findbag; SwamiObjectPropBag *propbag; SwamiRoot *root; GType type; g_return_val_if_fail (G_IS_OBJECT (object), NULL); type = g_type_from_name (type_name); g_return_val_if_fail (type != 0, NULL); g_return_val_if_fail (g_type_is_a (type, G_TYPE_OBJECT), NULL); if (!SWAMI_IS_ROOT (object)) { propbag = g_object_get_qdata (object, swami_object_propbag_quark); g_return_val_if_fail (propbag != NULL, NULL); g_return_val_if_fail (SWAMI_IS_ROOT (propbag->root), NULL); root = propbag->root; } else root = SWAMI_ROOT (object); findbag.type = type; findbag.list = ipatch_list_new (); /* ++ ref new list */ SWAMI_LOCK_READ (root->proptree); g_node_traverse (root->proptree->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_traverse_match_type, &findbag); SWAMI_UNLOCK_READ (root->proptree); if (!findbag.list->items) /* free list if no matching items */ { g_object_unref (findbag.list); /* -- unref list */ findbag.list = NULL; } /* sort object list by Swami rank */ else findbag.list->items = g_list_sort (findbag.list->items, swami_gcompare_object_rank); return (findbag.list); } static gboolean node_traverse_match_type (GNode *node, gpointer data) { ObjectFindTypeBag *bag = (ObjectFindTypeBag *)data; SwamiPropTreeNode *propnode; propnode = (SwamiPropTreeNode *)(node->data); if (g_type_is_a (G_TYPE_FROM_INSTANCE (propnode->object), bag->type)) { g_object_ref (propnode->object); /* ++ ref for list */ bag->list->items = g_list_prepend (bag->list->items, propnode->object); } return (FALSE); /* don't stop for nothin */ } static gint swami_gcompare_object_rank (gconstpointer a, gconstpointer b) { int arank, brank; swami_object_get (G_OBJECT (a), "rank", &arank, NULL); swami_object_get (G_OBJECT (b), "rank", &brank, NULL); return (arank - brank); } /** * swami_object_get_by_type: * @object: Swami root object (#SwamiRoot) or an object added to one * @type_name: Name of GObject derived GType to search for * * Lookup the highest ranking (see "rank" Swami property) object of * the given @type_name or derived from it. The returned object's * reference count is incremented and the caller is responsible for * removing it with g_object_unref when finished with it. * * Returns: The highest ranking object of the given type or NULL if none. * Remember to unreference it when done. */ GObject * swami_object_get_by_type (GObject *object, const char *type_name) { IpatchList *list; GObject *match = NULL; list = swami_object_find_by_type (object, type_name); /* ++ ref list */ if (list) { match = (GObject *)(list->items->data); g_object_ref (match); g_object_unref (list); /* -- unref list */ } return (match); } /** * swami_object_get_valist: * @object: An object * @first_property_name: Name of first Swami property to get * @var_args: Pointer to store first property value in followed by * property name pointer pairs, terminated with a NULL property name. * * Get Swami properties from an object. */ void swami_object_get_valist (GObject *object, const char *first_property_name, va_list var_args) { const char *name; g_return_if_fail (G_IS_OBJECT (object)); name = first_property_name; while (name) { GValue value = { 0, }; GParamSpec *pspec; char *error; pspec = g_param_spec_pool_lookup (object_property_pool, name, SWAMI_TYPE_ROOT, FALSE); if (!pspec) { g_warning ("%s: no Swami property named `%s'", G_STRLOC, name); break; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("%s: Swami property `%s' is not readable", G_STRLOC, pspec->name); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); object_get_property (object, pspec, &value); G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); name = va_arg (var_args, char *); } } /** * swami_object_get_property: * @object: An object * @property_name: Name of Swami property to get * @value: An initialized value to store the property value in. The value's * type must be a type that the property can be transformed to. * * Get a Swami property from an object. */ void swami_object_get_property (GObject *object, const char *property_name, GValue *value) { GParamSpec *pspec; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (property_name != NULL); g_return_if_fail (G_IS_VALUE (value)); pspec = g_param_spec_pool_lookup (object_property_pool, property_name, SWAMI_TYPE_ROOT, FALSE); if (!pspec) g_warning ("%s: no Swami property named `%s'", G_STRLOC, property_name); else if (!(pspec->flags & G_PARAM_READABLE)) g_warning ("%s: Swami property `%s' is not readable", G_STRLOC, pspec->name); else { GValue *prop_value, tmp_value = { 0, }; /* auto-conversion of the callers value type */ if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec)) { g_value_reset (value); prop_value = value; } else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value))) { g_warning ("can't retrieve Swami property `%s' of type `%s' as" " value of type `%s'", pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), G_VALUE_TYPE_NAME (value)); return; } else { g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); prop_value = &tmp_value; } object_get_property (object, pspec, prop_value); if (prop_value != value) { g_value_transform (prop_value, value); g_value_unset (&tmp_value); } } } /** * swami_object_get: * @object: A GObject * @first_prop_name: Name of first Swami property to get * @...: First value should be a pointer to store the first property value into * followed by property name and pointer pairs, terminated by a %NULL * property name. * * Get multiple Swami properties from an @object. */ void swami_object_get (gpointer object, const char *first_prop_name, ...) { va_list var_args; g_return_if_fail (G_IS_OBJECT (object)); va_start (var_args, first_prop_name); swami_object_get_valist (object, first_prop_name, var_args); va_end (var_args); } /** * swami_object_set_valist: * @object: An object * @first_property_name: Name of first Swami property to set * @var_args: Pointer to get first property value from followed by * property name pointer pairs, terminated with a NULL property name. * * Set Swami properties of an object. */ void swami_object_set_valist (GObject *object, const char *first_property_name, va_list var_args) { GObjectNotifyQueue *nqueue; const char *name; g_return_if_fail (G_IS_OBJECT (object)); nqueue = g_object_notify_queue_freeze (object, object_property_notify_context); name = first_property_name; while (name) { GValue value = { 0, }; gchar *error = NULL; GParamSpec *pspec = g_param_spec_pool_lookup (object_property_pool, name, SWAMI_TYPE_ROOT, FALSE); if (!pspec) { g_warning ("%s: no Swami property named `%s'", G_STRLOC, name); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Swami property `%s' is not writable", G_STRLOC, pspec->name); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); /* we purposely leak the value here, it might not be * in a sane state if an error condition occured */ break; } object_set_property (object, pspec, &value, nqueue); g_value_unset (&value); name = va_arg (var_args, char *); } g_object_notify_queue_thaw (object, nqueue); } /** * swami_object_set_property: * @object: An object * @property_name: Name of Swami property to set * @value: Value to set the property to. The value's type must be the same * as the property's type. * * Set a Swami property of an object. */ void swami_object_set_property (GObject *object, const char *property_name, const GValue *value) { GObjectNotifyQueue *nqueue; GParamSpec *pspec; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (property_name != NULL); g_return_if_fail (G_IS_VALUE (value)); nqueue = g_object_notify_queue_freeze (object, object_property_notify_context); pspec = g_param_spec_pool_lookup (object_property_pool, property_name, SWAMI_TYPE_ROOT, FALSE); if (!pspec) g_warning ("%s: no Swami property named `%s'", G_STRLOC, property_name); else if (!(pspec->flags & G_PARAM_WRITABLE)) g_warning ("%s: Swami property `%s' is not writable", G_STRLOC, pspec->name); else object_set_property (object, pspec, value, nqueue); g_object_notify_queue_thaw (object, nqueue); } /** * swami_object_set: * @object: A GObject * @first_prop_name: Name of first Swami property to set * @...: First parameter should be the value to set the first property * value to followed by property name and pointer pairs, terminated * by a %NULL property name. * * Set multiple Swami properties on an @object. */ void swami_object_set (gpointer object, const char *first_prop_name, ...) { va_list var_args; g_return_if_fail (G_IS_OBJECT (object)); va_start (var_args, first_prop_name); swami_object_set_valist (object, first_prop_name, var_args); va_end (var_args); } /* install a Swami property */ static void swami_install_object_property (guint property_id, GParamSpec *pspec) { g_return_if_fail (property_id > 0); g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */ if (g_param_spec_pool_lookup (object_property_pool, pspec->name, SWAMI_TYPE_ROOT, FALSE)) { g_warning (G_STRLOC ": already a Swami property named `%s'", pspec->name); return; } g_param_spec_ref (pspec); g_param_spec_sink (pspec); PARAM_SPEC_SET_PARAM_ID (pspec, property_id); g_param_spec_pool_insert (object_property_pool, pspec, SWAMI_TYPE_ROOT); } /** * swami_find_object_property: * @property_name: Name of Swami property to find * * Find a Swami property. * * Returns: Parameter spec with @property_name or %NULL if not found. * Returned parameter spec is internal and should not be modified or freed. */ GParamSpec * swami_find_object_property (const char *property_name) { g_return_val_if_fail (property_name != NULL, NULL); return (g_param_spec_pool_lookup (object_property_pool, property_name, SWAMI_TYPE_ROOT, FALSE)); } /** * swami_list_object_properties: * @n_properties: Location to store number of param specs or %NULL * * Get the list of Swami properties. * * Returns: A %NULL terminated array of GParamSpecs. Free the array when * finished with it, but don't mess with the contents. */ GParamSpec ** swami_list_object_properties (guint *n_properties) { GParamSpec **pspecs; guint n; pspecs = g_param_spec_pool_list (object_property_pool, SWAMI_TYPE_ROOT, &n); if (n_properties) *n_properties = n; return (pspecs); } /** * swami_object_get_flags: * @object: Object to get Swami flags from * * Get Swami flags from an object (see #SwamiObjectFlags). * * Returns: The flags value. */ guint swami_object_get_flags (GObject *object) { SwamiObjectPropBag *propbag; g_return_val_if_fail (G_IS_OBJECT (object), 0); propbag = g_object_get_qdata (object, swami_object_propbag_quark); if (!propbag) return (0); return (propbag->flags); } /** * swami_object_set_flags: * @object: Object to set Swami flags on * @flags: Flags to set * * Sets Swami @flags in an @object. Flag bits are only set, not cleared. */ void swami_object_set_flags (GObject *object, guint flags) { SwamiObjectPropBag *propbag; g_return_if_fail (G_IS_OBJECT (object)); G_LOCK (SwamiObjectPropBag); propbag = g_object_get_qdata (object, swami_object_propbag_quark); if (!propbag) propbag = object_new_propbag (object); propbag->flags |= flags; G_UNLOCK (SwamiObjectPropBag); } /** * swami_object_clear_flags: * @object: Object to clear Swami flags on * @flags: Flags to clear * * Clears Swami @flags in an @object. Flag bits are only cleared, not set. */ void swami_object_clear_flags (GObject *object, guint flags) { SwamiObjectPropBag *propbag; g_return_if_fail (G_IS_OBJECT (object)); propbag = g_object_get_qdata (object, swami_object_propbag_quark); if (!propbag) return; propbag->flags &= ~flags; } /* dispatches swami property changes */ static void object_property_notify_dispatcher (GObject *object, guint n_pspecs, GParamSpec **pspecs) { SwamiObjectPropBag *propbag; guint i; propbag = g_object_get_qdata (object, swami_object_propbag_quark); if (!propbag || !propbag->root) return; /* rootless objects */ for (i = 0; i < n_pspecs; i++) g_signal_emit (propbag->root, root_prop_notify_signal_id, g_quark_from_string (pspecs[i]->name), pspecs[i]); } static inline SwamiObjectPropBag * object_new_propbag (GObject *object) { SwamiObjectPropBag *propbag; propbag = g_slice_new0 (SwamiObjectPropBag); g_object_set_qdata_full (object, swami_object_propbag_quark, propbag, free_propbag); return (propbag); } static void free_propbag (gpointer data) { g_slice_free (SwamiObjectPropBag, data); } /* Swami child object get property routine */ static void object_get_property (GObject *object, GParamSpec *pspec, GValue *value) { guint property_id = PARAM_SPEC_PARAM_ID (pspec); SwamiObjectPropBag *propbag; propbag = g_object_get_qdata (object, swami_object_propbag_quark); switch (property_id) { case OBJ_PROP_NAME: G_LOCK (SwamiObjectPropBag); if (propbag) g_value_set_string (value, propbag->name); else g_value_set_string (value, NULL); G_UNLOCK (SwamiObjectPropBag); break; case OBJ_PROP_RANK: if (propbag) g_value_set_int (value, propbag->rank); else g_value_set_int (value, SWAMI_RANK_NORMAL); break; case OBJ_PROP_FLAGS: if (propbag) g_value_set_flags (value, propbag->flags); else g_value_set_flags (value, 0); break; case OBJ_PROP_ROOT: if (propbag) g_value_set_object (value, propbag->root); else g_value_set_object (value, NULL); break; default: break; } } static void object_set_property (GObject *object, GParamSpec *pspec, const GValue *value, GObjectNotifyQueue *nqueue) { GValue tmp_value = { 0, }; /* provide a copy to work from, convert (if necessary) and validate */ g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); if (!g_value_transform (value, &tmp_value)) g_warning ("unable to set Swami property `%s' of type `%s' from" " value of type `%s'", pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), G_VALUE_TYPE_NAME (value)); else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { char *contents = g_strdup_value_contents (value); g_warning ("value \"%s\" of type `%s' is invalid for Swami" " property `%s' of type `%s'", contents, G_VALUE_TYPE_NAME (value), pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); g_free (contents); } else { guint property_id = PARAM_SPEC_PARAM_ID (pspec); SwamiObjectPropBag *propbag; G_LOCK (SwamiObjectPropBag); propbag = g_object_get_qdata (object, swami_object_propbag_quark); if (!propbag) propbag = object_new_propbag (object); switch (property_id) { case OBJ_PROP_NAME: g_free (propbag->name); propbag->name = g_value_dup_string (value); break; case OBJ_PROP_RANK: propbag->rank = g_value_get_int (value); break; case OBJ_PROP_FLAGS: propbag->flags = g_value_get_flags (value); break; case OBJ_PROP_ROOT: propbag->root = g_value_get_object (value); break; default: break; } G_UNLOCK (SwamiObjectPropBag); g_object_notify_queue_add (G_OBJECT (object), nqueue, pspec); } g_value_unset (&tmp_value); } /** * swami_object_set_origin: * @obj: Object to assign origin object value * @origin: Value to assign as the origin * * This is used for associating an origin object to another object. Currently * this is only used for #GtkList item selections. The current active item * selection is assigned to the swamigui_root object. In order to determine * what GUI widget is the source, this function can be used to associate the * widget object with the #GtkList selection (for example the #SwamiguiTree or * #SwamiguiSplits widgets). In this way, only the selection needs to be * assigned to the root object and the source widget can be determined. */ void swami_object_set_origin (GObject *obj, GObject *origin) { g_return_if_fail (G_IS_OBJECT (obj)); g_return_if_fail (G_IS_OBJECT (origin)); g_object_set_data (obj, "_SwamiOrigin", origin); } /** * swami_object_get_origin: * @obj: Object to get the origin object of * * See swamigui_object_set_origin() for more details. * * Returns: The origin object of @obj or NULL if no origin object assigned. * The caller owns a reference to the returned object and should unref it * when finished. */ GObject * swami_object_get_origin (GObject *obj) { GObject *origin; g_return_val_if_fail (G_IS_OBJECT (obj), NULL); origin = g_object_get_data (obj, "_SwamiOrigin"); if (origin) g_object_ref (origin); return (origin); } swami-2.2.0/src/libswami/SwamiObject.h000066400000000000000000000066371361104770400176300ustar00rootroot00000000000000/* * SwamiObject.h - Child object properties and type rank systems * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_OBJECT_H__ #define __SWAMI_OBJECT_H__ #include #include #include #include typedef struct _SwamiObjectPropBag SwamiObjectPropBag; /* structure used for Swami object properties */ struct _SwamiObjectPropBag { SwamiRoot *root; /* parent swami root object */ char *name; /* Swami object property name */ guint8 rank; /* Swami object rank property */ guint8 flags; /* Swami object flags property */ guint16 reserved; }; /* some pre-defined ranks (valid range is 1-100) */ typedef enum { SWAMI_RANK_INVALID = 0, SWAMI_RANK_LOWEST = 10, SWAMI_RANK_LOW = 25, SWAMI_RANK_NORMAL = 50, /* NORMAL default value */ SWAMI_RANK_DEFAULT = 60, /* value to elect default objects */ SWAMI_RANK_HIGH = 75, SWAMI_RANK_HIGHEST = 90 } SwamiRank; typedef enum /*< flags >*/ { SWAMI_OBJECT_SAVE = 1 << 0, /* flags if object state should be saved */ SWAMI_OBJECT_USER = 1 << 1 /* user visable object (in tree view, etc) */ } SwamiObjectFlags; extern GQuark swami_object_propbag_quark; void swami_type_set_rank (GType type, GType group_type, int rank); int swami_type_get_rank (GType type, GType group_type); GType *swami_type_get_children (GType group_type); GType swami_type_get_default (GType group_type); void swami_object_set_default (GObject *object, GType type); GObject *swami_object_get_by_name (GObject *object, const char *name); IpatchList *swami_object_find_by_type (GObject *object, const char *type_name); GObject *swami_object_get_by_type (GObject *object, const char *type_name); void swami_object_get_valist (GObject *object, const char *first_property_name, va_list var_args); void swami_object_get_property (GObject *object, const char *property_name, GValue *value); void swami_object_get (gpointer object, const char *first_prop_name, ...); void swami_object_set_valist (GObject *object, const char *first_property_name, va_list var_args); void swami_object_set_property (GObject *object, const char *property_name, const GValue *value); void swami_object_set (gpointer object, const char *first_prop_name, ...); GParamSpec *swami_find_object_property (const char *property_name); GParamSpec **swami_list_object_properties (guint *n_properties); guint swami_object_get_flags (GObject *object); void swami_object_set_flags (GObject *object, guint flags); void swami_object_clear_flags (GObject *object, guint flags); void swami_object_set_origin (GObject *obj, GObject *origin); GObject *swami_object_get_origin (GObject *obj); #endif swami-2.2.0/src/libswami/SwamiParam.c000066400000000000000000000370071361104770400174500ustar00rootroot00000000000000/* * SwamiParam.c - GParamSpec related functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include "SwamiParam.h" /** * swami_param_get_limits: * @pspec: GParamSpec to get limits of * @min: Output: Minimum value of parameter specification * @max: Output: Maximum value of parameter specification * @def: Output: Default value of parameter specification * @integer: Output: %TRUE if integer parameter spec, %FALSE if floating point * * Get limits of a numeric parameter specification. * * Returns: %TRUE if @pspec is numeric, %FALSE otherwise (in which case * output variables are undefined) */ gboolean swami_param_get_limits (GParamSpec *pspec, gdouble *min, gdouble *max, gdouble *def, gboolean *integer) { GType type; gdouble rmin, rmax, rdef; gboolean isint; g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); type = G_PARAM_SPEC_TYPE (pspec); if (type == G_TYPE_PARAM_BOOLEAN) { GParamSpecBoolean *sp = (GParamSpecBoolean *)pspec; rmin = 0.0; rmax = 1.0; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_CHAR) { GParamSpecChar *sp = (GParamSpecChar *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_UCHAR) { GParamSpecUChar *sp = (GParamSpecUChar *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_INT) { GParamSpecInt *sp = (GParamSpecInt *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_UINT) { GParamSpecUInt *sp = (GParamSpecUInt *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_LONG) { GParamSpecLong *sp = (GParamSpecLong *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_ULONG) { GParamSpecULong *sp = (GParamSpecULong *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_INT64) { GParamSpecInt64 *sp = (GParamSpecInt64 *)pspec; rmin = (gdouble)sp->minimum; rmax = (gdouble)sp->maximum; rdef = (gdouble)sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_UINT64) { GParamSpecUInt64 *sp = (GParamSpecUInt64 *)pspec; rmin = (gdouble)sp->minimum; rmax = (gdouble)sp->maximum; rdef = (gdouble)sp->default_value; isint = TRUE; } else if (type == G_TYPE_PARAM_FLOAT) { GParamSpecFloat *sp = (GParamSpecFloat *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = FALSE; } else if (type == G_TYPE_PARAM_DOUBLE) { GParamSpecDouble *sp = (GParamSpecDouble *)pspec; rmin = sp->minimum; rmax = sp->maximum; rdef = sp->default_value; isint = FALSE; } else return (FALSE); if (min) *min = rmin; if (max) *max = rmax; if (def) *def = rdef; if (integer) *integer = isint; return (TRUE); } /** * swami_param_set_limits: * @pspec: GParamSpec to set limits of * @min: Minimum value of parameter specification * @max: Maximum value of parameter specification * @def: Default value of parameter specification * * Set limits of a numeric parameter specification. * * Returns: %TRUE if @pspec is numeric, %FALSE otherwise (in which case * @pspec is unchanged) */ gboolean swami_param_set_limits (GParamSpec *pspec, gdouble min, gdouble max, gdouble def) { GType type; g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); type = G_PARAM_SPEC_TYPE (pspec); if (type == G_TYPE_PARAM_BOOLEAN) { GParamSpecBoolean *sp = (GParamSpecBoolean *)pspec; sp->default_value = (def != 0.0); } else if (type == G_TYPE_PARAM_CHAR) { GParamSpecChar *sp = (GParamSpecChar *)pspec; sp->minimum = (gint8)min; sp->maximum = (gint8)max; sp->default_value = (gint8)def; } else if (type == G_TYPE_PARAM_UCHAR) { GParamSpecUChar *sp = (GParamSpecUChar *)pspec; sp->minimum = (guint8)min; sp->maximum = (guint8)max; sp->default_value = (guint8)def; } else if (type == G_TYPE_PARAM_INT) { GParamSpecInt *sp = (GParamSpecInt *)pspec; sp->minimum = (gint)min; sp->maximum = (gint)max; sp->default_value = (gint)def; } else if (type == G_TYPE_PARAM_UINT) { GParamSpecUInt *sp = (GParamSpecUInt *)pspec; sp->minimum = (guint)min; sp->maximum = (guint)max; sp->default_value = (guint)def; } else if (type == G_TYPE_PARAM_LONG) { GParamSpecLong *sp = (GParamSpecLong *)pspec; sp->minimum = (glong)min; sp->maximum = (glong)max; sp->default_value = (glong)def; } else if (type == G_TYPE_PARAM_ULONG) { GParamSpecULong *sp = (GParamSpecULong *)pspec; sp->minimum = (gulong)min; sp->maximum = (gulong)max; sp->default_value = (gulong)def; } else if (type == G_TYPE_PARAM_INT64) { GParamSpecInt64 *sp = (GParamSpecInt64 *)pspec; sp->minimum = (gint64)min; sp->maximum = (gint64)max; sp->default_value = (gint64)def; } else if (type == G_TYPE_PARAM_UINT64) { GParamSpecUInt64 *sp = (GParamSpecUInt64 *)pspec; sp->minimum = (guint64)min; sp->maximum = (guint64)max; sp->default_value = (guint64)def; } else if (type == G_TYPE_PARAM_FLOAT) { GParamSpecFloat *sp = (GParamSpecFloat *)pspec; sp->minimum = (gfloat)min; sp->maximum = (gfloat)max; sp->default_value = (gfloat)def; } else if (type == G_TYPE_PARAM_DOUBLE) { GParamSpecDouble *sp = (GParamSpecDouble *)pspec; sp->minimum = min; sp->maximum = max; sp->default_value = def; } else return (FALSE); return (TRUE); } /** * swami_param_type_has_limits: * @param_type: #GParamSpec type * * Check if a given #GParamSpec type can be used with swami_param_get_limits() * and swami_param_set_limits(). * * Returns: %TRUE if can get/set limits, %FALSE otherwise */ gboolean swami_param_type_has_limits (GType param_type) { return (param_type == G_TYPE_PARAM_BOOLEAN || param_type == G_TYPE_PARAM_CHAR || param_type == G_TYPE_PARAM_UCHAR || param_type == G_TYPE_PARAM_INT || param_type == G_TYPE_PARAM_UINT || param_type == G_TYPE_PARAM_LONG || param_type == G_TYPE_PARAM_ULONG || param_type == G_TYPE_PARAM_INT64 || param_type == G_TYPE_PARAM_UINT64 || param_type == G_TYPE_PARAM_FLOAT || param_type == G_TYPE_PARAM_DOUBLE); } /** * swami_param_convert: * @src: Source param specification to get limits from * @dest: Destination param specification to set limits of * * Convert parameter limits between two numeric parameter specifications, also * copies the "unit-type" and "float-digit" parameter properties. Sets * "float-digits" to 0 on the @dest parameter spec if the source is an integer * type. * * Returns: %TRUE if both parameter specs are numeric, %FALSE otherwise * (in which case @dest will be unchanged). */ gboolean swami_param_convert (GParamSpec *src, GParamSpec *dest) { gdouble min, max, def; GValue value = { 0 }; gboolean isint; g_return_val_if_fail (G_IS_PARAM_SPEC (dest), FALSE); g_return_val_if_fail (G_IS_PARAM_SPEC (src), FALSE); if (!swami_param_get_limits (src, &min, &max, &def, &isint)) return (FALSE); if (!swami_param_set_limits (dest, min, max, def)) return (FALSE); g_value_init (&value, G_TYPE_UINT); if (ipatch_param_get_property (src, "unit-type", &value)) ipatch_param_set_property (dest, "unit-type", &value); if (isint) g_value_set_uint (&value, 0); if (isint || ipatch_param_get_property (src, "float-digits", &value)) ipatch_param_set_property (dest, "float-digits", &value); return (TRUE); } /** * swami_param_convert_new: * @pspec: Source parameter spec to convert * @value_type: Value type of new parameter spec * * Create a new parameter spec using values of @value_type and convert * @pspec to the new parameter spec. * * Returns: New parameter spec that uses @value_type or %NULL on error * (unable to convert @pspec to @value_type) */ GParamSpec * swami_param_convert_new (GParamSpec *pspec, GType value_type) { GParamSpec *newspec; GType newspec_type; g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); newspec_type = swami_param_type_from_value_type (value_type); g_return_val_if_fail (newspec_type != 0, NULL); newspec = g_param_spec_internal (newspec_type, pspec->name, pspec->_nick, pspec->_blurb, pspec->flags); if (!swami_param_convert (pspec, newspec)) { g_param_spec_ref (newspec); g_param_spec_sink (newspec); g_param_spec_unref (newspec); return (NULL); } return (newspec); } /** * swami_param_type_transformable: * @src_type: Source #GParamSpec type. * @dest_type: Destination #GParamSpec type. * * Check if a source #GParamSpec type is transformable to a destination type. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_param_type_transformable (GType src_type, GType dest_type) { return (swami_param_type_has_limits (src_type) && swami_param_type_has_limits (dest_type)); } /** * swami_param_type_transformable_value: * @src_type: Source #GValue type. * @dest_type: Destination #GValue type. * * Check if a source #GParamSpec type is transformable to a destination type * by corresponding #GValue types. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_param_type_transformable_value (GType src_valtype, GType dest_valtype) { GType src_type, dest_type; src_type = swami_param_type_from_value_type (src_valtype); dest_type = swami_param_type_from_value_type (dest_valtype); return (swami_param_type_has_limits (src_type) && swami_param_type_has_limits (dest_type)); } /** * swami_param_transform: * @src: Source param specification to get limits from * @dest: Destination param specification to set limits of * @trans: Transform function * @user_data: User defined data passed to transform function * * Convert parameter limits between two numeric parameter specifications using * a custom transform function. The @trans parameter specifies the transform * function which is called for the min, max and default values of the @src * parameter spec, which are then assigned to @dest. The source and destination * GValue parameters to the transform function are of #G_TYPE_DOUBLE. * * Returns: %TRUE if both parameter specs are numeric, %FALSE otherwise * (in which case @dest will be unchanged). */ gboolean swami_param_transform (GParamSpec *src, GParamSpec *dest, SwamiValueTransform trans, gpointer user_data) { gdouble min, max, def; gdouble tmin, tmax, tdef; GValue srcval = { 0 }, destval = { 0 }; g_return_val_if_fail (G_IS_PARAM_SPEC (src), FALSE); g_return_val_if_fail (G_IS_PARAM_SPEC (dest), FALSE); g_return_val_if_fail (trans != NULL, FALSE); if (!swami_param_get_limits (src, &min, &max, &def, NULL)) return (FALSE); g_value_init (&srcval, G_TYPE_DOUBLE); g_value_init (&destval, G_TYPE_DOUBLE); g_value_set_double (&srcval, min); trans (&srcval, &destval, user_data); tmin = g_value_get_double (&destval); g_value_reset (&srcval); g_value_reset (&destval); g_value_set_double (&srcval, max); trans (&srcval, &destval, user_data); tmax = g_value_get_double (&destval); g_value_reset (&srcval); g_value_reset (&destval); g_value_set_double (&srcval, def); trans (&srcval, &destval, user_data); tdef = g_value_get_double (&destval); g_value_unset (&srcval); g_value_unset (&destval); if (!swami_param_set_limits (dest, tmin, tmax, tdef)) return (FALSE); return (TRUE); } /** * swami_param_transform_new: * @pspec: Source parameter spec to convert * @value_type: Value type of new parameter spec * @trans: Transform function * @user_data: User defined data passed to transform function * * Create a new parameter spec using values of @value_type and transform * @pspec to the new parameter spec using a custom transform function. * See swami_param_transform() for more details. * * Returns: New parameter spec that uses @value_type or %NULL on error * (error message is then printed) */ GParamSpec * swami_param_transform_new (GParamSpec *pspec, GType value_type, SwamiValueTransform trans, gpointer user_data) { GParamSpec *newspec; GType newspec_type; g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); g_return_val_if_fail (trans != NULL, NULL); newspec_type = swami_param_type_from_value_type (value_type); g_return_val_if_fail (newspec_type != 0, NULL); newspec = g_param_spec_internal (newspec_type, pspec->name, pspec->_nick, pspec->_blurb, pspec->flags); if (!swami_param_transform (pspec, newspec, trans, user_data)) { g_critical ("%s: Failed to transform param spec of type '%s' to '%s'", G_STRLOC, G_PARAM_SPEC_TYPE_NAME (pspec), G_PARAM_SPEC_TYPE_NAME (newspec)); g_param_spec_ref (newspec); g_param_spec_sink (newspec); g_param_spec_unref (newspec); return (FALSE); } return (newspec); } /** * swami_param_type_from_value_type: * @value_type: A value type to get the parameter spec type that contains it. * * Get a parameter spec type for a given value type. * * Returns: Parameter spec type or 0 if no param spec type for @value_type. */ GType swami_param_type_from_value_type (GType value_type) { value_type = G_TYPE_FUNDAMENTAL (value_type); switch (value_type) { case G_TYPE_BOOLEAN: return G_TYPE_PARAM_BOOLEAN; case G_TYPE_CHAR: return G_TYPE_PARAM_CHAR; case G_TYPE_UCHAR: return G_TYPE_PARAM_UCHAR; case G_TYPE_INT: return G_TYPE_PARAM_INT; case G_TYPE_UINT: return G_TYPE_PARAM_UINT; case G_TYPE_LONG: return G_TYPE_PARAM_LONG; case G_TYPE_ULONG: return G_TYPE_PARAM_ULONG; case G_TYPE_INT64: return G_TYPE_PARAM_INT64; case G_TYPE_UINT64: return G_TYPE_PARAM_UINT64; case G_TYPE_ENUM: return G_TYPE_PARAM_ENUM; case G_TYPE_FLAGS: return G_TYPE_PARAM_FLAGS; case G_TYPE_FLOAT: return G_TYPE_PARAM_FLOAT; case G_TYPE_DOUBLE: return G_TYPE_PARAM_DOUBLE; case G_TYPE_STRING: return G_TYPE_PARAM_STRING; case G_TYPE_POINTER: return G_TYPE_PARAM_POINTER; case G_TYPE_BOXED: return G_TYPE_PARAM_BOXED; case G_TYPE_OBJECT: return G_TYPE_PARAM_OBJECT; default: return 0; } } swami-2.2.0/src/libswami/SwamiParam.h000066400000000000000000000043671361104770400174600ustar00rootroot00000000000000/* * SwamiParam.h - GParamSpec related functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_PARAM_H__ #define __SWAMI_PARAM_H__ #include #include /** * SwamiValueTransform: * @src: Source value to transform from * @dest: Destination value to store to * @user_data: User defined data (set when transform was connected for example) * * Prototype for value transform function. The transform function should * handle any value conversions required. */ typedef void (*SwamiValueTransform)(const GValue *src, GValue *dest, gpointer user_data); gboolean swami_param_get_limits (GParamSpec *pspec, gdouble *min, gdouble *max, gdouble *def, gboolean *integer); gboolean swami_param_set_limits (GParamSpec *pspec, gdouble min, gdouble max, gdouble def); gboolean swami_param_type_has_limits (GType param_type); gboolean swami_param_convert (GParamSpec *dest, GParamSpec *src); GParamSpec *swami_param_convert_new (GParamSpec *pspec, GType value_type); gboolean swami_param_type_transformable (GType src_type, GType dest_type); gboolean swami_param_type_transformable_value (GType src_valtype, GType dest_valtype); gboolean swami_param_transform (GParamSpec *dest, GParamSpec *src, SwamiValueTransform trans, gpointer user_data); GParamSpec *swami_param_transform_new (GParamSpec *dest, GType value_type, SwamiValueTransform trans, gpointer user_data); GType swami_param_type_from_value_type (GType value_type); #endif swami-2.2.0/src/libswami/SwamiPlugin.c000066400000000000000000000406711361104770400176470ustar00rootroot00000000000000/* * SwamiPlugin.c - Swami plugin system * * Swami * Copyright (C) 1999-2014 Element Green * * Inspired by gstplugin from GStreamer (although re-coded) * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include /* ipatch_gerror_message() */ #include "SwamiPlugin.h" #include "SwamiLog.h" #include "version.h" #include "i18n.h" #ifdef __APPLE__ #include #endif enum { PROP_0, PROP_MODULE, PROP_LOADED, PROP_NAME, PROP_FILENAME, PROP_VERSION, PROP_AUTHOR, PROP_COPYRIGHT, PROP_DESCR, PROP_LICENSE }; static GList *swami_plugins = NULL; /* global list of plugins */ static int plugin_seqno = 0; /* plugin counter (for stats) */ static GList *plugin_paths = NULL; /* plugin search paths */ static GObjectClass *parent_class = NULL; static void swami_plugin_class_init (SwamiPluginClass *klass); static void swami_plugin_finalize (GObject *object); static void swami_plugin_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swami_plugin_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean swami_plugin_type_module_load (GTypeModule *type_module); static void swami_plugin_type_module_unload (GTypeModule *type_module); static gboolean swami_plugin_load_recurse (char *file, char *name); /* initialize plugin system */ void _swami_plugin_initialize (void) { gchar *path = NULL; #if defined (__APPLE__) CFURLRef ResDirURL; gchar buf[PATH_MAX]; /* Use plugins within the bundle if we are in an app bundle */ ResDirURL = CFBundleCopyBuiltInPlugInsURL (CFBundleGetMainBundle()); if (CFURLGetFileSystemRepresentation (ResDirURL, TRUE, (UInt8 *)buf, PATH_MAX) && g_str_has_suffix (buf, ".app/Contents/PlugIns")) path = g_strdup (buf); CFRelease (ResDirURL); #elif defined (G_OS_WIN32) gchar *appdir; appdir = g_win32_get_package_installation_directory_of_module (NULL); if (appdir) { path = g_build_filename (appdir, "plugins", NULL); /* !! never freed */ g_free (appdir); /* -- free appdir */ } #endif if (path) plugin_paths = g_list_append (plugin_paths, path); else plugin_paths = g_list_append (plugin_paths, PLUGINS_DIR); } GType swami_plugin_get_type (void) { static GType obj_type = 0; if (!obj_type) { static GTypeInfo obj_info = { sizeof (SwamiPluginClass), NULL, NULL, (GClassInitFunc) swami_plugin_class_init, NULL, NULL, sizeof (SwamiPlugin), 0, (GInstanceInitFunc) NULL, }; obj_type = g_type_register_static (G_TYPE_TYPE_MODULE, "SwamiPlugin", &obj_info, 0); } return (obj_type); } static void swami_plugin_class_init (SwamiPluginClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_plugin_finalize; obj_class->set_property = swami_plugin_set_property; obj_class->get_property = swami_plugin_get_property; module_class->load = swami_plugin_type_module_load; module_class->unload = swami_plugin_type_module_unload; g_object_class_install_property (obj_class, PROP_MODULE, g_param_spec_pointer ("module", "Module", "GModule of plugin", G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_LOADED, g_param_spec_boolean ("loaded", "Loaded", "Loaded state of plugin", FALSE, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name of plugin", NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_FILENAME, g_param_spec_string ("file-name", "File Name", "Name of plugin file", NULL, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_VERSION, g_param_spec_string ("version", "Version", "Plugin specific version", NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_AUTHOR, g_param_spec_string ("author", "Author", "Author of the plugin", NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_COPYRIGHT, g_param_spec_string ("copyright", "Copyright", "Copyright string", NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_DESCR, g_param_spec_string ("descr", "Description", "Description of plugin", NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LICENSE, g_param_spec_string ("license", "License", "Plugin license type", NULL, G_PARAM_READWRITE)); } static void swami_plugin_finalize (GObject *object) { SwamiPlugin *plugin = SWAMI_PLUGIN (object); g_free (plugin->filename); g_free (plugin->version); g_free (plugin->author); g_free (plugin->copyright); g_free (plugin->descr); g_free (plugin->license); G_OBJECT_CLASS (parent_class)->finalize (object); } static void swami_plugin_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiPlugin *plugin = SWAMI_PLUGIN (object); switch (property_id) { case PROP_NAME: g_type_module_set_name (G_TYPE_MODULE (plugin), g_value_get_string (value)); break; case PROP_VERSION: g_free (plugin->version); plugin->version = g_value_dup_string (value); break; case PROP_AUTHOR: g_free (plugin->author); plugin->author = g_value_dup_string (value); break; case PROP_COPYRIGHT: g_free (plugin->copyright); plugin->copyright = g_value_dup_string (value); break; case PROP_DESCR: g_free (plugin->descr); plugin->descr = g_value_dup_string (value); break; case PROP_LICENSE: g_free (plugin->license); plugin->license = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_plugin_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiPlugin *plugin = SWAMI_PLUGIN (object); switch (property_id) { case PROP_MODULE: g_value_set_pointer (value, plugin->module); break; case PROP_LOADED: g_value_set_boolean (value, plugin->module != NULL); break; case PROP_NAME: g_value_set_string (value, G_TYPE_MODULE (plugin)->name); break; case PROP_FILENAME: g_value_set_string (value, plugin->filename); break; case PROP_VERSION: g_value_set_string (value, plugin->version); break; case PROP_AUTHOR: g_value_set_string (value, plugin->author); break; case PROP_COPYRIGHT: g_value_set_string (value, plugin->copyright); break; case PROP_DESCR: g_value_set_string (value, plugin->descr); break; case PROP_LICENSE: g_value_set_string (value, plugin->license); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean swami_plugin_type_module_load (GTypeModule *type_module) { SwamiPlugin *plugin = SWAMI_PLUGIN (type_module); SwamiPluginInfo *info; GError *err = NULL; g_return_val_if_fail (SWAMI_IS_PLUGIN (plugin), FALSE); g_return_val_if_fail (plugin->filename != NULL, FALSE); g_return_val_if_fail (plugin->module == NULL, FALSE); plugin->module = g_module_open (plugin->filename, G_MODULE_BIND_LAZY); if (!plugin->module) { g_critical (_("Failed to load plugin \"%s\": %s"), plugin->filename, g_module_error ()); return (FALSE); } if (!g_module_symbol (plugin->module, "swami_plugin_info", (gpointer *)&info)) { g_critical (_("Symbol \"swami_plugin_info\" not found in plugin \"%s\""), plugin->filename); goto fail; } if (strncmp (info->magic, SWAMI_PLUGIN_MAGIC, 4) != 0) { g_critical (_("Invalid Swami plugin magic number")); goto fail; } if (strcmp (info->swami_version, SWAMI_VERSION) != 0) { g_critical (_("Plugin compiled for Swami version %s but loading on %s"), info->swami_version, SWAMI_VERSION); goto fail; } if (info->init && !(*info->init)(plugin, &err)) { g_critical (_("Plugin init function failed: %s"), ipatch_gerror_message (err)); g_clear_error (&err); goto fail; } plugin->init = info->init; plugin->exit = info->exit; return (TRUE); fail: g_module_close (plugin->module); plugin->module = NULL; return (FALSE); } static void swami_plugin_type_module_unload (GTypeModule *type_module) { SwamiPlugin *plugin = SWAMI_PLUGIN (type_module); g_return_if_fail (plugin->module != NULL); if (plugin->exit) /* call the plugin's exit function (if any) */ (*plugin->exit)(plugin); plugin->init = NULL; plugin->exit = NULL; g_module_close (plugin->module); plugin->module = NULL; } /** * swami_plugin_add_path: * @path: The directory to add to the search path * * Add a directory to the path searched for plugins. */ void swami_plugin_add_path (const char *path) { plugin_paths = g_list_append (plugin_paths, g_strdup (path)); } /** * swami_plugin_load_all: * * Load all plugins in the plugin search path. */ void swami_plugin_load_all (void) { GList *path; path = plugin_paths; if (path == NULL) g_warning ("Plugin search path is empty"); while (path) { /* g_message (_("Loading plugins from %s"), (char *)path->data); */ swami_plugin_load_recurse (path->data, NULL); path = g_list_next (path); } /* g_message (_("Loaded %d plugins"), plugin_seqno); */ } static gboolean swami_plugin_load_recurse (char *file, char *name) { GDir *dir; const gchar *d_name; gboolean loaded = FALSE; char *dirname, *temp; dir = g_dir_open (file, 0, NULL); if (dir) /* is "file" a directory or file? */ { while ((d_name = g_dir_read_name (dir))) /* its a directory, open it */ { /* don't want to recurse in place or backwards */ if (strcmp (d_name, ".") && strcmp (d_name, "..")) { dirname = g_strjoin (G_DIR_SEPARATOR_S, file, d_name, NULL); loaded = swami_plugin_load_recurse (dirname, name); g_free (dirname); if (loaded && name) /* done searching for a specific plugin? */ { g_dir_close (dir); return TRUE; } } } g_dir_close (dir); } else { /* search for shared library extension */ temp = g_strrstr (file, "." G_MODULE_SUFFIX); /* make sure file ends with shared library extension */ if (temp && strcmp (temp, "." G_MODULE_SUFFIX) == 0) { /* if we aren't searching for a specific plugin or this file name matches the search, load the plugin */ if (!name || ((temp = strstr (file, name)) && strcmp (temp, name) == 0)) loaded = swami_plugin_load_absolute (file); } } return (loaded); } /** * swami_plugin_load: * @filename: File name of plugin to load * * Load the named plugin. Name should be the plugin file name * ("libplugin.so" on Linux for example). * * Returns: Whether the plugin was loaded or not */ gboolean swami_plugin_load (const char *filename) { GList *path; char *pluginname; SwamiPlugin *plugin; g_return_val_if_fail (filename != NULL, FALSE); plugin = swami_plugin_find (filename); if (plugin && plugin->module) return (TRUE); path = plugin_paths; while (path) { pluginname = g_module_build_path (path->data, filename); if (swami_plugin_load_absolute (pluginname)) { g_free (pluginname); return (TRUE); } g_free (pluginname); path = g_list_next (path); } return (FALSE); } /** * swami_plugin_load_absolute: * @filename: Name of plugin to load * * Load the named plugin. Name should be the full path and file name of the * plugin to load. * * Returns: whether the plugin was loaded or not */ gboolean swami_plugin_load_absolute (const char *filename) { SwamiPlugin *plugin = NULL; GList *plugins = swami_plugins; g_return_val_if_fail (filename != NULL, FALSE); while (plugins) /* see if plugin already exists */ { SwamiPlugin *testplugin = (SwamiPlugin *)plugins->data; if (testplugin->filename && strcmp (testplugin->filename, filename) == 0) { plugin = testplugin; break; } plugins = g_list_next (plugins); } if (!plugin) { plugin = g_object_new (SWAMI_TYPE_PLUGIN, NULL); plugin->filename = g_strdup (filename); swami_plugins = g_list_prepend (swami_plugins, plugin); plugin_seqno++; } return (g_type_module_use (G_TYPE_MODULE (plugin))); } /** * swami_plugin_load_plugin: * @plugin: The plugin to load * * Load the given plugin. * * Returns: %TRUE on success (loaded or already loaded), %FALSE otherwise */ gboolean swami_plugin_load_plugin (SwamiPlugin *plugin) { g_return_val_if_fail (SWAMI_IS_PLUGIN (plugin), FALSE); if (plugin->module) return (TRUE); return (g_type_module_use (G_TYPE_MODULE (plugin))); } /** * swami_plugin_is_loaded: * @plugin: Plugin to query * * Queries if the plugin is loaded into memory * * Returns: %TRUE is loaded, %FALSE otherwise */ gboolean swami_plugin_is_loaded (SwamiPlugin *plugin) { g_return_val_if_fail (plugin != NULL, FALSE); return (plugin->module != NULL); } /** * swami_plugin_find: * @name: Name of plugin to find * * Search the list of registered plugins for one with the given plugin name * (not file name). * * Returns: Pointer to the #SwamiPlugin if found, %NULL otherwise */ SwamiPlugin * swami_plugin_find (const char *name) { GList *plugins = swami_plugins; g_return_val_if_fail (name != NULL, NULL); while (plugins) { SwamiPlugin *plugin = (SwamiPlugin *)plugins->data; if (G_TYPE_MODULE (plugin)->name && strcmp (G_TYPE_MODULE (plugin)->name, name) == 0) return (plugin); plugins = g_list_next (plugins); } return (NULL); } /** * swami_plugin_get_list: * * get the currently loaded plugins * * Returns: a GList of SwamiPlugin elements which should be freed with * g_list_free when finished with. */ GList * swami_plugin_get_list (void) { return (g_list_copy (swami_plugins)); } /** * swami_plugin_save_xml: * @plugin: Swami plugin * @xmlnode: XML node to save plugin state to * @err: Location to store error info or %NULL to ignore * * Save a plugin's preferences state to XML. Not all plugins implement this * method. Check if the xml_save field is set for @plugin before calling, just * returns %TRUE if not implemented. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean swami_plugin_save_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err) { g_return_val_if_fail (SWAMI_IS_PLUGIN (plugin), FALSE); g_return_val_if_fail (xmlnode != NULL, FALSE); g_return_val_if_fail (!err || !*err, FALSE); if (!plugin->save_xml) return (TRUE); return (plugin->save_xml (plugin, xmlnode, err)); } /** * swami_plugin_load_xml: * @plugin: Swami plugin * @xmlnode: XML node to load plugin state from * @err: Location to store error info or %NULL to ignore * * Load a plugin's preferences state from XML. Not all plugins implement this * method. Check if the xml_load field is set for @plugin before calling, just * returns %TRUE if not implemented. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean swami_plugin_load_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err) { g_return_val_if_fail (SWAMI_IS_PLUGIN (plugin), FALSE); g_return_val_if_fail (xmlnode != NULL, FALSE); g_return_val_if_fail (!err || !*err, FALSE); if (!plugin->load_xml) return (TRUE); return (plugin->load_xml (plugin, xmlnode, err)); } swami-2.2.0/src/libswami/SwamiPlugin.h000066400000000000000000000127621361104770400176540ustar00rootroot00000000000000/* * SwamiPlugin.h - Header file for Swami plugin system * * Swami * Copyright (C) 1999-2014 Element Green * * Inspired by gstplugin from GStreamer (although re-coded) * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_PLUGIN_H__ #define __SWAMI_PLUGIN_H__ #include #include #include #include typedef struct _SwamiPlugin SwamiPlugin; typedef struct _SwamiPluginClass SwamiPluginClass; typedef struct _SwamiPluginInfo SwamiPluginInfo; #define SWAMI_TYPE_PLUGIN (swami_plugin_get_type ()) #define SWAMI_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_PLUGIN, SwamiPlugin)) #define SWAMI_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_PLUGIN, SwamiPluginClass)) #define SWAMI_IS_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_PLUGIN)) #define SWAMI_IS_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_PLUGIN)) /** * SwamiPluginInitFunc: * @plugin: Plugin object being loaded * @err: Location to store error information * * A function type called after a plugin has been loaded. * * Returns: Should return %TRUE on success, %FALSE otherwise (@err should be * set on failure). */ typedef gboolean (*SwamiPluginInitFunc)(SwamiPlugin *plugin, GError **err); /** * SwamiPluginExitFunc: * @plugin: Plugin object being unloaded * * A function type called before a plugin is unloaded. */ typedef void (*SwamiPluginExitFunc)(SwamiPlugin *plugin); /** * SwamiPluginSaveXmlFunc: * @plugin: Plugin object to save preference state of * @xmlnode: #IpatchXmlNode tree to save preferences to * @err: Location to store error info to * * An optional function type which is called to save a plugin's preference state to * an XML tree. The passed in @xmlnode is an XML node created for this plugin. * * Returns: Should return %TRUE on success, %FALSE otherwise (in which case @err * should be set). */ typedef gboolean (*SwamiPluginSaveXmlFunc)(SwamiPlugin *plugin, GNode *xmlnode, GError **err); /** * SwamiPluginLoadXmlFunc: * @plugin: Plugin object to load preference state to * @xmlnode: #IpatchXmlNode tree to load preferences from * @err: Location to store error info to * * An optional function type which is called to load a plugin's preference state from * an XML tree. The passed in @xmlnode is an XML node for this plugin. * * Returns: Should return %TRUE on success, %FALSE otherwise (in which case @err * should be set). */ typedef gboolean (*SwamiPluginLoadXmlFunc)(SwamiPlugin *plugin, GNode *xmlnode, GError **err); /* Swami plugin object (each loaded plugin gets one of these) */ struct _SwamiPlugin { GTypeModule parent_instance; /* derived from GTypeModule */ /*< private >*/ GModule *module; /* module of the plugin or NULL if not loaded */ SwamiPluginInitFunc init; /* function stored from SwamiPluginInfo */ SwamiPluginExitFunc exit; /* function stored from SwamiPluginInfo */ SwamiPluginSaveXmlFunc save_xml; /* Optional XML save function assigned in init */ SwamiPluginLoadXmlFunc load_xml; /* Optional XML load function assigned in init */ char *filename; /* filename it came from */ char *version; /* plugin specific version string */ char *author; /* author of this plugin */ char *copyright; /* copyright string */ char *descr; /* description of plugin */ char *license; /* license this plugin is distributed under */ }; /* Swami plugin class */ struct _SwamiPluginClass { GTypeModuleClass parent_class; }; /* magic string to check sanity of plugins */ //#define SWAMI_PLUGIN_MAGIC GUINT_FROM_BE(0x53574D49) #define SWAMI_PLUGIN_MAGIC "SWMI" struct _SwamiPluginInfo { char magic[4]; /* magic string to ensure sanity */ char *swami_version; /* version of Swami plugin compiled for */ SwamiPluginInitFunc init; /* called to initialize plugin */ SwamiPluginExitFunc exit; /* called before plugin is unloaded */ }; /* a convenience macro to define plugin info */ #define SWAMI_PLUGIN_INFO(init, exit) \ SwamiPluginInfo swami_plugin_info = \ { \ SWAMI_PLUGIN_MAGIC, \ SWAMI_VERSION, \ init, \ exit \ }; GType swami_plugin_get_type (void); void swami_plugin_add_path (const char *path); void swami_plugin_load_all (void); gboolean swami_plugin_load (const char *filename); gboolean swami_plugin_load_absolute (const char *filename); gboolean swami_plugin_load_plugin (SwamiPlugin *plugin); gboolean swami_plugin_is_loaded (SwamiPlugin *plugin); SwamiPlugin *swami_plugin_find (const char *name); GList *swami_plugin_get_list (void); gboolean swami_plugin_save_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err); gboolean swami_plugin_load_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err); #endif /* __SWAMI_PLUGIN_H__ */ swami-2.2.0/src/libswami/SwamiPropTree.c000066400000000000000000000661511361104770400201520ustar00rootroot00000000000000/* * SwamiPropTree.c - Swami property tree object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiPropTree.h" #include "SwamiControlProp.h" #include "SwamiLog.h" #include "swami_priv.h" /* a cached active property value */ typedef struct { GParamSpec *pspec; /* parameter spec for this cached property */ SwamiControl *prop_ctrl; /* object property control for this cache */ SwamiPropTreeValue *value; /* tree value connected to prop_ctrl */ SwamiPropTreeNode *value_node; /* node containing @value */ } CacheValue; /* unlocked chunk alloc/free macros (requires external locking) */ #define swami_prop_tree_new_node_L() \ g_slice_new (SwamiPropTreeNode) #define swami_prop_tree_free_node_L(node) \ g_slice_free (SwamiPropTreeNode, node) #define swami_prop_tree_new_value_L() \ g_slice_new (SwamiPropTreeValue) #define swami_prop_tree_free_value_L(value) \ g_slice_free (SwamiPropTreeValue, value) #define TREE_CACHE_PREALLOC 64 #define swami_prop_tree_new_cache_L() \ g_slice_new (CacheValue) #define swami_prop_tree_free_cache_L(cache) \ g_slice_free (CacheValue, cache) static void swami_prop_tree_class_init (SwamiPropTreeClass *klass); static void swami_prop_tree_init (SwamiPropTree *proptree); static void swami_prop_tree_finalize (GObject *object); static void swami_prop_tree_object_weak_notify (gpointer user_data, GObject *object); static inline void swami_prop_tree_node_reset_L (SwamiPropTree *proptree, SwamiPropTreeNode *treenode); static inline void swami_prop_tree_node_clear_cache_L (SwamiPropTreeNode *treenode); static void recursive_remove_nodes (GNode *node, SwamiPropTree *proptree); static void resolve_object_props_L (SwamiPropTree *proptree, GNode *object_node, GList *speclist); static GList *object_spec_list (GObject *object); static void refresh_value_nodes_L (GNode *node, SwamiPropTreeValue *treeval); static void refresh_value_nodes_list_L (GNode *node, GSList *treevals); static inline void refresh_cache_value_L (GNode *node, CacheValue *cache); static GObjectClass *parent_class = NULL; GType swami_prop_tree_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiPropTreeClass), NULL, NULL, (GClassInitFunc) swami_prop_tree_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiPropTree), 0, (GInstanceInitFunc) swami_prop_tree_init }; otype = g_type_register_static (SWAMI_TYPE_LOCK, "SwamiPropTree", &type_info, 0); } return (otype); } static void swami_prop_tree_class_init (SwamiPropTreeClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swami_prop_tree_finalize; } static void swami_prop_tree_init (SwamiPropTree *proptree) { proptree->tree = NULL; proptree->object_hash = g_hash_table_new (NULL, NULL); } static void swami_prop_tree_finalize (GObject *object) { SwamiPropTree *proptree = SWAMI_PROP_TREE (object); SWAMI_LOCK_WRITE (proptree); if (proptree->tree) recursive_remove_nodes (proptree->tree, proptree); /* recursive remove */ g_hash_table_destroy (proptree->object_hash); SWAMI_UNLOCK_WRITE (proptree); if (parent_class->finalize) parent_class->finalize (object); } /** * swami_prop_tree_new: * * Create a new property tree object. * * Returns: New property tree object with a refcount of 1. */ SwamiPropTree * swami_prop_tree_new (void) { return (SWAMI_PROP_TREE (g_object_new (SWAMI_TYPE_PROP_TREE, NULL))); } /** * swami_prop_tree_set_root: * @proptree: Property tree object * @root: Object to make the root object of the tree * * Set the root object of a property tree. Should only be set once. */ void swami_prop_tree_set_root (SwamiPropTree *proptree, GObject *root) { SwamiPropTreeNode *node; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (root)); SWAMI_LOCK_WRITE (proptree); if (swami_log_if_fail (proptree->tree == NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } node = swami_prop_tree_new_node_L (); node->object = root; node->values = NULL; node->cache = NULL; proptree->tree = g_node_new (node); /* add to object => GNode hash */ g_hash_table_insert (proptree->object_hash, root, proptree->tree); /* weak ref to passively catch objects demise */ g_object_weak_ref (root, swami_prop_tree_object_weak_notify, proptree); SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_prepend: * @proptree: Property tree object * @parent: Object in @proptree to parent to * @obj: Object to prepend to @proptree * * Prepends an object to a property tree. */ void swami_prop_tree_prepend (SwamiPropTree *proptree, GObject *parent, GObject *obj) { SwamiPropTreeNode *treenode; GNode *parent_node, *newnode; GList *speclist; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (parent)); g_return_if_fail (G_IS_OBJECT (obj)); speclist = object_spec_list (obj); SWAMI_LOCK_WRITE (proptree); parent_node = g_hash_table_lookup (proptree->object_hash, parent); if (swami_log_if_fail (parent_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } treenode = swami_prop_tree_new_node_L (); treenode->object = obj; treenode->values = NULL; treenode->cache = NULL; newnode = g_node_prepend_data (parent_node, treenode); g_hash_table_insert (proptree->object_hash, obj, newnode); /* weak ref to passively catch objects demise */ g_object_weak_ref (obj, swami_prop_tree_object_weak_notify, proptree); /* resolve properties if any (speclist is freed) */ if (speclist) resolve_object_props_L (proptree, newnode, speclist); SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_insert_before: * @proptree: Property tree object * @parent: Object in @proptree to parent to * @sibling: Object in @proptree to insert before or %NULL to append * @obj: Object to prepend to @proptree * * Inserts an object to a property tree before @sibling and parented to * @parent. */ void swami_prop_tree_insert_before (SwamiPropTree *proptree, GObject *parent, GObject *sibling, GObject *obj) { SwamiPropTreeNode *treenode; GNode *parent_node, *sibling_node = NULL, *newnode; GList *speclist; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (parent)); g_return_if_fail (!sibling || G_IS_OBJECT (sibling)); g_return_if_fail (G_IS_OBJECT (obj)); speclist = object_spec_list (obj); SWAMI_LOCK_WRITE (proptree); parent_node = g_hash_table_lookup (proptree->object_hash, parent); if (swami_log_if_fail (parent_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } if (sibling) { sibling_node = g_hash_table_lookup (proptree->object_hash, sibling); if (swami_log_if_fail (sibling_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } } treenode = swami_prop_tree_new_node_L (); treenode->object = obj; treenode->values = NULL; treenode->cache = NULL; newnode = g_node_insert_data_before (parent_node, sibling_node, treenode); g_hash_table_insert (proptree->object_hash, obj, newnode); /* weak ref to passively catch objects demise */ g_object_weak_ref (obj, swami_prop_tree_object_weak_notify, proptree); /* resolve properties if any (speclist is freed) */ if (speclist) resolve_object_props_L (proptree, newnode, speclist); SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_remove: * @proptree: Property tree object * @obj: Object in @proptree to remove * * Removes an @obj, and all values bound to it, from a property tree. * All child nodes are moved up to the next parent node. */ void swami_prop_tree_remove (SwamiPropTree *proptree, GObject *obj) { SwamiPropTreeNode *treenode; GNode *obj_node, *newparent, *n, *temp; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (obj)); SWAMI_LOCK_WRITE (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, obj); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } newparent = obj_node->parent; if (swami_log_if_fail (newparent != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } treenode = (SwamiPropTreeNode *)(obj_node->data); g_node_unlink (obj_node); /* unlink the GNode */ n = g_node_last_child (obj_node); while (n) /* move children of removed node to parent of it and refresh */ { temp = n; n = n->prev; g_node_prepend (newparent, temp); /* recursive refresh */ if (treenode->values) refresh_value_nodes_list_L (n, treenode->values); } obj_node->children = NULL; g_node_destroy (obj_node); /* destroy the GNode */ /* reset and free the tree node */ swami_prop_tree_node_reset_L (proptree, treenode); swami_prop_tree_free_node_L (treenode); SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_remove_recursive: * @proptree: Property tree object * @obj: Object in @proptree to recursively remove * * Recursively removes an @object, and all values bound to it, from a property * tree. */ void swami_prop_tree_remove_recursive (SwamiPropTree *proptree, GObject *obj) { GNode *obj_node; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (obj)); SWAMI_LOCK_WRITE (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, obj); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } recursive_remove_nodes (obj_node, proptree); /* recursive remove */ if (obj_node == proptree->tree) proptree->tree = NULL; SWAMI_UNLOCK_WRITE (proptree); } /* called when an object in the tree is destroyed */ static void swami_prop_tree_object_weak_notify (gpointer user_data, GObject *object) { SwamiPropTree *proptree = SWAMI_PROP_TREE (user_data); GNode *obj_node; SWAMI_LOCK_WRITE (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, object); if (obj_node && proptree->tree) { ((SwamiPropTreeNode *)(obj_node->data))->object = NULL; recursive_remove_nodes (obj_node, proptree); /* recursive remove */ if (obj_node == proptree->tree) proptree->tree = NULL; } SWAMI_UNLOCK_WRITE (proptree); } /* reset a SwamiPropTreeNode (remove all of its innards) */ static inline void swami_prop_tree_node_reset_L (SwamiPropTree *proptree, SwamiPropTreeNode *treenode) { GSList *p; if (treenode->object) /* clear object->treenode hash entry */ { g_object_weak_unref (treenode->object, /* -- remove the weak ref */ swami_prop_tree_object_weak_notify, proptree); g_hash_table_remove (proptree->object_hash, treenode->object); } p = treenode->values; while (p) /* destroy values */ { swami_prop_tree_free_value_L (p->data); p = g_slist_delete_link (p, p); } swami_prop_tree_node_clear_cache_L (treenode); } /* clear cache of a property tree node */ static inline void swami_prop_tree_node_clear_cache_L (SwamiPropTreeNode *treenode) { CacheValue *cache; GSList *p; p = treenode->cache; while (p) /* destroy cache */ { cache = (CacheValue *)(p->data); if (cache->prop_ctrl) /* destroy property control */ { swami_control_disconnect_all ((SwamiControl *)(cache->prop_ctrl)); g_object_unref (cache->prop_ctrl); /* -- unref from cache */ } swami_prop_tree_free_cache_L (p->data); /* free cache value */ p = g_slist_delete_link (p, p); } treenode->cache = NULL; } /* recursively remove SwamiPropTreeNodes */ static void recursive_remove_nodes (GNode *node, SwamiPropTree *proptree) { GNode *n; n = node->children; while (n) { recursive_remove_nodes (n, proptree); n = n->next; } { SwamiPropTreeNode *treenode = (SwamiPropTreeNode *)(node->data); swami_prop_tree_node_reset_L (proptree, treenode); swami_prop_tree_free_node_L (treenode); /* free node */ g_node_destroy (node); } } /** * swami_prop_tree_replace: * @proptree: Property tree object * @old: Old object in @proptree to replace * @new: New object to replace @old object with * * Replaces an @old object with a @new object in a property tree. */ void swami_prop_tree_replace (SwamiPropTree *proptree, GObject *old, GObject *new) { SwamiPropTreeNode *treenode; GNode *obj_node; GList *speclist; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (old)); g_return_if_fail (G_IS_OBJECT (new)); speclist = object_spec_list (new); /* get GParamSpec list for new object */ SWAMI_LOCK_WRITE (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, old); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); g_list_free (speclist); return; } treenode = (SwamiPropTreeNode *)(obj_node->data); /* clear old cache and remove old object hash entry */ swami_prop_tree_node_clear_cache_L (treenode); g_hash_table_remove (proptree->object_hash, old); g_hash_table_insert (proptree->object_hash, new, obj_node); /* weak ref to passively catch objects demise */ g_object_weak_ref (new, swami_prop_tree_object_weak_notify, proptree); /* re-resolve properties if any (speclist is freed) */ if (speclist) resolve_object_props_L (proptree, obj_node, speclist); SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_get_children: * @proptree: Property tree object * @obj: Object in @proptree to get children of * * Gets the list of GObject children of @obj in a property tree. * * Returns: A new object list populated with the children of @obj in @proptree. * The new list has a reference count of 1 which the caller owns, remember to * unref it when finished. */ IpatchList * swami_prop_tree_get_children (SwamiPropTree *proptree, GObject *obj) { IpatchList *list; SwamiPropTreeNode *treenode; GNode *obj_node, *n; g_return_val_if_fail (SWAMI_IS_PROP_TREE (proptree), NULL); g_return_val_if_fail (G_IS_OBJECT (obj), NULL); list = ipatch_list_new (); /* ++ ref new list */ SWAMI_LOCK_READ (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, obj); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_READ (proptree); g_object_unref (list); /* -- unref list */ return (NULL); } n = obj_node->children; while (n) { treenode = (SwamiPropTreeNode *)(n->data); g_object_ref (treenode->object); /* ++ ref for list */ list->items = g_list_prepend (list->items, treenode->object); n = n->next; } SWAMI_UNLOCK_READ (proptree); list->items = g_list_reverse (list->items); return (list); } /** * swami_prop_tree_get_node: * @proptree: Property tree object * @obj: Object in @proptree to get GNode of * * Gets the GNode of an object in a property tree. This should only be done * for objects which are sure to remain in the property tree for the duration * of GNode use. * * Returns: GNode of @obj in property tree. GNode can only be used for as * long as the object is in the tree. */ GNode * swami_prop_tree_object_get_node (SwamiPropTree *proptree, GObject *obj) { GNode *node; g_return_val_if_fail (SWAMI_IS_PROP_TREE (proptree), NULL); g_return_val_if_fail (G_IS_OBJECT (obj), NULL); SWAMI_LOCK_READ (proptree); node = g_hash_table_lookup (proptree->object_hash, obj); SWAMI_UNLOCK_READ (proptree); return (node); } /** * swami_prop_tree_add_value: * @proptree: Property tree object * @obj: Object in @proptree * @prop_type: GObject derived type the value should match (0 = wildcard) * @prop_name: Property name to match * @control: Active value control * * Adds a value to an object in a property tree. If a value already exists * with the same @prop_type and @prop_name its @control value is replaced. */ void swami_prop_tree_add_value (SwamiPropTree *proptree, GObject *obj, GType prop_type, const char *prop_name, SwamiControl *control) { SwamiPropTreeValue *treeval; SwamiPropTreeNode *treenode; GNode *obj_node; GSList *p; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (obj)); g_return_if_fail (!prop_type || g_type_is_a (prop_type, G_TYPE_OBJECT)); g_return_if_fail (prop_name != NULL && *prop_name != '\0'); g_return_if_fail (SWAMI_IS_CONTROL (control)); SWAMI_LOCK_WRITE (proptree); obj_node = g_hash_table_lookup (proptree->object_hash, obj); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } treenode = (SwamiPropTreeNode *)(obj_node->data); g_object_ref (control); /* ++ ref control for tree value */ p = treenode->values; while (p) /* look for existing duplicate tree value */ { treeval = (SwamiPropTreeValue *)(p->data); if (treeval->prop_type == prop_type && strcmp (treeval->prop_name, prop_name) == 0) { g_object_unref (control); /* -- unref old control */ break; } p = g_slist_next (p); } /* if a matching value doesn't already exist, allocate a new one */ if (!p) { treeval = swami_prop_tree_new_value_L (); treeval->prop_type = prop_type; treeval->prop_name = g_strdup (prop_name); treenode->values = g_slist_prepend (treenode->values, treeval); } treeval->control = control; refresh_value_nodes_L (obj_node, treeval); /* refresh nodes */ SWAMI_UNLOCK_WRITE (proptree); } /** * swami_prop_tree_remove_value: * @proptree: Property tree object * @obj: Object in @proptree * @prop_type: GObject derived type field of existing value * @prop_name: Property name field of existing value * * Removes a value from an object in a property tree. The @prop_type and * @prop_name parameters are used to find the value to remove. */ void swami_prop_tree_remove_value (SwamiPropTree *proptree, GObject *obj, GType prop_type, const char *prop_name) { SwamiPropTreeValue *treeval; SwamiPropTreeNode *treenode; GNode *obj_node; GSList *p, *prev = NULL; g_return_if_fail (SWAMI_IS_PROP_TREE (proptree)); g_return_if_fail (G_IS_OBJECT (obj)); g_return_if_fail (!prop_type || g_type_is_a (prop_type, G_TYPE_OBJECT)); g_return_if_fail (prop_name != NULL && *prop_name != '\0'); obj_node = g_hash_table_lookup (proptree->object_hash, obj); if (swami_log_if_fail (obj_node != NULL)) { SWAMI_UNLOCK_WRITE (proptree); return; } treenode = (SwamiPropTreeNode *)(obj_node->data); p = treenode->values; while (p) /* loop over values */ { treeval = (SwamiPropTreeValue *)(p->data); if (treeval->prop_type == prop_type /* tree value matches? */ && strcmp (treeval->prop_name, prop_name) == 0) break; prev = p; p = g_slist_next (p); } if (p) /* if a match was found */ { /* quick remove the value from the list */ if (prev) prev->next = p->next; else treenode->values = p->next; refresh_value_nodes_L (obj_node, treeval); /* refresh nodes */ /* free the tree value */ g_free (treeval->prop_name); g_object_unref (treeval->control); /* -- unref control */ swami_prop_tree_free_value_L (treeval); } SWAMI_UNLOCK_WRITE (proptree); } /* one time property resolve and cache function */ static void resolve_object_props_L (SwamiPropTree *proptree, GNode *object_node, GList *speclist) { SwamiPropTreeNode *treenode, *obj_treenode; SwamiPropTreeValue *treeval; SwamiControl *prop_ctrl; CacheValue *cache; GParamSpec *pspec; GType obj_type; GNode *n; GList *sp, *temp; GSList *p; int flags; obj_treenode = (SwamiPropTreeNode *)(object_node->data); obj_type = G_TYPE_FROM_INSTANCE (obj_treenode->object); n = object_node; do { /* loop over tree ancestry */ treenode = (SwamiPropTreeNode *)(n->data); p = treenode->values; while (p) /* loop over variables in each node */ { treeval = (SwamiPropTreeValue *)(p->data); /* object type matches? */ if (!treeval->prop_type || (treeval->prop_type == obj_type)) { sp = speclist; while (sp) /* loop over remaining object param specs */ { pspec = (GParamSpec *)(sp->data); if (strcmp (pspec->name, treeval->prop_name) == 0) { /* property name matches */ /* create a new object property control */ prop_ctrl = swami_get_control_prop_by_name (obj_treenode->object, pspec->name); flags = SWAMI_CONTROL_CONN_INIT; if (swami_control_get_flags (prop_ctrl) & SWAMI_CONTROL_SENDS) flags |= SWAMI_CONTROL_CONN_BIDIR; /* connect the tree value control to the property control and initialize the property to the tree value */ swami_control_connect (treeval->control, (SwamiControl *)prop_ctrl, flags); /* create cache value and add it to object tree node */ cache = swami_prop_tree_new_cache_L (); cache->pspec = pspec; cache->prop_ctrl = prop_ctrl; cache->value = treeval; cache->value_node = treenode; obj_treenode->cache = g_slist_prepend (obj_treenode->cache, cache); temp = sp; sp = g_list_next (sp); /* delete param spec from find spec list */ speclist = g_list_delete_link (speclist, temp); if (!speclist) goto done; /* no more properties? */ } else sp = g_list_next (sp); } } p = g_slist_next (p); } } while ((n = n->parent)); done: while (speclist) /* loop over remaining parameter specs */ { pspec = (GParamSpec *)(speclist->data); /* create an "unset" cache value */ cache = swami_prop_tree_new_cache_L (); cache->pspec = pspec; cache->prop_ctrl = NULL; cache->value = NULL; cache->value_node = NULL; obj_treenode->cache = g_slist_prepend (obj_treenode->cache, cache); /* free each node of the spec list */ speclist = g_list_delete_link (speclist, speclist); } } /* create a GList of parameter specs for a given object */ static GList * object_spec_list (GObject *object) { GParamSpec **pspecs, **spp; GList *speclist = NULL; pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), NULL); spp = pspecs; while (*spp) /* create a param spec list */ { speclist = g_list_prepend (speclist, *spp); spp++; } g_free (pspecs); /* no longer need array */ return (speclist); } /* recursively refresh cache values affected by a tree value */ static void refresh_value_nodes_L (GNode *node, SwamiPropTreeValue *treeval) { GNode *n; n = node->children; while (n) { refresh_value_nodes_L (n, treeval); n = n->next; } { SwamiPropTreeNode *treenode = (SwamiPropTreeNode *)(node->data); GType obj_type = G_TYPE_FROM_INSTANCE (treenode->object); char *prop_name = treeval->prop_name; CacheValue *cache; GSList *p; /* object type matches type criteria of treeval? */ if (treeval->prop_type && treeval->prop_type != obj_type) return; p = treenode->cache; while (p) /* loop over the node's cache */ { cache = (CacheValue *)(p->data); if (strcmp (prop_name, cache->pspec->name) == 0) break; p = g_slist_next (p); } if (p) refresh_cache_value_L (node, cache); } } /* recursively refresh cache values affected by a list of tree values */ static void refresh_value_nodes_list_L (GNode *node, GSList *treevals) { GNode *n; n = node->children; while (n) { refresh_value_nodes_list_L (n, treevals); n = n->next; } { SwamiPropTreeNode *treenode = (SwamiPropTreeNode *)(node->data); GType obj_type = G_TYPE_FROM_INSTANCE (treenode->object); SwamiPropTreeValue *treeval; CacheValue *cache; GSList *p, *p2; p = treevals; while (p) /* loop over list of tree values to match for refresh */ { treeval = (SwamiPropTreeValue *)(p->data); /* treeval matches type criteria of treeval? */ if (!treeval->prop_type || treeval->prop_type == obj_type) { p2 = treenode->cache; while (p2) /* loop over the node's cache */ { cache = (CacheValue *)(p2->data); /* prop name matches? - refresh cache val */ if (strcmp (treeval->prop_name, cache->pspec->name) == 0) refresh_cache_value_L (node, cache); p2 = g_slist_next (p2); } } p = g_slist_next (p); } } } /* refreshes a single cache value in a tree node */ static inline void refresh_cache_value_L (GNode *node, CacheValue *cache) { SwamiPropTreeNode *treenode, *obj_treenode; SwamiPropTreeValue *treeval; const char *prop_name; GType obj_type; GSList *p; int flags; obj_treenode = (SwamiPropTreeNode *)(node->data); obj_type = G_TYPE_FROM_INSTANCE (obj_treenode->object); prop_name = cache->pspec->name; do { /* loop over tree ancestry */ treenode = (SwamiPropTreeNode *)(node->data); p = treenode->values; while (p) /* loop over variables in each node */ { treeval = (SwamiPropTreeValue *)(p->data); /* property type and name matches? */ if ((!treeval->prop_type || (treeval->prop_type == obj_type)) && strcmp (prop_name, treeval->prop_name) == 0) { /* property name matches */ if (cache->value == treeval) return; /* cache is correct? */ if (!cache->prop_ctrl) /* no existing property control? */ cache->prop_ctrl = /* ++ ref new property control */ swami_get_control_prop_by_name (obj_treenode->object, prop_name); else /* disconnect current property control */ swami_control_disconnect_all ((SwamiControl *)(cache->prop_ctrl)); /* update the cached tree value */ cache->value = treeval; cache->value_node = treenode; flags = SWAMI_CONTROL_CONN_INIT; if (swami_control_get_flags (cache->prop_ctrl) & SWAMI_CONTROL_SENDS) flags |= SWAMI_CONTROL_CONN_BIDIR; /* connect tree value control to property control and initialize to current value (connect bi-direction if prop_ctrl sends) */ swami_control_connect (treeval->control, (SwamiControl *)(cache->prop_ctrl), flags); return; } p = g_slist_next (p); } } while ((node = node->parent)); /* no tree value found to satisfy cache property criteria */ if (cache->prop_ctrl) /* destroy unused property control? */ { swami_control_disconnect_all ((SwamiControl *)(cache->prop_ctrl)); g_object_unref (cache->prop_ctrl); /* -- unref prop ctrl from cache */ cache->prop_ctrl = NULL; } /* set cached value to "unset" state */ cache->value = NULL; cache->value_node = NULL; } swami-2.2.0/src/libswami/SwamiPropTree.h000066400000000000000000000076111361104770400201530ustar00rootroot00000000000000/* * SwamiPropTree.h - Header for property tree system * A tree of objects with inheritable active values. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_PROP_TREE_H__ #define __SWAMI_PROP_TREE_H__ #include #include #include #include #include typedef struct _SwamiPropTree SwamiPropTree; typedef struct _SwamiPropTreeClass SwamiPropTreeClass; typedef struct _SwamiPropTreeNode SwamiPropTreeNode; typedef struct _SwamiPropTreeValue SwamiPropTreeValue; #define SWAMI_TYPE_PROP_TREE (swami_prop_tree_get_type ()) #define SWAMI_PROP_TREE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_PROP_TREE, SwamiPropTree)) #define SWAMI_PROP_TREE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_PROP_TREE, SwamiPropTreeClass)) #define SWAMI_IS_PROP_TREE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_PROP_TREE)) #define SWAMI_IS_PROP_TREE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_PROP_TREE)) /* Swami property tree */ struct _SwamiPropTree { SwamiLock parent_instance; /* derived from SwamiLock */ GNode *tree; /* tree of SwamiPropTreeNode structures */ GHashTable *object_hash; /* object->node hash */ }; struct _SwamiPropTreeClass { SwamiLockClass parent_class; }; /* defines a node of a property tree - an object with a list of node property values and cached object property values */ struct _SwamiPropTreeNode { GObject *object; /* pointer to the object the node manages */ GSList *values; /* list of SwamiPropTreeValue for this node */ GSList *cache; /* cached values for object (struct in SwamiPropTree.c) */ guint16 flags; guint16 reserved; }; /* defines an active value in a property tree */ struct _SwamiPropTreeValue { GType prop_type; /* instance type owning property to match (0 = wildcard) */ char *prop_name; /* name of property to match */ SwamiControl *control; /* source value control (defines the value) */ }; GType swami_prop_tree_get_type (void); SwamiPropTree *swami_prop_tree_new (void); void swami_prop_tree_set_root (SwamiPropTree *proptree, GObject *root); void swami_prop_tree_prepend (SwamiPropTree *proptree, GObject *parent, GObject *obj); #define swami_prop_tree_append(proptree, parent, obj) \ swami_prop_tree_insert_before (proptree, parent, NULL, obj) void swami_prop_tree_insert_before (SwamiPropTree *proptree, GObject *parent, GObject *sibling, GObject *obj); void swami_prop_tree_remove (SwamiPropTree *proptree, GObject *obj); void swami_prop_tree_remove_recursive (SwamiPropTree *proptree, GObject *obj); void swami_prop_tree_replace (SwamiPropTree *proptree, GObject *old, GObject *new); IpatchList *swami_prop_tree_get_children (SwamiPropTree *proptree, GObject *obj); GNode *swami_prop_tree_object_get_node (SwamiPropTree *proptree, GObject *obj); void swami_prop_tree_add_value (SwamiPropTree *proptree, GObject *obj, GType prop_type, const char *prop_name, SwamiControl *control); void swami_prop_tree_remove_value (SwamiPropTree *proptree, GObject *obj, GType prop_type, const char *prop_name); #endif swami-2.2.0/src/libswami/SwamiRoot.c000066400000000000000000000425611361104770400173340ustar00rootroot00000000000000/* * SwamiRoot.c - Root Swami application object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include #include "SwamiRoot.h" #include "SwamiLog.h" #include "SwamiObject.h" #include "i18n.h" #define DEFAULT_SWAP_MAX_WASTE 64 // Maximum swap file waste in megabytes #define DEFAULT_SWAP_RAM_SIZE 32 // Size of RAM sample swap in megabytes #define DEFAULT_SAMPLE_CACHE_MAX_WASTE 64 // Maximum unused sample cache in megabytes #define DEFAULT_SAMPLE_CACHE_MAX_AGE 0 // Maximum age of unused samples in seconds (0 to disable) /* Maximum sample size to import in megabytes * (To prevent "O crap, I didn't mean to load that one!") */ #define DEFAULT_SAMPLE_MAX_SIZE 32 #define SWAP_MAX_WASTE_INTERVAL 10 // Swap max waste check interval in seconds /* Swami root object properties */ enum { PROP_0, PROP_PATCH_SEARCH_PATH, PROP_PATCH_PATH, PROP_SAMPLE_PATH, PROP_SAMPLE_FORMAT, PROP_SWAP_MAX_WASTE, PROP_SWAP_RAM_SIZE, PROP_SAMPLE_CACHE_MAX_WASTE, PROP_SAMPLE_CACHE_MAX_AGE, PROP_SAMPLE_MAX_SIZE, PROP_PATCH_ROOT }; /* Swami root object signals */ enum { SWAMI_PROP_NOTIFY, OBJECT_ADD, /* add object */ LAST_SIGNAL }; /* --- private function prototypes --- */ static gboolean swami_root_sample_waste_checks (gpointer user_data); static void swami_root_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swami_root_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swami_root_finalize (GObject *object); guint root_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (SwamiRoot, swami_root, SWAMI_TYPE_LOCK); static int swami_root_swap_max_waste = DEFAULT_SWAP_MAX_WASTE; static int swami_root_sample_cache_max_waste = DEFAULT_SAMPLE_CACHE_MAX_WASTE; /* max sample cache unused size in megabytes */ static int swami_root_sample_cache_max_age = DEFAULT_SAMPLE_CACHE_MAX_AGE; /* max age of unused samples in seconds */ static void swami_root_class_init (SwamiRootClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); root_signals[SWAMI_PROP_NOTIFY] = g_signal_new ("swami-prop-notify", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_VOID__PARAM, G_TYPE_NONE, 1, G_TYPE_PARAM); root_signals[OBJECT_ADD] = g_signal_new ("object-add", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (SwamiRootClass, object_add), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); obj_class->finalize = swami_root_finalize; obj_class->set_property = swami_root_set_property; obj_class->get_property = swami_root_get_property; g_object_class_install_property (obj_class, PROP_PATCH_SEARCH_PATH, g_param_spec_string ("patch-search-path", N_("Patch search path"), N_("Patch search path"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PATCH_PATH, g_param_spec_string ("patch-path", N_("Patch path"), N_("Default patch path"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_PATH, g_param_spec_string ("sample-path", N_("Sample path"), N_("Default sample path"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_FORMAT, g_param_spec_string ("sample-format", N_("Sample format"), N_("Default sample format"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SWAP_MAX_WASTE, g_param_spec_int ("swap-max-waste", N_("Swap max waste"), N_("Max waste of sample swap in megabytes"), 0, G_MAXINT, DEFAULT_SWAP_MAX_WASTE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SWAP_RAM_SIZE, g_param_spec_int ("swap-ram-size", N_("Swap RAM size"), N_("Size of RAM sample swap in megabytes"), 0, G_MAXINT, DEFAULT_SWAP_RAM_SIZE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_CACHE_MAX_WASTE, g_param_spec_int ("sample-cache-max-waste", N_("Sample cache max waste"), N_("Max unused sample cache in megabytes"), 0, G_MAXINT, DEFAULT_SAMPLE_CACHE_MAX_WASTE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_CACHE_MAX_AGE, g_param_spec_int ("sample-cache-max-age", N_("Sample cache max age"), N_("Max unused age of cached samples in seconds (0 disables)"), 0, G_MAXINT, DEFAULT_SAMPLE_CACHE_MAX_AGE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_MAX_SIZE, g_param_spec_int ("sample-max-size", N_("Sample max size"), N_("Max sample size in megabytes"), 0, G_MAXINT, DEFAULT_SAMPLE_MAX_SIZE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PATCH_ROOT, g_param_spec_object ("patch-root", N_("Patch root"), N_("Root container of instrument patch tree"), SWAMI_TYPE_CONTAINER, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE)); g_timeout_add_seconds (SWAP_MAX_WASTE_INTERVAL, swami_root_sample_waste_checks, NULL); } /* Periodically check if max swap or sample cache waste has been exceeded and compact them if so */ static gboolean swami_root_sample_waste_checks (gpointer user_data) { GError *err = NULL; if (ipatch_get_sample_store_swap_unused_size () > swami_root_swap_max_waste * 1024 * 1024) { if (!ipatch_compact_sample_store_swap (&err)) { g_warning (_("Error compacting swap file: %s"), ipatch_gerror_message (err)); g_clear_error (&err); } } ipatch_sample_cache_clean ((guint64)swami_root_sample_cache_max_waste * (1024 * 1024), swami_root_sample_cache_max_age); return (TRUE); } static void swami_root_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiRoot *root = SWAMI_ROOT (object); switch (property_id) { case PROP_PATCH_SEARCH_PATH: if (root->patch_search_path) g_free (root->patch_search_path); root->patch_search_path = g_value_dup_string (value); break; case PROP_PATCH_PATH: if (root->patch_path) g_free (root->patch_path); root->patch_path = g_value_dup_string (value); break; case PROP_SAMPLE_PATH: if (root->sample_path) g_free (root->sample_path); root->sample_path = g_value_dup_string (value); break; case PROP_SAMPLE_FORMAT: if (root->sample_format) g_free (root->sample_format); root->sample_format = g_value_dup_string (value); break; case PROP_SWAP_MAX_WASTE: swami_root_swap_max_waste = g_value_get_int (value); break; case PROP_SWAP_RAM_SIZE: root->swap_ram_size = g_value_get_int (value); ipatch_set_sample_store_swap_max_memory (root->swap_ram_size * 1024 * 1024); break; case PROP_SAMPLE_CACHE_MAX_WASTE: swami_root_sample_cache_max_waste = g_value_get_int (value); break; case PROP_SAMPLE_CACHE_MAX_AGE: swami_root_sample_cache_max_age = g_value_get_int (value); break; case PROP_SAMPLE_MAX_SIZE: root->sample_max_size = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_root_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiRoot *root = SWAMI_ROOT (object); switch (property_id) { case PROP_PATCH_SEARCH_PATH: g_value_set_string (value, root->patch_search_path); break; case PROP_PATCH_PATH: g_value_set_string (value, root->patch_path); break; case PROP_SAMPLE_PATH: g_value_set_string (value, root->sample_path); break; case PROP_SAMPLE_FORMAT: g_value_set_string (value, root->sample_format); break; case PROP_SWAP_MAX_WASTE: g_value_set_int (value, swami_root_swap_max_waste); break; case PROP_SWAP_RAM_SIZE: g_value_set_int (value, root->swap_ram_size); break; case PROP_SAMPLE_CACHE_MAX_WASTE: g_value_set_int (value, swami_root_sample_cache_max_waste); break; case PROP_SAMPLE_CACHE_MAX_AGE: g_value_set_int (value, swami_root_sample_cache_max_age); break; case PROP_SAMPLE_MAX_SIZE: g_value_set_int (value, root->sample_max_size); break; case PROP_PATCH_ROOT: g_value_set_object (value, root->patch_root); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_root_init (SwamiRoot *root) { root->swap_ram_size = DEFAULT_SWAP_RAM_SIZE; root->sample_max_size = DEFAULT_SAMPLE_MAX_SIZE; root->patch_root = swami_container_new (); root->patch_root->root = root; /* set the IpatchItem hooks active flag to make all children items execute hook callback functions (once added to root) */ ipatch_item_set_flags (IPATCH_ITEM (root->patch_root), IPATCH_ITEM_HOOKS_ACTIVE); root->proptree = swami_prop_tree_new (); /* ++ ref property tree */ swami_prop_tree_set_root (root->proptree, G_OBJECT (root)); ipatch_set_sample_store_swap_max_memory (root->swap_ram_size * 1024 * 1024); } static void swami_root_finalize (GObject *object) { SwamiRoot *root = SWAMI_ROOT (object); g_object_unref (root->patch_root); g_object_unref (root->proptree); g_free (root->patch_search_path); g_free (root->patch_path); g_free (root->sample_path); g_free (root->sample_format); if (G_OBJECT_CLASS (swami_root_parent_class)->finalize) G_OBJECT_CLASS (swami_root_parent_class)->finalize (object); } /** * swami_root_new: * * Create a new Swami root object which is a toplevel container for * patches, objects, configuration data and state history. * * Returns: New Swami root object */ SwamiRoot * swami_root_new (void) { return SWAMI_ROOT (g_object_new (SWAMI_TYPE_ROOT, NULL)); } /** * swami_get_root: * @object: An object registered to a #SwamiRoot, an #IpatchItem contained * in a #SwamiRoot or the root itself * * Gets the #SwamiRoot object associated with a @object. * * Returns: The #SwamiRoot object or %NULL if @object not registered to a * root. Returned root object is not referenced, we assume it won't be * destroyed. */ SwamiRoot * swami_get_root (gpointer object) { SwamiContainer *container; SwamiRoot *root = NULL; SwamiObjectPropBag *propbag; g_return_val_if_fail (G_IS_OBJECT (object), NULL); if (SWAMI_IS_ROOT (object)) root = (SwamiRoot *)object; else if (IPATCH_IS_ITEM (object)) { container = (SwamiContainer *)ipatch_item_peek_ancestor_by_type (object, SWAMI_TYPE_CONTAINER); root = container->root; } else { propbag = g_object_get_qdata (G_OBJECT (object), swami_object_propbag_quark); if (propbag) root = propbag->root; } return (root); } /** * swami_root_get_objects: * @root: Swami root object * * Get an iterator filled with toplevel objects that are the first children * of a #SwamiRoot object property tree. * * Returns: New object list with a reference count of 1 which the caller owns, * remember to unref it when finished. */ IpatchList * swami_root_get_objects (SwamiRoot *root) { GNode *node; IpatchList *list; g_return_val_if_fail (SWAMI_IS_ROOT (root), NULL); list = ipatch_list_new (); /* ++ ref new list */ SWAMI_LOCK_READ (root->proptree); node = root->proptree->tree->children; while (node) { g_object_ref (node->data); list->items = g_list_prepend (list->items, node->data); node = node->next; } SWAMI_UNLOCK_READ (root->proptree); return (list); } /** * swami_root_add_object: * @root: Swami root object * @object: Object to add * * Add an object to a Swami root property tree. A reference is held on * the object for the @root object. */ void swami_root_add_object (SwamiRoot *root, GObject *object) { g_return_if_fail (SWAMI_IS_ROOT (root)); g_return_if_fail (G_IS_OBJECT (object)); swami_object_set (object, "root", root, NULL); swami_prop_tree_prepend (root->proptree, G_OBJECT (root), object); g_signal_emit (root, root_signals[OBJECT_ADD], 0, object); } /** * swami_root_new_object: * @root: Swami root object * @type_name: Name of GObject derived GType of object to create and add to a * @root object. * * Like swami_root_add_object() but creates a new object rather than using * an existing one. * * Returns: The new GObject created or NULL on error. The caller owns a * reference on the new object and should unref it when done. The @root * also owns a reference, until swami_root_remove_object() is called on it. */ GObject * swami_root_new_object (SwamiRoot *root, const char *type_name) { GObject *obj; GType type; g_return_val_if_fail (SWAMI_IS_ROOT (root), NULL); g_return_val_if_fail (g_type_from_name (type_name) != 0, NULL); type = g_type_from_name (type_name); g_return_val_if_fail (g_type_is_a (type, G_TYPE_OBJECT), NULL); if (!(obj = g_object_new (type, NULL))) return (NULL); /* ++ ref new item */ swami_root_add_object (root, obj); return (obj); /* !! caller takes over creator's ref */ } /** * swami_root_prepend_object: * @root: Swami root object * @parent: New parent of @object * @object: Object to prepend * * Prepends an object to an object property tree in a @root object as * a child of @parent. Like swami_root_add_object() but allows parent * to specified (rather than using the @root as the parent). */ void swami_root_prepend_object (SwamiRoot *root, GObject *parent, GObject *object) { g_return_if_fail (SWAMI_IS_ROOT (root)); g_return_if_fail (G_IS_OBJECT (parent)); g_return_if_fail (G_IS_OBJECT (object)); swami_object_set (object, "root", root, NULL); swami_prop_tree_prepend (root->proptree, parent, object); g_signal_emit (root, root_signals[OBJECT_ADD], 0, object); } /** * swami_root_insert_object_before: * @root: Swami root object * @parent: New parent of @object * @sibling: Sibling to insert object before or %NULL to append * @object: Object to insert * * Inserts an object into an object property tree in a @root object as * a child of @parent and before @sibling. */ void swami_root_insert_object_before (SwamiRoot *root, GObject *parent, GObject *sibling, GObject *object) { g_return_if_fail (SWAMI_IS_ROOT (root)); g_return_if_fail (G_IS_OBJECT (parent)); g_return_if_fail (!sibling || G_IS_OBJECT (sibling)); g_return_if_fail (G_IS_OBJECT (object)); swami_object_set (object, "root", root, NULL); swami_prop_tree_insert_before (root->proptree, parent, sibling, object); g_signal_emit (root, root_signals[OBJECT_ADD], 0, object); } /** * swami_root_patch_load: * @root: Swami root object to load into * @filename: Name and path of file to load * @item: Location to store pointer to object that has been loaded into Swami * root object (or %NULL). Remember to unref the object when done with it * (not necessary of course if %NULL was passed). * @err: Location to store error info or %NULL * * Load an instrument patch file and append to Swami object tree. The caller * owns a reference to the returned patch object and should unref it when * done with the object. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_root_patch_load (SwamiRoot *root, const char *filename, IpatchItem **item, GError **err) { IpatchFileHandle *handle; GObject *obj; g_return_val_if_fail (SWAMI_IS_ROOT (root), FALSE); g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (!err || !*err, FALSE); handle = ipatch_file_identify_open (filename, err); /* ++ open file handle */ if (!handle) return (FALSE); if (!(obj = ipatch_convert_object_to_type (G_OBJECT (handle->file), IPATCH_TYPE_BASE, err))) { ipatch_file_close (handle); /* -- close file handle */ return (FALSE); } ipatch_file_close (handle); /* -- close file handle */ ipatch_container_append (IPATCH_CONTAINER (root->patch_root), IPATCH_ITEM (obj)); /* !! if @item field was passed, then caller takes over ref */ if (item) *item = IPATCH_ITEM (obj); else g_object_unref (obj); return (TRUE); } /** * swami_root_patch_save: * @item: Patch item to save. * @filename: New file name to save to or NULL to use current one. * @err: Location to store error info or NULL * * Save a patch item to a file. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_root_patch_save (IpatchItem *item, const char *filename, GError **err) { return (ipatch_base_save_to_filename (IPATCH_BASE (item), filename, err)); } swami-2.2.0/src/libswami/SwamiRoot.h000066400000000000000000000062721361104770400173400ustar00rootroot00000000000000/* * SwamiRoot.h - Root Swami application object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_ROOT_H__ #define __SWAMI_ROOT_H__ #include #include #include typedef struct _SwamiRoot SwamiRoot; typedef struct _SwamiRootClass SwamiRootClass; #include #include #include #define SWAMI_TYPE_ROOT (swami_root_get_type ()) #define SWAMI_ROOT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_ROOT, SwamiRoot)) #define SWAMI_ROOT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_ROOT, SwamiRootClass)) #define SWAMI_IS_ROOT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_ROOT)) #define SWAMI_IS_ROOT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_ROOT)) struct _SwamiRoot { SwamiLock parent_instance; SwamiContainer *patch_root; /* instrument patch tree */ /*< private >*/ SwamiPropTree *proptree; /* object property tree */ char *patch_search_path; char *patch_path; /* default path to patch files */ char *sample_path; /* default path to sample files */ char *sample_format; /* default sample format string */ int sample_max_size; /* max sample size in MB (until big samples handled) */ int swap_ram_size; /* maximum size of RAM swap in MB */ }; struct _SwamiRootClass { SwamiLockClass parent_class; /* object add signal */ void (*object_add)(GObject *object); }; GType swami_root_get_type (void); SwamiRoot *swami_root_new (void); #define swami_root_get_patch_items(swami) \ ipatch_container_get_children (IPATCH_CONTAINER (swami->patch_root), \ IPATCH_TYPE_ITEM) SwamiRoot *swami_get_root (gpointer object); IpatchList *swami_root_get_objects (SwamiRoot *root); void swami_root_add_object (SwamiRoot *root, GObject *object); GObject *swami_root_new_object (SwamiRoot *root, const char *type_name); void swami_root_prepend_object (SwamiRoot *root, GObject *parent, GObject *object); #define swami_root_append_object(root, parent, object) \ swami_root_insert_object_before (root, parent, NULL, object) void swami_root_insert_object_before (SwamiRoot *root, GObject *parent, GObject *sibling, GObject *object); gboolean swami_root_patch_load (SwamiRoot *root, const char *filename, IpatchItem **item, GError **err); gboolean swami_root_patch_save (IpatchItem *item, const char *filename, GError **err); #endif swami-2.2.0/src/libswami/SwamiWavetbl.c000066400000000000000000000305661361104770400200170ustar00rootroot00000000000000/* * SwamiWavetbl.c - Swami Wavetable object (base class for drivers) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include "SwamiWavetbl.h" #include "SwamiLog.h" #include "i18n.h" /* --- signals and properties --- */ enum { ACTIVE, LAST_SIGNAL }; enum { PROP_0, PROP_VBANK, PROP_ACTIVE, PROP_ACTIVE_BANK, PROP_ACTIVE_PROGRAM }; /* --- private function prototypes --- */ static void swami_wavetbl_class_init (SwamiWavetblClass *klass); static void swami_wavetbl_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swami_wavetbl_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swami_wavetbl_init (SwamiWavetbl *wavetbl); static void swami_wavetbl_finalize (GObject *object); /* --- private data --- */ SwamiWavetblClass *wavetbl_class = NULL; static guint wavetbl_signals[LAST_SIGNAL] = { 0 }; static GObjectClass *parent_class = NULL; /* --- functions --- */ GType swami_wavetbl_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (SwamiWavetblClass), NULL, NULL, (GClassInitFunc) swami_wavetbl_class_init, NULL, NULL, sizeof (SwamiWavetbl), 0, (GInstanceInitFunc) swami_wavetbl_init, }; item_type = g_type_register_static (SWAMI_TYPE_LOCK, "SwamiWavetbl", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void swami_wavetbl_class_init (SwamiWavetblClass *klass) { GObjectClass *objclass = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); objclass->set_property = swami_wavetbl_set_property; objclass->get_property = swami_wavetbl_get_property; objclass->finalize = swami_wavetbl_finalize; wavetbl_signals[ACTIVE] = g_signal_new ("active", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); g_object_class_install_property (objclass, PROP_VBANK, g_param_spec_object ("vbank", _("Virtual bank"), _("Virtual bank"), IPATCH_TYPE_VBANK, G_PARAM_READABLE)); g_object_class_install_property (objclass, PROP_ACTIVE, g_param_spec_boolean ("active", _("Active"), _("State of driver"), FALSE, G_PARAM_READABLE)); g_object_class_install_property (objclass, PROP_ACTIVE_BANK, g_param_spec_int ("active-bank", _("Active bank"), _("Active (focused) MIDI bank number"), 0, 128, 127, G_PARAM_READWRITE)); g_object_class_install_property (objclass, PROP_ACTIVE_PROGRAM, g_param_spec_int ("active-program", _("Active program"), _("Active (focused) MIDI program number"), 0, 127, 127, G_PARAM_READWRITE)); } static void swami_wavetbl_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiWavetbl *wavetbl = SWAMI_WAVETBL (object); switch (property_id) { case PROP_ACTIVE_BANK: wavetbl->active_bank = g_value_get_int (value); break; case PROP_ACTIVE_PROGRAM: wavetbl->active_program = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_wavetbl_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiWavetbl *wavetbl; g_return_if_fail (SWAMI_IS_WAVETBL (object)); wavetbl = SWAMI_WAVETBL (object); switch (property_id) { case PROP_VBANK: g_value_set_object (value, G_OBJECT (wavetbl->vbank)); break; case PROP_ACTIVE: g_value_set_boolean (value, wavetbl->active); break; case PROP_ACTIVE_BANK: g_value_set_int (value, wavetbl->active_bank); break; case PROP_ACTIVE_PROGRAM: g_value_set_int (value, wavetbl->active_program); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swami_wavetbl_init (SwamiWavetbl *wavetbl) { wavetbl->vbank = ipatch_vbank_new (); wavetbl->active = FALSE; wavetbl->active_bank = 127; wavetbl->active_program = 127; } static void swami_wavetbl_finalize (GObject *object) { SwamiWavetbl *wavetbl = SWAMI_WAVETBL (object); swami_wavetbl_close (wavetbl); g_object_unref (wavetbl->vbank); if (parent_class->finalize) parent_class->finalize (object); } /** * swami_wavetbl_get_virtual_bank: * @wavetbl: Swami Wavetable object * * Retrieve the #IpatchVBank object from a Wavetable instance. This * bank is the main synthesis object for the Wavetable instance, which is used * for mapping instruments to MIDI bank:program locales. * * Returns: The virtual bank of the Wavetable instance with a reference count * added for the caller. */ IpatchVBank * swami_wavetbl_get_virtual_bank (SwamiWavetbl *wavetbl) { return (g_object_ref (wavetbl->vbank)); } /** * swami_wavetbl_set_active_item_locale: * @wavetbl: Swami wave table object * @bank: MIDI bank number of active item * @program: MIDI program number of active item * * Sets the MIDI bank and program numbers (MIDI locale) of the * active item. The active item is the currently focused item in the user * interface, which doesn't necessarily have its own locale bank and program. * * MT-NOTE: This function ensures bank and program number are set atomically, * which is not assured if using the standard * g_object_set() routine. */ void swami_wavetbl_set_active_item_locale (SwamiWavetbl *wavetbl, int bank, int program) { g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); g_return_if_fail (bank >= 0 && bank <= 128); g_return_if_fail (program >= 0 && program <= 127); SWAMI_LOCK_WRITE (wavetbl); wavetbl->active_bank = bank; wavetbl->active_program = program; SWAMI_UNLOCK_WRITE (wavetbl); } /** * swami_wavetbl_get_active_item_locale: * @wavetbl: Swami wave table object * @bank: Location to store MIDI bank number of active item or %NULL * @program: Location to store MIDI program number of active item or %NULL * * Gets the MIDI bank and program numbers (MIDI locale) of the active * item. See swami_wavetbl_set_active_item_locale() for more info. */ void swami_wavetbl_get_active_item_locale (SwamiWavetbl *wavetbl, int *bank, int *program) { g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); SWAMI_LOCK_READ (wavetbl); if (bank) *bank = wavetbl->active_bank; if (program) *program = wavetbl->active_program; SWAMI_UNLOCK_READ (wavetbl); } /** * swami_wavetbl_open: * @wavetbl: Swami Wavetable object * @err: Location to store error information or %NULL. * * Open Wavetbl driver. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_wavetbl_open (SwamiWavetbl *wavetbl, GError **err) { SwamiWavetblClass *wavetbl_class; gboolean retval = TRUE; g_return_val_if_fail (SWAMI_IS_WAVETBL (wavetbl), FALSE); wavetbl_class = SWAMI_WAVETBL_GET_CLASS (wavetbl); g_return_val_if_fail (wavetbl_class->open != NULL, FALSE); if (wavetbl->active) return (TRUE); retval = wavetbl_class->open (wavetbl, err); if (retval) g_signal_emit (G_OBJECT (wavetbl), wavetbl_signals[ACTIVE], 0, TRUE); return (retval); } /** * swami_wavetbl_close: * @wavetbl: Swami Wavetable object * * Close driver, has no effect if already closed. Emits the "active" signal. */ void swami_wavetbl_close (SwamiWavetbl *wavetbl) { SwamiWavetblClass *oclass; g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); oclass = SWAMI_WAVETBL_GET_CLASS (wavetbl); g_return_if_fail (oclass->close != NULL); if (!wavetbl->active) return; (*oclass->close)(wavetbl); g_signal_emit (G_OBJECT (wavetbl), wavetbl_signals[ACTIVE], 0, FALSE); } /** * swami_wavetbl_get_control: * @wavetbl: Swami Wavetable object * @index: Control index * * Get a MIDI control from a wavetable object. Calls the wavetable's * "get_control" method. A control @index is used to support multiple * controls (for example if the wavetable device supports more than 16 * MIDI channels). * * Returns: New MIDI control object linked to @wavetbl or %NULL if no * control with the given @index. The returned control's reference count * has been incremented and is owned by the caller, remember to unref it * when finished. */ SwamiControlMidi * swami_wavetbl_get_control (SwamiWavetbl *wavetbl, int index) { SwamiWavetblClass *wavetbl_class; SwamiControlMidi *control = NULL; g_return_val_if_fail (SWAMI_IS_WAVETBL (wavetbl), NULL); wavetbl_class = SWAMI_WAVETBL_GET_CLASS (wavetbl); if (wavetbl_class->get_control) { control = (*wavetbl_class->get_control)(wavetbl, index); if (control) g_object_ref (control); /* ++ ref control for caller */ } return (control); } /** * swami_wavetbl_load_patch: * @wavetbl: Swami Wavetable object * @patch: Patch object to load * @err: Location to store error information or %NULL. * * Load a patch into a wavetable object. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_wavetbl_load_patch (SwamiWavetbl *wavetbl, IpatchItem *patch, GError **err) { SwamiWavetblClass *wavetbl_class; g_return_val_if_fail (SWAMI_IS_WAVETBL (wavetbl), FALSE); g_return_val_if_fail (IPATCH_IS_ITEM (patch), FALSE); wavetbl_class = SWAMI_WAVETBL_GET_CLASS (wavetbl); g_return_val_if_fail (wavetbl_class->load_patch != NULL, FALSE); return ((*wavetbl_class->load_patch)(wavetbl, patch, err)); } /** * swami_wavetbl_load_active_item: * @wavetbl: Swami Wavetable object * @item: Patch item to load as active item. * @err: Location to store error information or %NULL. * * Load an item as the active program item. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean swami_wavetbl_load_active_item (SwamiWavetbl *wavetbl, IpatchItem *item, GError **err) { SwamiWavetblClass *wavetbl_class; gboolean retval; g_return_val_if_fail (SWAMI_IS_WAVETBL (wavetbl), FALSE); g_return_val_if_fail (IPATCH_IS_ITEM (item), FALSE); wavetbl_class = SWAMI_WAVETBL_GET_CLASS (wavetbl); g_return_val_if_fail (wavetbl_class->load_active_item != NULL, FALSE); retval = (*wavetbl_class->load_active_item)(wavetbl, item, err); return (retval); } /** * swami_wavetbl_check_update_item: * @wavetbl: Swami Wavetable object * @item: Patch item to check if an update is needed * @prop: #GParamSpec of property that has changed * * Checks if a given @item needs to be updated if the property @prop has * changed. * * Returns: %TRUE if @item should be updated, %FALSE otherwise (@prop is not * a synthesis property or @item is not currently loaded in @wavetbl). */ gboolean swami_wavetbl_check_update_item (SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop) { SwamiWavetblClass *oclass; gboolean retval; g_return_val_if_fail (SWAMI_IS_WAVETBL (wavetbl), FALSE); g_return_val_if_fail (IPATCH_IS_ITEM (item), FALSE); g_return_val_if_fail (G_IS_PARAM_SPEC (prop), FALSE); oclass = SWAMI_WAVETBL_GET_CLASS (wavetbl); if (!oclass->update_item) return (FALSE); retval = (*oclass->check_update_item)(wavetbl, item, prop); return (retval); } /** * swami_wavetbl_update_item: * @wavetbl: Swami Wavetable object * @item: Patch item to force update on * * Refresh a given @item object's synthesis cache. This should be called after * a change affecting synthesis output occurs to @item, which can be tested * with swami_wavetbl_check_update_item(). */ void swami_wavetbl_update_item (SwamiWavetbl *wavetbl, IpatchItem *item) { SwamiWavetblClass *oclass; g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); g_return_if_fail (IPATCH_IS_ITEM (item)); oclass = SWAMI_WAVETBL_GET_CLASS (wavetbl); if (!oclass->update_item) return; (*oclass->update_item)(wavetbl, item); } swami-2.2.0/src/libswami/SwamiWavetbl.h000066400000000000000000000072521361104770400200200ustar00rootroot00000000000000/* * SwamiWavetbl.h - Swami wave table object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_WAVETBL_H__ #define __SWAMI_WAVETBL_H__ #include #include #include #include #include #include typedef struct _SwamiWavetbl SwamiWavetbl; typedef struct _SwamiWavetblClass SwamiWavetblClass; #define SWAMI_TYPE_WAVETBL (swami_wavetbl_get_type ()) #define SWAMI_WAVETBL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_WAVETBL, SwamiWavetbl)) #define SWAMI_WAVETBL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_WAVETBL, SwamiWavetblClass)) #define SWAMI_IS_WAVETBL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_WAVETBL)) #define SWAMI_IS_WAVETBL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMI_TYPE_WAVETBL)) #define SWAMI_WAVETBL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), SWAMI_TYPE_WAVETBL, SwamiWavetblClass)) /* Swami Wavetbl object */ struct _SwamiWavetbl { SwamiLock parent_instance; IpatchVBank *vbank; /* Virtual bank of available instruments */ /*< private >*/ gboolean active; /* driver is active? */ guint16 active_bank; /* active (focused) audible MIDI bank number */ guint16 active_program; /* active (focused) audible MIDI program number */ }; struct _SwamiWavetblClass { SwamiLockClass parent_class; /*< public >*/ gboolean (*open)(SwamiWavetbl *wavetbl, GError **err); void (*close)(SwamiWavetbl *wavetbl); SwamiControlMidi * (*get_control) (SwamiWavetbl *wavetbl, int index); gboolean (*load_patch)(SwamiWavetbl *wavetbl, IpatchItem *patch, GError **err); gboolean (*load_active_item)(SwamiWavetbl *wavetbl, IpatchItem *item, GError **err); gboolean (*check_update_item)(SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop); void (*update_item)(SwamiWavetbl *wavetbl, IpatchItem *item); void (*realtime_effect)(SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop, GValue *value); }; GType swami_wavetbl_get_type (void); IpatchVBank *swami_wavetbl_get_virtual_bank (SwamiWavetbl *wavetbl); void swami_wavetbl_set_active_item_locale (SwamiWavetbl *wavetbl, int bank, int program); void swami_wavetbl_get_active_item_locale (SwamiWavetbl *wavetbl, int *bank, int *program); gboolean swami_wavetbl_open (SwamiWavetbl *wavetbl, GError **err); void swami_wavetbl_close (SwamiWavetbl *wavetbl); SwamiControlMidi *swami_wavetbl_get_control (SwamiWavetbl *wavetbl, int index); gboolean swami_wavetbl_load_patch (SwamiWavetbl *wavetbl, IpatchItem *patch, GError **err); gboolean swami_wavetbl_load_active_item (SwamiWavetbl *wavetbl, IpatchItem *item, GError **err); gboolean swami_wavetbl_check_update_item (SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop); void swami_wavetbl_update_item (SwamiWavetbl *wavetbl, IpatchItem *item); #endif swami-2.2.0/src/libswami/builtin_enums.c000066400000000000000000000147401361104770400202630ustar00rootroot00000000000000/* builtin_enums.c This file contains functions registration of type enumeration (or flags) defined in the respectives headers files. Typically this file could be generated automatically at make time (with the help of glib-mkenums). Compiling on Windows: glib-mkenums is a perl script and Perl isn't natively installed. To avoid Perl dependency, the file should manually updated. This shouldn't be a problem when new enumerations are slowly added over time. Please respect the naming conventions. This example assumes an enum definition in a header file: typedef enum { SWAMI_CONTROL_SENDS = 1 << 4, .... .... } SwamiControlFlags; The name of registration function should be: xxx_get_type, with xxx the enum name (e.g word-separated by underscores. (e.g patch_base_flags_get_type When the enum value definitions contain bit-shift operators, this function must call g_flags_register_static()otherwise g_enum_register_static() must be called. First parameter of g_flags_register_static or g_enum_register_static(type name) must be the enum name (e.g "SwamiControlFlags"). Second parameter of of g_flags_register_static() must be GFlagsValue table value. Second parameter of of g_enum_register_static() must be GFEnumValue table value. Each value (GFlagsValue or GFEnumValue) must be {ENUM_VALUE, "VALUE_NAME", "valuenick"}: - ENUM_VALUE, the integer value for the enum value.(e.g SWAMI_CONTROL_SENDS). - VALUE_NAME, name with words uppercase and word-separated by underscores (e.g "SWAMI_CONTROL_SENDS"). - valuenick, this is usually stripping common prefix words of all the enum values. the words are lowercase and underscores are substituted by a minus (e.g. "sends"). */ #include "libswami.h" #include "swami_priv.h" /* enumerations from "SwamiControl.h" */ GType swami_control_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMI_CONTROL_SENDS, "SWAMI_CONTROL_SENDS", "sends" }, { SWAMI_CONTROL_RECVS, "SWAMI_CONTROL_RECVS", "recvs" }, { SWAMI_CONTROL_NO_CONV, "SWAMI_CONTROL_NO_CONV", "no-conv" }, { SWAMI_CONTROL_NATIVE, "SWAMI_CONTROL_NATIVE", "native" }, { SWAMI_CONTROL_VALUE, "SWAMI_CONTROL_VALUE", "value" }, { SWAMI_CONTROL_SPEC_NO_CONV, "SWAMI_CONTROL_SPEC_NO_CONV", "spec-no-conv" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiControlFlags", values); } return etype; } GType swami_control_conn_priority_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMI_CONTROL_CONN_PRIORITY_DEFAULT, "SWAMI_CONTROL_CONN_PRIORITY_DEFAULT", "default" }, { SWAMI_CONTROL_CONN_PRIORITY_LOW, "SWAMI_CONTROL_CONN_PRIORITY_LOW", "low" }, { SWAMI_CONTROL_CONN_PRIORITY_MEDIUM, "SWAMI_CONTROL_CONN_PRIORITY_MEDIUM", "medium" }, { SWAMI_CONTROL_CONN_PRIORITY_HIGH, "SWAMI_CONTROL_CONN_PRIORITY_HIGH", "high" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiControlConnPriority", values); } return etype; } GType swami_control_conn_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMI_CONTROL_CONN_INPUT, "SWAMI_CONTROL_CONN_INPUT", "input" }, { SWAMI_CONTROL_CONN_OUTPUT, "SWAMI_CONTROL_CONN_OUTPUT", "output" }, { SWAMI_CONTROL_CONN_INIT, "SWAMI_CONTROL_CONN_INIT", "init" }, { SWAMI_CONTROL_CONN_BIDIR, "SWAMI_CONTROL_CONN_BIDIR", "bidir" }, { SWAMI_CONTROL_CONN_SPEC, "SWAMI_CONTROL_CONN_SPEC", "spec" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiControlConnFlags", values); } return etype; } /* enumerations from "SwamiLog.h" */ GType swami_error_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMI_ERROR_FAIL, "SWAMI_ERROR_FAIL", "fail" }, { SWAMI_ERROR_INVALID, "SWAMI_ERROR_INVALID", "invalid" }, { SWAMI_ERROR_CANCELED, "SWAMI_ERROR_CANCELED", "canceled" }, { SWAMI_ERROR_UNSUPPORTED, "SWAMI_ERROR_UNSUPPORTED", "unsupported" }, { SWAMI_ERROR_IO, "SWAMI_ERROR_IO", "io" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiError", values); } return etype; } /* enumerations from "SwamiMidiEvent.h" */ GType swami_midi_event_type_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMI_MIDI_NONE, "SWAMI_MIDI_NONE", "none" }, { SWAMI_MIDI_NOTE, "SWAMI_MIDI_NOTE", "note" }, { SWAMI_MIDI_NOTE_ON, "SWAMI_MIDI_NOTE_ON", "note-on" }, { SWAMI_MIDI_NOTE_OFF, "SWAMI_MIDI_NOTE_OFF", "note-off" }, { SWAMI_MIDI_KEY_PRESSURE, "SWAMI_MIDI_KEY_PRESSURE", "key-pressure" }, { SWAMI_MIDI_PITCH_BEND, "SWAMI_MIDI_PITCH_BEND", "pitch-bend" }, { SWAMI_MIDI_PROGRAM_CHANGE, "SWAMI_MIDI_PROGRAM_CHANGE", "program-change" }, { SWAMI_MIDI_CONTROL, "SWAMI_MIDI_CONTROL", "control" }, { SWAMI_MIDI_CONTROL14, "SWAMI_MIDI_CONTROL14", "control14" }, { SWAMI_MIDI_CHAN_PRESSURE, "SWAMI_MIDI_CHAN_PRESSURE", "chan-pressure" }, { SWAMI_MIDI_RPN, "SWAMI_MIDI_RPN", "rpn" }, { SWAMI_MIDI_NRPN, "SWAMI_MIDI_NRPN", "nrpn" }, { SWAMI_MIDI_BEND_RANGE, "SWAMI_MIDI_BEND_RANGE", "bend-range" }, { SWAMI_MIDI_BANK_SELECT, "SWAMI_MIDI_BANK_SELECT", "bank-select" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiMidiEventType", values); } return etype; } /* enumerations from "SwamiObject.h" */ GType swami_rank_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMI_RANK_INVALID, "SWAMI_RANK_INVALID", "invalid" }, { SWAMI_RANK_LOWEST, "SWAMI_RANK_LOWEST", "lowest" }, { SWAMI_RANK_LOW, "SWAMI_RANK_LOW", "low" }, { SWAMI_RANK_NORMAL, "SWAMI_RANK_NORMAL", "normal" }, { SWAMI_RANK_DEFAULT, "SWAMI_RANK_DEFAULT", "default" }, { SWAMI_RANK_HIGH, "SWAMI_RANK_HIGH", "high" }, { SWAMI_RANK_HIGHEST, "SWAMI_RANK_HIGHEST", "highest" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiRank", values); } return etype; } GType swami_object_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMI_OBJECT_SAVE, "SWAMI_OBJECT_SAVE", "save" }, { SWAMI_OBJECT_USER, "SWAMI_OBJECT_USER", "user" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiObjectFlags", values); } return etype; } /* Generated data ends here */ swami-2.2.0/src/libswami/builtin_enums.h000066400000000000000000000031461361104770400202660ustar00rootroot00000000000000/* builin_enums.h Prototype of registration functions of type enumeration (or flags) defined in the respectives headers files. Typically this file could be generated automatically at make time (with the help of glib-mkenums). Compiling on Windows: glib-mkenums is a perl script and Perl isn't natively installed. To avoid Perl dependency, the file should manually updated. This shouldn't be a problem when new enumerations are slowly added over time. Please read the naming conventions described in builin_enums.c. */ #ifndef __SWAMI_BUILTIN_ENUMS_H__ #define __SWAMI_BUILTIN_ENUMS_H__ #include G_BEGIN_DECLS /* enumerations from "SwamiControl.h" */ GType swami_control_flags_get_type (void); #define SWAMI_TYPE_CONTROL_FLAGS (swami_control_flags_get_type()) GType swami_control_conn_priority_get_type (void); #define SWAMI_TYPE_CONTROL_CONN_PRIORITY (swami_control_conn_priority_get_type()) GType swami_control_conn_flags_get_type (void); #define SWAMI_TYPE_CONTROL_CONN_FLAGS (swami_control_conn_flags_get_type()) /* enumerations from "SwamiLog.h" */ GType swami_error_get_type (void); #define SWAMI_TYPE_ERROR (swami_error_get_type()) /* enumerations from "SwamiMidiEvent.h" */ GType swami_midi_event_type_get_type (void); #define SWAMI_TYPE_MIDI_EVENT_TYPE (swami_midi_event_type_get_type()) /* enumerations from "SwamiObject.h" */ GType swami_rank_get_type (void); #define SWAMI_TYPE_RANK (swami_rank_get_type()) GType swami_object_flags_get_type (void); #define SWAMI_TYPE_OBJECT_FLAGS (swami_object_flags_get_type()) G_END_DECLS #endif /* __SWAMI_BUILTIN_ENUMS_H__ */ /* Generated data ends here */ swami-2.2.0/src/libswami/i18n.h000066400000000000000000000006101361104770400161610ustar00rootroot00000000000000#ifndef __LIBSWAMI_I18N_H__ #define __LIBSWAMI_I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) dgettext("libswami", x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif swami-2.2.0/src/libswami/libswami.c000066400000000000000000000200431361104770400172060ustar00rootroot00000000000000/* * libswami.c - libswami library functions and sub systems * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include #include "libswami.h" #include "builtin_enums.h" #include "i18n.h" /* inactive control event expiration interval in milliseconds */ #define SWAMI_CONTROL_EVENT_EXPIRE_INTERVAL 10000 /* --- private function prototypes --- */ static gboolean swami_control_event_expire_timeout (gpointer data); static void container_add_notify (IpatchContainer *container, IpatchItem *item, gpointer user_data); static void container_remove_notify (IpatchContainer *container, IpatchItem *item, gpointer user_data); /* local libswami prototypes (in separate source files) */ void _swami_object_init (void); /* SwamiObject.c */ void _swami_plugin_initialize (void); /* SwamiPlugin.c */ void _swami_value_transform_init (void); /* value_transform.c */ /* Ipatch property and container add/remove event controls */ /* public variables */ /* useful when libswami is used as a static library */ SwamiControl *swami_patch_prop_title_control; SwamiControl *swami_patch_add_control; SwamiControl *swami_patch_remove_control; /* Getter function returning swami_patch_prop_title_control. Useful when libswami library is used as a shared library linked at load time. */ SwamiControl * swami_patch_get_prop_title_control(void) { return swami_patch_prop_title_control; } /* Getter function returning swami_patch_add_control. Useful when libswami library is used as a shared library linked at load time. */ SwamiControl * swami_patch_get_add_control(void) { return swami_patch_add_control; } /* Getter function returning swami_patch_remove_control. Useful when libswami library is used as a shared library linked at load time. */ SwamiControl * swami_patch_get_remove_control(void) { return swami_patch_remove_control; } /** * swami_init: * * Initialize libSwami (should be called before any other libSwami functions) */ void swami_init (void) { static gboolean initialized = FALSE; char *swap_dir, *swap_filename; if (initialized) return; initialized = TRUE; ipatch_init (); /* initialize libInstPatch */ /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain ("libswami", LOCALEDIR); #endif g_type_class_ref (SWAMI_TYPE_ROOT); /* initialize child properties and type rank systems */ _swami_object_init (); /* Register additional value transform functions */ _swami_value_transform_init (); /* initialize libswami types */ g_type_class_ref (SWAMI_TYPE_CONTROL); g_type_class_ref (SWAMI_TYPE_CONTROL_FUNC); g_type_class_ref (SWAMI_TYPE_CONTROL_HUB); g_type_class_ref (SWAMI_TYPE_CONTROL_MIDI); g_type_class_ref (SWAMI_TYPE_CONTROL_PROP); g_type_class_ref (SWAMI_TYPE_CONTROL_QUEUE); g_type_class_ref (SWAMI_TYPE_CONTROL_VALUE); g_type_class_ref (SWAMI_TYPE_LOCK); g_type_class_ref (SWAMI_TYPE_MIDI_DEVICE); swami_midi_event_get_type (); g_type_class_ref (SWAMI_TYPE_PLUGIN); g_type_class_ref (SWAMI_TYPE_PROP_TREE); g_type_class_ref (SWAMI_TYPE_WAVETBL); _swami_plugin_initialize (); /* initialize plugin system */ /* create IpatchItem title property control */ swami_patch_prop_title_control /* ++ ref forever */ = SWAMI_CONTROL (swami_control_prop_new (NULL, ipatch_item_get_pspec_title())); /* create ipatch container event controls */ swami_patch_add_control = swami_control_new (); /* ++ ref forever */ swami_patch_remove_control = swami_control_new (); /* ++ ref forever */ /* connect libInstPatch item notifies */ ipatch_container_add_connect (NULL, container_add_notify, NULL, NULL); ipatch_container_remove_connect (NULL, NULL, container_remove_notify, NULL, NULL); /* install periodic control event expiration process */ g_timeout_add (SWAMI_CONTROL_EVENT_EXPIRE_INTERVAL, swami_control_event_expire_timeout, NULL); /* Construct Swami directory name for swap file (uses XDG cache directory - seems the most appropriate) */ swap_dir = g_build_filename (g_get_user_cache_dir(), "swami", NULL); /* ++ alloc */ if (!g_file_test (swap_dir, G_FILE_TEST_EXISTS)) { if (g_mkdir_with_parents (swap_dir, 0700) == -1) { g_critical (_("Failed to create sample swap file directory '%s': %s"), swap_dir, g_strerror (errno)); g_free (swap_dir); /* -- free */ return; } } swap_filename = g_build_filename (swap_dir, "sample_swap.dat", NULL); /* ++ alloc */ g_free (swap_dir); /* -- free swap dir */ /* assign libInstPatch sample store swap file name (uses XDG cache directory) */ ipatch_set_sample_store_swap_file_name (swap_filename); g_free (swap_filename); // -- free swap file name } static gboolean swami_control_event_expire_timeout (gpointer data) { swami_control_do_event_expiration (); return (TRUE); } #if 0 /** * */ SwamiControlEvent * swami_item_prop_assign_change_event (IpatchItemPropNotify *info) { SwamiControlEvent *event, *origin; SwamiEventPropChange *prop_change; /* if event has already been created, return it */ if (info->event_ptrs[0]) return (info->event_ptrs[0]); event = swami_control_event_new (TRUE); /* ++ ref new control event */ g_value_init (&event->value, SWAMI_TYPE_EVENT_PROP_CHANGE); prop_change = swami_event_prop_change_new (); prop_change->object = g_object_ref (info->item); /* ++ ref object */ prop_change->pspec = g_param_spec_ref (info->pspec); /* ++ ref parameter spec */ g_value_init (&prop_change->value, G_VALUE_TYPE (info->new_value)); g_value_copy (info->new_value, &prop_change->value); g_value_set_boxed_take_ownership (&event->value, prop_change); /* IpatchItem property loop prevention, get current IpatchItem property origin event for this thread (if any) */ if ((origin = g_static_private_get (&prop_notify_origin))) swami_control_event_set_origin (event, origin); /* assign the pointer and destroy function to the event notify structure */ IPATCH_ITEM_PROP_NOTIFY_SET_EVENT (info, 0, event, (GDestroyNotify)swami_control_event_unref); return (event); } #endif /* IpatchContainer "add" notify callback */ static void container_add_notify (IpatchContainer *container, IpatchItem *item, gpointer user_data) { SwamiControlEvent *event; event = swami_control_event_new (TRUE); /* ++ ref new control event */ g_value_init (&event->value, SWAMI_TYPE_EVENT_ITEM_ADD); /* boxed copy handles reference counting */ g_value_set_boxed (&event->value, item); swami_control_transmit_event (swami_patch_add_control, event); swami_control_event_unref (event); /* -- unref creator's reference */ } /* IpatchContainer "remove" notify */ static void container_remove_notify (IpatchContainer *container, IpatchItem *item, gpointer user_data) { SwamiControlEvent *event; SwamiEventItemRemove *item_remove; event = swami_control_event_new (TRUE); /* ++ ref new control event */ g_value_init (&event->value, SWAMI_TYPE_EVENT_ITEM_REMOVE); item_remove = swami_event_item_remove_new (); item_remove->item = g_object_ref (item); item_remove->parent = ipatch_item_get_parent (item); g_value_take_boxed (&event->value, item_remove); swami_control_transmit_event (swami_patch_remove_control, event); swami_control_event_unref (event); /* -- unref creator's reference */ } swami-2.2.0/src/libswami/libswami.def000066400000000000000000000170021361104770400175230ustar00rootroot00000000000000LIBRARY EXPORTS _swami_object_init ;_swami_param_init _swami_plugin_initialize _swami_ret_g_log ;_swami_set_patch_prop_origin_event ;_swami_state_types_init ;_swami_util_init ;group_parent_class DATA ;item_parent_class DATA ;patch_add_parent_class DATA ;patch_change_parent_class DATA ;patch_remove_parent_class DATA root_signals DATA ;state_parent_class DATA ;swami_add_patch_prop_callback ;swami_clear_item_prop_change_cache swami_control_conn_flags_get_type swami_control_conn_priority_get_type swami_control_connect ;swami_control_debug DATA swami_control_connect_item_prop swami_control_connect_transform swami_control_disconnect swami_control_disconnect_all swami_control_disconnect_unref swami_control_do_event_expiration swami_control_event_active_ref swami_control_event_active_unref swami_control_event_duplicate swami_control_event_free swami_control_event_get_type swami_control_event_new swami_control_event_ref swami_control_event_set_origin swami_control_event_stamp swami_control_event_unref swami_control_flags_get_type swami_control_func_assign_funcs swami_control_func_get_type swami_control_func_new swami_control_get_connections swami_control_get_flags swami_control_get_type swami_control_get_value swami_control_get_value_native swami_control_hub_get_type swami_control_hub_new swami_control_midi_get_type swami_control_midi_new swami_control_midi_send swami_control_midi_set_callback swami_control_midi_transmit swami_control_new swami_control_new_event ;swami_control_prop_assign_property swami_control_prop_get_type swami_control_prop_new swami_control_queue_add_event swami_control_queue_get_type swami_control_queue_new swami_control_queue_run swami_control_queue_set_test_func ;swami_control_ref_queue ;swami_control_ref_spec swami_control_set_event swami_control_set_event_no_queue swami_control_set_event_no_queue_loop swami_control_set_flags swami_control_set_queue swami_control_set_spec swami_control_set_value swami_control_set_value_no_queue swami_control_set_value_type ;swami_control_slave_spec swami_control_transmit_event swami_control_transmit_event_loop swami_control_transmit_value swami_control_value_alloc_value swami_control_value_assign_value swami_control_value_get_type swami_control_value_new swami_error_get_type swami_error_quark swami_event_item_add_copy swami_event_item_add_free swami_event_item_add_get_type swami_event_item_remove_copy swami_event_item_remove_free swami_event_item_remove_get_type swami_event_item_remove_new swami_event_prop_change_copy swami_event_prop_change_free swami_event_prop_change_get_type swami_event_prop_change_new swami_find_object_property swami_get_root swami_init swami_list_object_properties swami_lock_get_atomic swami_lock_get_type swami_lock_set_atomic swami_marshal_VOID__OBJECT_UINT swami_midi_device_close swami_midi_device_get_type swami_midi_device_new swami_midi_device_open ;swami_midi_device_ref_control swami_midi_event_bank_select swami_midi_event_control swami_midi_event_control14 swami_midi_event_copy swami_midi_event_free swami_midi_event_get_type swami_midi_event_new swami_midi_event_note_off swami_midi_event_note_on swami_midi_event_nrpn swami_midi_event_pitch_bend swami_midi_event_rpn swami_midi_event_set swami_midi_event_set_bend_range swami_midi_event_set_program swami_midi_event_type_get_type swami_object_clear_flags swami_object_find_by_type swami_object_flags_get_type swami_object_get swami_object_get_flags swami_object_get_property swami_object_get_valist swami_object_propbag_quark DATA ;swami_object_ref_by_name ;swami_object_ref_by_type ;swami_object_remove ;swami_object_remove_recursive ;swami_object_replace swami_object_get_origin swami_object_set swami_object_set_default swami_object_set_flags swami_object_set_property swami_object_set_valist swami_param_convert swami_param_convert_new ;swami_param_get_digits swami_param_get_limits ;swami_param_set_digits swami_param_set_limits swami_param_type_from_value_type ;swami_patch_add_control DATA ;swami_patch_add_control DATA ;Getter function returning swami_patch_add_control. swami_patch_get_add_control ;swami_patch_load_ref ;swami_patch_prop_control DATA ;swami_patch_prop_title_control ;Getter function returning swami_patch_prop_title_control. swami_patch_get_prop_title_control ;swami_patch_remove_control DATA ;Getter function returning swami_patch_remove_control. swami_patch_get_remove_control ;swami_patch_save swami_plugin_add_path swami_plugin_find swami_plugin_get_list swami_plugin_get_type swami_plugin_is_loaded swami_plugin_load swami_plugin_load_xml swami_plugin_save_xml swami_plugin_load_absolute swami_plugin_load_all swami_plugin_load_plugin swami_prop_tree_add_value swami_prop_tree_get_children swami_prop_tree_get_type swami_prop_tree_insert_before swami_prop_tree_new ;swami_prop_tree_node_flags_get_type swami_prop_tree_object_get_node swami_prop_tree_prepend swami_prop_tree_remove swami_prop_tree_remove_recursive swami_prop_tree_remove_value swami_prop_tree_replace swami_prop_tree_set_root ;swami_prop_tree_xml_save_state swami_rank_get_type ;swami_remove_patch_prop_callback ;swami_remove_patch_prop_callback_matched swami_root_add_object swami_root_get_objects swami_root_get_type swami_root_insert_object_before swami_root_new swami_root_new_object swami_root_patch_save swami_root_prepend_object ;swami_state_begin_group ;swami_state_end_group ;swami_state_get_type ;swami_state_get_undo_depends ;swami_state_group_flags_get_type ;swami_state_group_get_type ;swami_state_item_conflict ;swami_state_item_depend ;swami_state_item_describe ;swami_state_item_flags_get_type ;swami_state_item_get_type ;swami_state_item_restore ;swami_state_item_type_get_type ;swami_state_new ;swami_state_patch_add_get_type ;swami_state_patch_change_get_type ;swami_state_patch_remove_get_type ;swami_state_record ;swami_state_record_item ;swami_state_ref_active_group ;swami_state_retract ;swami_state_set_active_group ;swami_state_undo swami_type_get_children swami_type_get_default swami_type_get_rank swami_type_set_rank swami_util_free_value swami_util_get_child_types swami_util_new_value swami_util_midi_str_to_note swami_util_midi_note_to_str swami_wavetbl_close ;swami_wavetbl_get_temp_item_locale swami_wavetbl_get_control swami_wavetbl_get_type swami_wavetbl_load_patch ;swami_wavetbl_load_temp_item swami_wavetbl_open ;swami_wavetbl_ref_control ;swami_wavetbl_set_gen_realtime ;swami_wavetbl_set_temp_item_locale ;swami_xml_is_doc ;swami_xml_new_doc ;swami_xml_object_get_type ;swami_xml_object_get_xml ;swami_xml_object_set_xml ;swami_xml_read_file ;swami_xml_restore_ctx_add_fixup ;swami_xml_restore_ctx_fixup ;swami_xml_restore_ctx_get_type ;swami_xml_restore_ctx_new ;swami_xml_restore_object ;swami_xml_restore_object_state ;swami_xml_restore_object_state_by_type ;swami_xml_restore_properties_by_type ;swami_xml_save_ctx_get_object_id ;swami_xml_save_ctx_get_type ;swami_xml_save_ctx_new ;swami_xml_save_object ;swami_xml_save_object_node ;swami_xml_save_object_state ;swami_xml_save_object_state_by_type ;swami_xml_save_properties_by_type ;swami_xml_state_get_type ;swami_xml_write_file swami_loop_finder_new swami_loop_results_get_values swami_loop_finder_get_results swami_loop_finder_get_type swami_loop_finder_verify_params swami_loop_finder_find swami_wavetbl_get_active_item_locale swami_object_get_by_type swami_get_control_prop_by_name swami_control_get_spec swami_control_prop_connect_objects swami_param_type_transformable swami_get_control_prop swami_container_get_type swami_object_set_origin swami_root_patch_load swami_control_prop_connect_to_control ;swami_patch_add_control wavetbl_class DATA swami-2.2.0/src/libswami/libswami.h000066400000000000000000000046571361104770400172300ustar00rootroot00000000000000/* * libswami.h - Main libswami header file that includes all others * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __LIB_SWAMI_H__ #define __LIB_SWAMI_H__ #include #include #include #include /* libswami.c */ void swami_init (void); /* IpatchItem event controls */ extern SwamiControl *swami_patch_prop_title_control; extern SwamiControl *swami_patch_add_control; extern SwamiControl *swami_patch_remove_control; /* getter useful when libswami library is used as a shared library linked at load time.*/ /* Getter function returning swami_patch_prop_title_control.*/ SwamiControl *swami_patch_get_prop_title_control(void); /* Getter function returning swami_patch_add_control.*/ SwamiControl *swami_patch_get_add_control(void); /* Getter function returning swami_patch_remove_control.*/ SwamiControl *swami_patch_get_remove_control(void); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif swami-2.2.0/src/libswami/marshals.list000066400000000000000000000023451361104770400177470ustar00rootroot00000000000000# see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # PARAM for GParamSpec or derived types (GParamSpec*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN VOID:OBJECT,UINT swami-2.2.0/src/libswami/swami_priv.h000066400000000000000000000020201361104770400175570ustar00rootroot00000000000000/* * swami_priv.h - Private header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_PRIV_H__ #define __SWAMI_PRIV_H__ #include #include "i18n.h" #define SWAMI_PARAM_SPEC_ID(pspec) ((pspec)->param_id) #endif /* #ifndef __SWAMI_PRIV_H__ */ swami-2.2.0/src/libswami/util.c000066400000000000000000000105211361104770400163540ustar00rootroot00000000000000/* * util.c - Miscellaneous utility functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include "util.h" #include "swami_priv.h" static void recurse_types (GType type, GArray *array); /** * swami_util_get_child_types: * @type: Type to get all children from * @n_types: Location to store count of types or %NULL to ignore * * Recursively get all child types of a @type. * * Returns: Newly allocated and 0 terminated array of types */ GType * swami_util_get_child_types (GType type, guint *n_types) { GArray *array; GType *types; array = g_array_new (TRUE, FALSE, sizeof (GType)); recurse_types (type, array); if (n_types) *n_types = array->len; types = (GType *)(array->data); g_array_free (array, FALSE); return (types); } static void recurse_types (GType type, GArray *array) { GType *child_types, *ptype; child_types = g_type_children (type, NULL); for (ptype = child_types; *ptype; ptype++) { g_array_append_val (array, *ptype); recurse_types (*ptype, array); } g_free (child_types); } /** * swami_util_new_value: * * Allocate a new GValue using a GMemChunk. * * Returns: New uninitialized (zero) GValue */ GValue * swami_util_new_value (void) { return (g_slice_new0 (GValue)); } /** * swami_util_free_value: * @value: GValue created from swami_util_new_value(). * * Free a GValue that was allocated with swami_util_new_value(). */ void swami_util_free_value (GValue *value) { g_value_unset (value); g_slice_free (GValue, value); } /** * swami_util_midi_note_to_str: * @note: MIDI note number (0-127) * @str: Buffer to store string to (at least 5 bytes in length) */ void swami_util_midi_note_to_str (int note, char *str) { static const char *notes[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; char octavestr[3]; g_return_if_fail (note >= 0 && note <= 127); strcpy (str, notes[note % 12]); sprintf (octavestr, "%d", note / 12 - 2); /* C0 is actually note 24 */ strcat (str, octavestr); } /** * swami_util_midi_str_to_note: * @str: String to parse as a MIDI note * * Parse a string in the form "[A-Ga-g][b#]n" or "0"-"127" as a MIDI note. * Where 'n' is the octave number between -2 and 8. '#' is used to indicate * "sharp". 'b' means "flat". Examples: "C3" is middle C (note 60), * "F#-2" is note 5, "Db-2" is the same as "C#-2" (note 0). Any chars * following a valid MIDI note string are ignored. * * Returns: The MIDI note # or -1 on error (str is malformed). */ int swami_util_midi_str_to_note (const char *str) { guint8 octofs[7] = { 9, 11, 0, 2, 4, 5, 7 }; /* octave offset for A-G */ char *endptr; long int l; int note; char c; g_return_val_if_fail (str != NULL, -1); if (!*str) return (-1); /* try converting as a decimal string */ l = strtol (str, &endptr, 10); if (!*endptr && l >= 0 && l <= 127) return (l); /* get first char (should be a note character */ c = *str++; if (c >= 'A' && c <= 'G') note = octofs[c - 'A']; else if (c >= 'a' && c <= 'g') note = octofs[c - 'a']; else return (-1); c = *str++; if (c == '#') { note++; c = *str++; } else if (c == 'b') { note--; c = *str++; } else if (c == 0) return (-1); if (c == '-') { c = *str++; if (c == '1') note += 12; else if (c != '2') return (-1); } else if ((c >= '0' && c <= '8')) /* Only 1 digit? */ note += (c - '0') * 12 + 24; else return (-1); if (note >= 0 && note <= 127) return (note); else return (-1); } swami-2.2.0/src/libswami/util.h000066400000000000000000000022371361104770400163660ustar00rootroot00000000000000/* * util.h - Swami utility stuff * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_UTIL_H__ #define __SWAMI_UTIL_H__ GType *swami_util_get_child_types (GType type, guint *n_types); GValue *swami_util_new_value (void); void swami_util_free_value (GValue *value); void swami_util_midi_note_to_str (int note, char *str); int swami_util_midi_str_to_note (const char *str); #endif /* __SWAMI_UTIL_H__ */ swami-2.2.0/src/libswami/value_transform.c000066400000000000000000000040661361104770400206150ustar00rootroot00000000000000/* * value_transform.c - Value transform functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include static void value_transform_string_int (const GValue *src_value, GValue *dest_value); static void value_transform_string_double (const GValue *src_value, GValue *dest_value); void _swami_value_transform_init (void) { g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_string_int); g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, value_transform_string_double); } /* string to int transform function */ static void value_transform_string_int (const GValue *src_value, GValue *dest_value) { const char *str; char *endptr; long int lval = 0; str = g_value_get_string (src_value); if (str) { lval = strtol (str, &endptr, 10); if (*endptr != '\0' || endptr == str) lval = 0; } g_value_set_int (dest_value, lval); } /* string to double transform function */ static void value_transform_string_double (const GValue *src_value, GValue *dest_value) { const char *str; char *endptr; double dval = 0.0; str = g_value_get_string (src_value); if (str) { dval = strtod (str, &endptr); if (*endptr != '\0' || endptr == str) dval = 0; } g_value_set_double (dest_value, dval); } swami-2.2.0/src/libswami/version.h.in000066400000000000000000000025271361104770400175050ustar00rootroot00000000000000/* * version.h - Swami version information * * Swami * Copyright (C) 1999-2002 Josh Green * * 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 or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net */ #ifndef __SWAMI_VERSION_H__ #define __SWAMI_VERSION_H__ #define SWAMI_VERSION @SWAMI_VERSION@ #define SWAMI_VERSION_MAJOR @SWAMI_VERSION_MAJOR@ #define SWAMI_VERSION_MINOR @SWAMI_VERSION_MINOR@ #define SWAMI_VERSION_MICRO @SWAMI_VERSION_MICRO@ void swami_version (guint *major, guint *minor, guint *micro); #endif /* __SWAMI_VERSION_H__ */ swami-2.2.0/src/plugins/000077500000000000000000000000001361104770400151065ustar00rootroot00000000000000swami-2.2.0/src/plugins/CMakeLists.txt000066400000000000000000000067351361104770400176610ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # include_directories ( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${GOBJECT_INCLUDEDIR} ${GOBJECT_INCLUDE_DIRS} ${GUI_INCLUDEDIR} ${GUI_INCLUDE_DIRS} ${LIBINSTPATCH_INCLUDEDIR} ${LIBINSTPATCH_INCLUDE_DIRS} ${FLUIDSYNTH_INCLUDEDIR} ${FLUIDSYNTH_INCLUDE_DIRS} ${FFTW_INCLUDEDIR} ${FFTW_INCLUDE_DIRS} ) if ( FLUIDSYNTH_SUPPORT ) link_directories ( ${GOBJECT_LIBDIR} ${GOBJECT_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ${FLUIDSYNTH_LIBDIR} ${FLUIDSYNTH_LIBRARY_DIRS} ) set (DEFINITION_FILE_FLUIDSYNTH_PLUGIN "") set (DEFINITION_FILE_FLUIDSYNTH_GUI "") # Options for WINDOWS only if( MSVC ) if (BUILD_SHARED_LIBS) # adding a module definition file for shared libs will export symbols and produce import library set (DEFINITION_FILE_FLUIDSYNTH_PLUGIN fluidsynth_plugin.def) set (DEFINITION_FILE_FLUIDSYNTH_GUI fluidsynth_gui.def) endif(BUILD_SHARED_LIBS) endif( MSVC ) add_library ( fluidsynth_plugin MODULE fluidsynth.c ${DEFINITION_FILE_FLUIDSYNTH_PLUGIN}) target_link_libraries ( fluidsynth_plugin libswami libswamigui ${GOBJECT_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ${FLUIDSYNTH_LIBRARIES} ) link_directories ( ${GUI_LIBDIR} ${GUI_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ${FLUIDSYNTH_LIBDIR} ${FLUIDSYNTH_LIBRARY_DIRS} ) add_library ( fluidsynth_gui MODULE fluidsynth_gui.c ${DEFINITION_FILE_FLUIDSYNTH_GUI}) target_link_libraries ( fluidsynth_gui libswami libswamigui ${GUI_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ${FLUIDSYNTH_LIBRARIES} ) set_target_properties ( fluidsynth_plugin PROPERTIES PREFIX "" ) set_target_properties ( fluidsynth_gui PROPERTIES PREFIX "" ) install ( TARGETS fluidsynth_plugin fluidsynth_gui RUNTIME DESTINATION ${PLUGINS_DIR} LIBRARY DESTINATION ${PLUGINS_DIR} ) endif ( FLUIDSYNTH_SUPPORT ) if ( FFTW_SUPPORT ) link_directories ( ${GOBJECT_LIBDIR} ${GOBJECT_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ${FFTW_LIBDIR} ${FFTW_LIBRARY_DIRS} ) set (DEFINITION_FILE_FFTUNE "") set (DEFINITION_FILE_FFTUNE_GUI "") # Options for WINDOWS only if( MSVC ) # disable deprecation warnings add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) if (BUILD_SHARED_LIBS) # adding a module definition file for shared libs will export symbols and produce import library set (DEFINITION_FILE_FFTUNE fftune.def) set (DEFINITION_FILE_FFTUNE_GUI fftune_gui.def) endif(BUILD_SHARED_LIBS) endif( MSVC ) add_library ( fftune MODULE fftune.c ${DEFINITION_FILE_FFTUNE}) if (UNIX) set(MATH_LIB m) endif (UNIX) target_link_libraries ( fftune libswami ${GOBJECT_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ${FFTW_LIBRARIES} ${MATH_LIB} ) link_directories ( ${GUI_LIBDIR} ${GUI_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ) add_library ( fftune_gui MODULE fftune_gui.c ${DEFINITION_FILE_FFTUNE_GUI}) target_link_libraries ( fftune_gui libswami libswamigui ${GUI_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ) set_target_properties ( fftune PROPERTIES PREFIX "" ) set_target_properties ( fftune_gui PROPERTIES PREFIX "" ) install ( TARGETS fftune fftune_gui RUNTIME DESTINATION ${PLUGINS_DIR} LIBRARY DESTINATION ${PLUGINS_DIR} ) endif ( FFTW_SUPPORT ) swami-2.2.0/src/plugins/fftune.c000066400000000000000000000614211361104770400165450ustar00rootroot00000000000000/* * fftune.c - Fast Fourier Transform sample tuning plugin * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. * */ #include #include #include #include #include #include /* part of the FFTW package */ #include #include #include #include "fftune_i18n.h" #include "fftune.h" #define DEFAULT_THRESHOLD 0.1 #define DEFAULT_SEPARATION 20.0 #define DEFAULT_MIN_FREQ 20.0 #define DEFAULT_MAX_FREQ 14000.0 #define DEFAULT_MAX_TUNINGS 10 #define MAX_ALLOWED_TUNINGS 1024 /* absolute max tunings allowed */ /* Sample format used internally */ #define SAMPLE_FORMAT IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_ENDIAN_HOST enum { PROP_0, PROP_ACTIVE, /* when TRUE: spectrum and tuning suggestions are calculated any changes in parameters cause recalc of tuning suggestions */ PROP_SAMPLE, /* sample object to calculate spectrum on */ PROP_SAMPLE_MODE, /* sample calculation mode enum */ PROP_SAMPLE_START, /* start position in sample */ PROP_SAMPLE_END, /* end position in sample */ PROP_LIMIT, /* maximum samples to process or 0 for unlimited */ PROP_THRESHOLD, /* power threshold for tuning suggestions */ PROP_SEPARATION, /* min freq between consecutive tuning suggestions */ PROP_MIN_FREQ, /* minimum frequency for tuning suggestions */ PROP_MAX_FREQ, /* maximum frequency for tuning suggestions */ PROP_MAX_TUNINGS, /* max number of tuning suggestions */ /* properties for selecting and retrieving tuning suggestions */ PROP_TUNE_SELECT, /* for selecting tuning suggestions */ PROP_TUNE_COUNT, /* count of available tuning suggestions */ PROP_TUNE_INDEX, /* index in spectrum data for this tuning suggestion */ PROP_TUNE_POWER, /* power of current tuning (0.0 if no more suggestions) */ PROP_TUNE_FREQ, /* frequency of current tuning */ PROP_ENABLE_WINDOW, /* Enable Hann window of sample data */ PROP_ELLAPSED_TIME /* Ellapsed time of last execution in seconds */ }; enum { SPECTRUM_CHANGE, /* spectrum data change signal */ TUNINGS_CHANGE, /* tuning suggestions changed signal */ SIGNAL_COUNT }; #define ERRMSG_MALLOC_1 "Failed to allocate %u bytes in FFTune plugin" static gboolean plugin_fftune_init (SwamiPlugin *plugin, GError **err); static GType sample_mode_register_type (SwamiPlugin *plugin); static void fftune_spectra_finalize (GObject *object); static void fftune_spectra_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void fftune_spectra_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean fftune_spectra_calc_spectrum (FFTuneSpectra *spectra); static double *fftune_spectra_run_fftw (void *data, int *dsize); static gboolean fftune_spectra_calc_tunings (FFTuneSpectra *spectra); static gint tuneval_compare_func (gconstpointer a, gconstpointer b); /* set plugin information */ SWAMI_PLUGIN_INFO (plugin_fftune_init, NULL); /* define FFTuneSpectra type */ G_DEFINE_TYPE (FFTuneSpectra, fftune_spectra, G_TYPE_OBJECT); static GType sample_mode_enum_type; static guint obj_signals[SIGNAL_COUNT]; static gboolean plugin_fftune_init (SwamiPlugin *plugin, GError **err) { /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain ("SwamiPlugin-fftune", LOCALEDIR); #endif g_object_set (plugin, "name", "FFTune", "version", "1.0", "author", "Element Green", "copyright", "Copyright (C) 2004-2014", "descr", N_("Fast Fourier Transform sample tuner"), "license", "GPL", NULL); /* register types */ sample_mode_enum_type = sample_mode_register_type (plugin); fftune_spectra_get_type (); return (TRUE); } static GType sample_mode_register_type (SwamiPlugin *plugin) { static const GEnumValue values[] = { { FFTUNE_MODE_SELECTION, "FFTUNE_MODE_SELECTION", "Selection" }, { FFTUNE_MODE_LOOP, "FFTUNE_MODE_LOOP", "Loop" }, { 0, NULL, NULL } }; static GTypeInfo enum_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }; /* initialize the type info structure for the enum type */ g_enum_complete_type_info (G_TYPE_ENUM, &enum_info, values); return (g_type_module_register_type (G_TYPE_MODULE (plugin), G_TYPE_ENUM, "FFTuneSampleMode", &enum_info, 0)); } static void fftune_spectra_class_init (FFTuneSpectraClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = fftune_spectra_set_property; obj_class->get_property = fftune_spectra_get_property; obj_class->finalize = fftune_spectra_finalize; /* This signal gets emitted when spectrum data changes. The pointer argument is the new spectrum data (array of doubles), after this signal is emitted the old spectrum data should no longer be accessed. The unsigned int is the array size. */ obj_signals[SPECTRUM_CHANGE] = g_signal_new ("spectrum-change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); /* signal emitted when tuning suggestions change, first parameter is the count of tuning suggestions (equivalent to "tune-count") */ obj_signals[TUNINGS_CHANGE] = g_signal_new ("tunings-change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_object_class_install_property (obj_class, PROP_ACTIVE, g_param_spec_boolean ("active", _("Active"), _("Active"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE, g_param_spec_object ("sample", _("Sample"), _("Sample"), IPATCH_TYPE_SAMPLE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_MODE, g_param_spec_enum ("sample-mode", _("Sample mode"), _("Sample spectrum mode"), sample_mode_enum_type, FFTUNE_MODE_SELECTION, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_START, g_param_spec_uint ("sample-start", _("Sample start"), _("Sample start"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAMPLE_END, g_param_spec_uint ("sample-end", _("Sample end"), _("Sample end"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LIMIT, g_param_spec_uint ("limit", _("Limit"), _("Limit"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_THRESHOLD, g_param_spec_float ("threshold", _("Threshold"), _("Min ratio to max power of tuning suggestions"), (gfloat)0.0, (gfloat)1.0, (gfloat)DEFAULT_THRESHOLD, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SEPARATION, g_param_spec_float ("separation", _("Separation"), _("Min frequency separation between tunings"), 0.0, 24000.0, DEFAULT_SEPARATION, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MIN_FREQ, g_param_spec_float ("min-freq", _("Min frequency"), _("Min frequency of tuning suggestions"), 0.0, 24000.0, DEFAULT_MIN_FREQ, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MAX_FREQ, g_param_spec_float ("max-freq", _("Max frequency"), _("Max frequency of tuning suggestions"), 0.0, 24000.0, DEFAULT_MAX_FREQ, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MAX_TUNINGS, g_param_spec_int ("max-tunings", _("Max tunings"), _("Max tuning suggestions"), 0, MAX_ALLOWED_TUNINGS, DEFAULT_MAX_TUNINGS, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TUNE_SELECT, g_param_spec_int ("tune-select", _("Tune select"), _("Select tuning suggestion by index"), 0, MAX_ALLOWED_TUNINGS, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TUNE_COUNT, g_param_spec_int ("tune-count", _("Tune count"), _("Count of tuning suggestions"), 0, MAX_ALLOWED_TUNINGS, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TUNE_INDEX, g_param_spec_int ("tune-index", _("Tune index"), _("Spectrum index of current tuning"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TUNE_POWER, g_param_spec_double ("tune-power", _("Tune power"), _("Power of current tuning"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_TUNE_FREQ, g_param_spec_double ("tune-freq", _("Tune frequency"), _("Frequency of current tuning"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_ENABLE_WINDOW, g_param_spec_boolean ("enable-window", _("Enable window"), _("Enable window"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ELLAPSED_TIME, g_param_spec_float ("ellapsed-time", _("Ellapsed time"), _("Ellapsed time of last execution in seconds"), 0.0, G_MAXFLOAT, 0.0, G_PARAM_READABLE)); } static void fftune_spectra_init (FFTuneSpectra *spectra) { spectra->sample_mode = FFTUNE_MODE_SELECTION; spectra->threshold = (gfloat)DEFAULT_THRESHOLD; spectra->separation = DEFAULT_SEPARATION; spectra->min_freq = DEFAULT_MIN_FREQ; spectra->max_freq = DEFAULT_MAX_FREQ; spectra->max_tunings = DEFAULT_MAX_TUNINGS; } static void fftune_spectra_finalize (GObject *object) { FFTuneSpectra *spectra = FFTUNE_SPECTRA (object); if (spectra->spectrum) fftwf_free (spectra->spectrum); if (spectra->sample) g_object_unref (spectra->sample); if (spectra->tunevals) g_free (spectra->tunevals); } static void fftune_spectra_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { FFTuneSpectra *spectra = FFTUNE_SPECTRA (object); gboolean spectrum_update = FALSE; gboolean tunings_update = FALSE; switch (property_id) { case PROP_ACTIVE: if (!spectra->active && g_value_get_boolean (value)) /* activating? */ spectrum_update = TRUE; spectra->active = g_value_get_boolean (value); break; case PROP_SAMPLE: if (g_value_get_object (value) == spectra->sample) break; if (spectra->sample) g_object_unref (spectra->sample); spectra->sample = g_value_get_object (value); if (spectra->sample) g_object_ref (spectra->sample); spectrum_update = TRUE; break; case PROP_SAMPLE_MODE: if (g_value_get_enum (value) == spectra->sample_mode) break; spectra->sample_mode = g_value_get_enum (value); spectrum_update = TRUE; break; case PROP_SAMPLE_START: if (g_value_get_uint (value) == spectra->sample_start) break; spectra->sample_start = g_value_get_uint (value); spectrum_update = TRUE; break; case PROP_SAMPLE_END: if (g_value_get_uint (value) == spectra->sample_end) break; spectra->sample_end = g_value_get_uint (value); spectrum_update = TRUE; break; case PROP_LIMIT: if (g_value_get_uint (value) == spectra->limit) break; spectra->limit = g_value_get_uint (value); spectrum_update = TRUE; break; case PROP_THRESHOLD: if (g_value_get_float (value) == spectra->threshold) break; spectra->threshold = g_value_get_float (value); tunings_update = TRUE; break; case PROP_SEPARATION: if (g_value_get_float (value) == spectra->separation) break; spectra->separation = g_value_get_float (value); tunings_update = TRUE; break; case PROP_MIN_FREQ: if (g_value_get_float (value) == spectra->min_freq) break; spectra->min_freq = g_value_get_float (value); tunings_update = TRUE; break; case PROP_MAX_FREQ: if (g_value_get_float (value) == spectra->max_freq) break; spectra->max_freq = g_value_get_float (value); tunings_update = TRUE; break; case PROP_MAX_TUNINGS: if (g_value_get_int (value) == spectra->max_tunings) break; spectra->max_tunings = g_value_get_int (value); tunings_update = TRUE; break; case PROP_TUNE_SELECT: spectra->tune_select = g_value_get_int (value); break; case PROP_ENABLE_WINDOW: if (g_value_get_boolean (value) == spectra->enable_window) break; spectra->enable_window = g_value_get_boolean (value); spectrum_update = TRUE; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } if (spectra->active && spectra->sample) { if (spectrum_update) /* spectrum need update? */ { if (fftune_spectra_calc_spectrum (spectra)) fftune_spectra_calc_tunings (spectra); } else if (tunings_update) /* tuning values need update? */ fftune_spectra_calc_tunings (spectra); } } static void fftune_spectra_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { FFTuneSpectra *spectra = FFTUNE_SPECTRA (object); int index; switch (property_id) { case PROP_ACTIVE: g_value_set_boolean (value, spectra->active); break; case PROP_SAMPLE: g_value_set_object (value, spectra->sample); break; case PROP_SAMPLE_MODE: g_value_set_enum (value, spectra->sample_mode); break; case PROP_SAMPLE_START: g_value_set_uint (value, spectra->sample_start); break; case PROP_SAMPLE_END: g_value_set_uint (value, spectra->sample_end); break; case PROP_LIMIT: g_value_set_uint (value, spectra->limit); break; case PROP_THRESHOLD: g_value_set_float (value, spectra->threshold); break; case PROP_SEPARATION: g_value_set_float (value, spectra->separation); break; case PROP_MIN_FREQ: g_value_set_float (value, spectra->min_freq); break; case PROP_MAX_FREQ: g_value_set_float (value, spectra->max_freq); break; case PROP_MAX_TUNINGS: g_value_set_int (value, spectra->max_tunings); break; case PROP_TUNE_SELECT: g_value_set_int (value, spectra->tune_select); break; case PROP_TUNE_COUNT: g_value_set_int (value, spectra->n_tunevals); break; case PROP_TUNE_INDEX: if (spectra->tunevals && spectra->tune_select < spectra->n_tunevals) g_value_set_int (value, spectra->tunevals[spectra->tune_select]); else g_value_set_int (value, 0); break; case PROP_TUNE_POWER: if (spectra->tunevals && spectra->tune_select < spectra->n_tunevals && spectra->spectrum) { index = spectra->tunevals[spectra->tune_select]; g_value_set_double (value, spectra->spectrum[index]); } else g_value_set_double (value, 0.0); break; case PROP_TUNE_FREQ: if (spectra->tunevals && spectra->tune_select < spectra->n_tunevals && spectra->spectrum) { index = spectra->tunevals[spectra->tune_select]; g_value_set_double (value, spectra->freqres * index); } else g_value_set_double (value, 0.0); break; case PROP_ENABLE_WINDOW: g_value_set_boolean (value, spectra->enable_window); break; case PROP_ELLAPSED_TIME: g_value_set_float (value, spectra->ellapsed_time); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean fftune_spectra_calc_spectrum (FFTuneSpectra *spectra) { guint start, sample_size, loop_start, loop_end; IpatchSampleHandle handle; GError *err = NULL; void *data; /* Stores sample data (floats) */ double *result; /* FFT power spectrum result */ int result_size; guint count, i , dsize; GTimeVal start_time, end_time; float ellapsed; g_return_val_if_fail (spectra->sample != NULL, FALSE); g_object_get (spectra->sample, "sample-size", &sample_size, "loop-start", &loop_start, "loop-end", &loop_end, NULL); if (spectra->sample_mode == FFTUNE_MODE_LOOP) /* loop mode? */ { start = loop_start; count = loop_end - loop_start; /* number of samples in loop */ dsize = count * 2 + 1; /* 2 iterations + 1 (first sample appended) */ } else /* selection mode */ { if (spectra->sample_start == 0 && spectra->sample_end == 0) { start = 0; count = sample_size; } else { if (spectra->sample_start <= spectra->sample_end) { start = spectra->sample_start; count = spectra->sample_end - spectra->sample_start + 1; } else /* swap start/end if backwards */ { start = spectra->sample_end; count = spectra->sample_start - spectra->sample_end + 1; } } if (spectra->limit && count > spectra->limit) count = spectra->limit; dsize = count; } /* Allocate sample/result buffer, sample data is stored as floats and result * is stored as doubles (dsize / 2 + 1 in length). */ data = g_try_malloc (sizeof (double) * (dsize / 2 + 1)); /* allocate transform array */ if (!data) { g_critical (_(ERRMSG_MALLOC_1), (guint)(sizeof (double) * (dsize / 2 + 1))); return (FALSE); } if (!ipatch_sample_handle_open (spectra->sample, &handle, 'r', SAMPLE_FORMAT, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, &err)) { g_critical ("Failed to open sample in FFTune plugin: %s", ipatch_gerror_message (err)); g_error_free (err); g_free (data); return (FALSE); } /* load the sample data */ if (!ipatch_sample_handle_read (&handle, start, count, data, &err)) { g_critical ("Failed to read sample data in FFTune plugin: %s", ipatch_gerror_message (err)); g_error_free (err); ipatch_sample_handle_close (&handle); g_free (data); return (FALSE); } ipatch_sample_handle_close (&handle); if (spectra->sample_mode == FFTUNE_MODE_LOOP) /* do 2 iterations for loops */ { for (i=0; i < count; i++) ((float *)data)[i + count] = ((float *)data)[i]; /* append first sample to end to complete the 2 cycles */ ((float *)data)[dsize - 1] = ((float *)data)[0]; } result_size = dsize; g_get_current_time (&start_time); if (spectra->enable_window) { for (i = 0; i < dsize; i++) ((float *)data)[i] *= (float)(0.5 * (1.0 - cos (2.0 * G_PI * ((float)i / (dsize - 1))))); } /* Calculate FFT power spectrum of sample data. Result is returned in the same array. */ result = fftune_spectra_run_fftw (data, &result_size); g_get_current_time (&end_time); ellapsed = (float)((end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) / 1000000.0); spectra->ellapsed_time = ellapsed; if (!result) { g_free (data); return (FALSE); } /* emit spectrum-change signal */ g_signal_emit (spectra, obj_signals[SPECTRUM_CHANGE], 0, result_size, result); g_free (spectra->spectrum); spectra->spectrum = result; spectra->spectrum_size = result_size; return (TRUE); } static double * fftune_spectra_run_fftw (void *data, int *dsize) { fftwf_plan fftplan; float *outdata; int size = *dsize; int outsize; int i, x; outsize = size / 2 + 1; outdata = fftwf_malloc (sizeof (float) * size); /* allocate output transform array */ if (!outdata) { g_critical (_(ERRMSG_MALLOC_1), (guint)(sizeof (float) * size)); return (NULL); } /* create FFTW plan (real to half complex, in place transform) */ fftplan = fftwf_plan_r2r_1d (size, data, outdata, FFTW_R2HC, FFTW_ESTIMATE); fftwf_execute (fftplan); /* do the FFT calculation */ fftwf_destroy_plan (fftplan); /* compute power spectrum (its stored over the output array) */ ((double *)data)[0] = outdata[0] * outdata[0]; /* DC component */ x = (size+1) / 2; for (i=1; i < x; ++i) /* (i < size/2 rounded up) */ ((double *)data)[i] = outdata[i] * outdata[i] + outdata[size - i] * outdata[size - i]; if (size % 2 == 0) /* count is even? */ ((double *)data)[size / 2] = outdata[size / 2] * outdata[size / 2]; /* Nyquist freq. */ fftwf_free (outdata); *dsize = outsize; /* Resize data array to size of FFT power data */ data = realloc (data, sizeof (double) * outsize); return (data); } /* typebag for temporary sorted GList of tunings. Wouldn't need it if * g_list_insert_sorted had a user data parameter -:( */ typedef struct { double *spectrum; int index; } TuneBag; static gboolean fftune_spectra_calc_tunings (FFTuneSpectra *spectra) { GList *sugvals = NULL, *p; /* list of spectrum indexes */ double *spectrum; TuneBag *bag; int sugcount = 0; int tolndx, tolcount; double max, val, full_max, threshold; int i, start, stop, maxndx; int sample_rate; g_return_val_if_fail (spectra->sample != NULL, FALSE); g_return_val_if_fail (spectra->spectrum != NULL, FALSE); spectrum = spectra->spectrum; threshold = spectra->threshold; g_object_get (spectra->sample, "sample-rate", &sample_rate, NULL); /* frequency resolution (frequency difference between array indexes) */ spectra->freqres = (double)sample_rate / ((spectra->spectrum_size - 1) * 2); /* separation amount in array index units for selecting unique tunings */ tolndx = (int)(spectra->separation / spectra->freqres + 0.5); /* don't care about anything below min_freq */ start = (int)(spectra->min_freq / spectra->freqres) + 1; start = CLAMP (start, 0, spectra->spectrum_size - 1); /* don't care about anything above max_freq */ stop = (int)(spectra->max_freq / spectra->freqres); stop = CLAMP (stop, 0, spectra->spectrum_size - 1); /* get maximum power in frequency range (full_max) */ for (i = start, full_max = 0.0; i <= stop; i++) { val = spectrum[i]; if (val > full_max) full_max = val; } if (full_max == 0.0) full_max = 1.0; /* div/0 protection */ /* printf ("spectrum_size = %d, freqres = %0.2f, tolndx = %d, start = %d, stop = %d\n", spectra->spectrum_size, spectra->freqres, tolndx, start, stop); */ tolcount = tolndx; /* separation counter */ max = 0.0; /* current maximum power */ maxndx = -1; /* spectrum index of current maximum */ for (i = start; i <= stop; i++) { val = spectra->spectrum[i]; /* power of frequency exceeds full_max threshold ratio? */ if (val / full_max >= threshold) { if (val > max) /* find frequency with most power */ { /* new maximum */ max = val; maxndx = i; } tolcount = tolndx; /* reset separation counter */ } /* Do we have a frequency suggestion and no threshold, last index or separation counter expired? */ if (maxndx >= 0 && (threshold == 0.0 || tolcount-- <= 0 || i == stop - 1)) { /* if max tunevals exceeded, remove the least powerful one i.e. the little guy gets sacked. */ if (sugcount == spectra->max_tunings) { bag = sugvals->data; /* steal the TuneBag */ sugvals = g_list_delete_link (sugvals, sugvals); } else { bag = g_new (TuneBag, 1); bag->spectrum = spectra->spectrum; sugcount++; } bag->index = maxndx; /* add to tuning suggestion list using sort function (reverse sort) */ sugvals = g_list_insert_sorted (sugvals, bag, (GCompareFunc)tuneval_compare_func); /* reset variables */ tolcount = tolndx; max = 0.0; maxndx = -1; } } spectra->n_tunevals = sugcount; if (sugcount > 0) { spectra->tunevals = g_realloc (spectra->tunevals, sizeof (int) * sugcount); /* list is reverse sorted, so copy to descending array indexes */ for (i = sugcount - 1, p = sugvals; i >= 0; i--) { bag = (TuneBag *)(p->data); spectra->tunevals[i] = bag->index; g_free (bag); p = g_list_delete_link (p, p); } } else { g_free (spectra->tunevals); spectra->tunevals = NULL; } spectra->tune_select = 0; /* emit tunings-change signal */ g_signal_emit (spectra, obj_signals[TUNINGS_CHANGE], 0, sugcount); return (TRUE); } /* reverse sorted, so that tuneval with smallest value is always first for quick removal */ static gint tuneval_compare_func (gconstpointer a, gconstpointer b) { TuneBag *abag = (TuneBag *)a, *bbag = (TuneBag *)b; double f; f = abag->spectrum[abag->index] - bbag->spectrum[bbag->index]; if (f < 0.0) return -1; if (f > 0.0) return 1; return 0; } swami-2.2.0/src/plugins/fftune.def000066400000000000000000000000721361104770400170540ustar00rootroot00000000000000LIBRARY EXPORTS fftune_spectra_get_type swami_plugin_info swami-2.2.0/src/plugins/fftune.h000066400000000000000000000062251361104770400165530ustar00rootroot00000000000000/* * fftune.h - Header file for FFTW sample tuning plugin * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __PLUGIN_FFTUNE_H__ #define __PLUGIN_FFTUNE_H__ #include #include #define FFTUNE_TYPE_SPECTRA (fftune_spectra_get_type ()) #define FFTUNE_SPECTRA(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), FFTUNE_TYPE_SPECTRA, FFTuneSpectra)) #define FFTUNE_SPECTRA_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), FFTUNE_TYPE_SPECTRA, FFTuneSpectraClass)) #define FFTUNE_IS_SPECTRA(obj) \ (G_TYPE_CHECK_INSTANCE ((obj), FFTUNE_TYPE_SPECTRA)) #define FFTUNE_IS_SPECTRA_CLASS(klass) \ (G_TYPE_CHECK_CLASS ((klass), FFTUNE_TYPE_SPECTRA)) /* size in bytes for sample copy buffer */ #define FFTUNE_SAMPLE_COPY_BUFFER_SIZE (32 * 1024) /* FFT power spectrum object */ typedef struct { GObject parent; gboolean active; /* if TRUE, then change of params will update spectrum */ IpatchSample *sample; /* sample to calculate spectrum on */ int sample_mode; /* FFTUNE_MODE_* */ guint sample_start; /* start of selection (FFTUNE_MODE_SELECTION) */ guint sample_end; /* end of selection (start/end=0 will use entire sample) */ guint limit; /* maximum number of samples to process or 0 for unlimited */ double *spectrum; /* spectrum power data */ int spectrum_size; /* size of spectrum array */ double freqres; /* freq difference between consecutive spectrum indexes */ double max_index; /* index in spectrum of maximum power */ int *tunevals; /* array of spectrum indexes for tuning suggestions */ int n_tunevals; /* size of tunevals */ int tune_select; /* selected index in tunevals */ float threshold; /* power threshold for potential tuning suggestions */ float separation; /* min freq spread between suggestions */ float min_freq; /* min freq for tuning suggestions */ float max_freq; /* max freq for tuning suggestions */ int max_tunings; /* maximum tuning suggestions to find */ int enable_window; /* Enable Hann windowing of sample data */ float ellapsed_time; /* Ellapsed time of last execution in seconds */ } FFTuneSpectra; typedef struct { GObjectClass parent_class; } FFTuneSpectraClass; /* sample calculation mode enum */ enum { FFTUNE_MODE_SELECTION,/* sample start/end selection (entire sample if 0/0) */ FFTUNE_MODE_LOOP /* sample loop */ }; GType fftune_spectra_get_type (void); FFTuneSpectra *fftune_spectra_new (void); #endif swami-2.2.0/src/plugins/fftune_gui.c000066400000000000000000001036341361104770400174140ustar00rootroot00000000000000/* * fftune_gui.h - FFTune GUI object (Fast Fourier sample tuning GUI) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include #include #include /* log_if_fail() */ #include "fftune_gui.h" /* the range of time for the mouse wheel scroll zoom speed function, times are in milliseconds and represent the time between events */ #define WHEEL_ZOOM_MIN_TIME 10 /* fastest event time interval */ #define WHEEL_ZOOM_MAX_TIME 500 /* slowest event time interval */ /* zoom in speed range defined for the time interval above */ #define WHEEL_ZOOM_MIN_SPEED 0.98 /* slowest zoom in speed */ #define WHEEL_ZOOM_MAX_SPEED 0.7 /* fastest zoom in speed */ #define WHEEL_ZOOM_RANGE (WHEEL_ZOOM_MIN_SPEED - WHEEL_ZOOM_MAX_SPEED) /* min zoom value (indexes/pixel) */ #define SPECTRUM_CANVAS_MIN_ZOOM 0.02 #define SNAP_TIMEOUT_PRIORITY (G_PRIORITY_HIGH_IDLE + 40) #define SNAP_TIMEOUT_PIXEL_RANGE 60 /* range in pixels of timeout varience */ #define SNAP_TIMEOUT_MIN 40 /* minimum timeout in milliseconds */ #define SNAP_TIMEOUT_MAX 120 /* maximum timeout in milliseconds */ #define SNAP_SCROLL_MIN 1 /* minimum scroll amount in pixels */ #define SNAP_SCROLL_MULT 6.0 /* scroll multiplier in pixels */ /* range of zoom speeds over SNAP_TIMEOUT_PIXEL_RANGE */ #define SNAP_ZOOM_MIN 0.99 /* minimum (slowest) zoom in amount (< 1.0) */ #define SNAP_ZOOM_MAX 0.26 /* max (fastest) zoom in amount (< 1.0) */ enum { PROP_0, PROP_ITEM_SELECTION }; /* columns for fftunegui->freq_store */ enum { COL_POWER, /* power ratio column */ COL_FREQ, /* frequency column */ COL_NOTE, /* MIDI note column */ COL_CENTS, /* fractional tuning (cents) */ COL_COUNT /* count of columns */ }; typedef struct { int limit; char *label; } LimitInfo; /* Default index in limits[] array below */ #define DEFAULT_LIMIT_INDEX 2 /* Items in limits GtkComboBox */ const LimitInfo limits[] = { { 0, N_("Unlimited") }, { 128 * 1024, N_("128K") }, { 64 * 1024, N_("64K") }, { 32 * 1024, N_("32K") }, { 16 * 1024, N_("16K") } }; static gboolean plugin_fftune_gui_init (SwamiPlugin *plugin, GError **err); static void fftune_gui_panel_iface_init (SwamiguiPanelIface *panel_iface); static gboolean fftune_gui_panel_iface_check_selection (IpatchList *selection, GType *selection_types); static void fftune_gui_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void fftune_gui_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void fftune_gui_finalize (GObject *object); static void fftunegui_cb_revert_clicked (GtkButton *button, gpointer user_data); static void fftunegui_cb_freq_list_sel_changed (GtkTreeSelection *selection, gpointer user_data); static void fftune_gui_cb_spectrum_change (FFTuneSpectra *spectra, guint size, double *spectrum, gpointer user_data); static void fftune_gui_cb_tunings_change (FFTuneSpectra *spectra, guint count, gpointer user_data); static void fftune_gui_cb_mode_menu_changed (GtkComboBox *combo, gpointer user_data); static void fftune_gui_cb_limit_changed (GtkComboBox *combo, gpointer user_data); static void fftune_gui_cb_window_toggled (GtkToggleButton *button, gpointer user_data); static void fftune_gui_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data); static void fftune_gui_cb_scroll (GtkAdjustment *adj, gpointer user_data); static gboolean fftune_gui_cb_spectrum_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data); static gboolean fftune_gui_snap_timeout (gpointer data); static void fftune_gui_cb_ampl_zoom_value_changed (GtkAdjustment *adj, gpointer user_data); static void fftune_gui_zoom_ofs (FFTuneGui *fftunegui, double zoom_amt, int zoom_xpos); static void fftune_gui_scroll_ofs (FFTuneGui *fftunegui, int index_ofs); #ifdef _WIN32 /* * Parse the first float found in a char array. Parsing is terminated * when the end of string is reach or a non digit caracter is encountered. * @param str char array to parse * @return the first float detected in str or 0.0f. * Example: * "123.2" -> 123.2f * "123.2text" -> 123.2f * "text123.2" -> 0.0f */ static float strtof(const char* str, char **endptr) { return (float)strtod(str, endptr); } /* return the integer nearest num float value */ static int roundf(float num) { return (int)(num < 0 ? num - 0.5 : num + 0.5); } #endif /* set plugin information */ SWAMI_PLUGIN_INFO (plugin_fftune_gui_init, NULL); /* define FFTuneGui type */ G_DEFINE_TYPE_WITH_CODE (FFTuneGui, fftune_gui, GTK_TYPE_VBOX, G_IMPLEMENT_INTERFACE (SWAMIGUI_TYPE_PANEL, fftune_gui_panel_iface_init)); static gboolean plugin_fftune_gui_init (SwamiPlugin *plugin, GError **err) { /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain ("SwamiPlugin-fftune_gui", LOCALEDIR); #endif g_object_set (plugin, "name", "FFTuneGui", "version", "1.0", "author", "Element Green", "copyright", "Copyright (C) 2005-2014", "descr", N_("GUI for Fast Fourier Transform sample tuner"), "license", "GPL", NULL); /* register types and register it as a panel interface */ swamigui_register_panel_selector_type (fftune_gui_get_type (), 200); return (TRUE); } static void fftune_gui_class_init (FFTuneGuiClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = fftune_gui_set_property; obj_class->get_property = fftune_gui_get_property; obj_class->finalize = fftune_gui_finalize; g_object_class_override_property (obj_class, PROP_ITEM_SELECTION, "item-selection"); } static void fftune_gui_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("FFTune"); panel_iface->blurb = _("Semi-automated tuning plugin"); panel_iface->stockid = SWAMIGUI_STOCK_TUNING; panel_iface->check_selection = fftune_gui_panel_iface_check_selection; } static gboolean fftune_gui_panel_iface_check_selection (IpatchList *selection, GType *selection_types) { /* a single item with sample interface is valid */ return (!selection->items->next && g_type_is_a (*selection_types, IPATCH_TYPE_SAMPLE)); } static void fftune_gui_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { FFTuneGui *fftunegui = FFTUNE_GUI (object); SwamiControl *samctrl; IpatchSample *sample = NULL; IpatchSampleData *sampledata = NULL; IpatchList *list; int root_note, fine_tune; switch (property_id) { case PROP_ITEM_SELECTION: list = g_value_get_object (value); /* only set sample if a single IpatchSample item or NULL list */ if (list && list->items && !list->items->next && IPATCH_IS_SAMPLE (list->items->data)) { sample = IPATCH_SAMPLE (list->items->data); g_object_get (sample, "sample-data", &sampledata, NULL); } /* disconnect GUI controls (if connected) */ swami_control_disconnect_all (fftunegui->root_note_ctrl); swami_control_disconnect_all (fftunegui->fine_tune_ctrl); /* connect controls to sample properties */ if (sampledata) { g_object_get (sample, "root-note", &root_note, "fine-tune", &fine_tune, NULL); fftunegui->orig_root_note = root_note; fftunegui->orig_fine_tune = fine_tune; samctrl = swami_get_control_prop_by_name (G_OBJECT (sample), "root-note"); swami_control_connect (samctrl, fftunegui->root_note_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); samctrl = swami_get_control_prop_by_name (G_OBJECT (sample), "fine-tune"); swami_control_connect (samctrl, fftunegui->fine_tune_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); } /* recalculate full zoom */ fftunegui->recalc_zoom = TRUE; /* de-activate spectra object before setting sample */ g_object_set (fftunegui->spectra, "active", FALSE, NULL); /* reset amplitude zoom */ gtk_range_set_value (GTK_RANGE (fftunegui->vscale), 1.0); g_object_set (fftunegui->spectra, "sample", sample, NULL); /* re-activate spectra if sample is set */ if (sample) g_object_set (fftunegui->spectra, "active", TRUE, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void fftune_gui_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { FFTuneGui *fftunegui = FFTUNE_GUI (object); IpatchSampleData *sample = NULL; IpatchList *list; switch (property_id) { case PROP_ITEM_SELECTION: list = ipatch_list_new (); g_object_get (fftunegui->spectra, "sample", &sample, NULL); if (sample) list->items = g_list_append (list->items, sample); g_value_set_object (value, list); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void fftune_gui_finalize (GObject *object) { FFTuneGui *fftunegui = FFTUNE_GUI (object); g_object_unref (fftunegui->spectra); swami_control_disconnect_unref (fftunegui->root_note_ctrl); swami_control_disconnect_unref (fftunegui->fine_tune_ctrl); if (G_OBJECT_CLASS (fftune_gui_parent_class)->finalize) G_OBJECT_CLASS (fftune_gui_parent_class)->finalize (object); } static void fftune_gui_init (FFTuneGui *fftunegui) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *scrollwin; GtkWidget *box, *vbox, *hbox; GtkWidget *lbl; GtkWidget *frame; GtkStyle *style; GtkAdjustment *adj; GtkWidget *widg; int i; fftunegui->snap_active = FALSE; fftunegui->snap_timeout_handler = 0; fftunegui->scroll_active = FALSE; fftunegui->zoom_active = FALSE; fftunegui->snap_line_color = 0xFF0000FF; /* create spectrum tuning object */ fftunegui->spectra = g_object_new (g_type_from_name ("FFTuneSpectra"), NULL); /* Set default FFT limit */ g_object_set (fftunegui->spectra, "limit", limits[DEFAULT_LIMIT_INDEX].limit, NULL); /* connect to spectrum change signal */ g_signal_connect (fftunegui->spectra, "spectrum-change", G_CALLBACK (fftune_gui_cb_spectrum_change), fftunegui); /* connect to tunings change signal */ g_signal_connect (fftunegui->spectra, "tunings-change", G_CALLBACK (fftune_gui_cb_tunings_change), fftunegui); /* horizontal box to pack sample data selector, etc */ box = gtk_hbox_new (FALSE, 4); gtk_widget_show (box); lbl = gtk_label_new (_("Data")); gtk_widget_show (lbl); gtk_box_pack_start (GTK_BOX (box), lbl, FALSE, FALSE, 0); fftunegui->mode_menu = gtk_combo_box_new_text (); gtk_widget_show (fftunegui->mode_menu); gtk_box_pack_start (GTK_BOX (box), fftunegui->mode_menu, FALSE, FALSE, 0); gtk_combo_box_append_text (GTK_COMBO_BOX (fftunegui->mode_menu), _("All")); gtk_combo_box_append_text (GTK_COMBO_BOX (fftunegui->mode_menu), _("Loop")); gtk_combo_box_set_active (GTK_COMBO_BOX (fftunegui->mode_menu), 0); g_signal_connect (fftunegui->mode_menu, "changed", (GCallback)fftune_gui_cb_mode_menu_changed, fftunegui); lbl = gtk_label_new (_("Limit")); gtk_widget_show (lbl); gtk_box_pack_start (GTK_BOX (box), lbl, FALSE, FALSE, 0); widg = gtk_combo_box_new_text (); gtk_widget_show (widg); gtk_box_pack_start (GTK_BOX (box), widg, FALSE, FALSE, 0); for (i = 0; i < G_N_ELEMENTS (limits); i++) gtk_combo_box_append_text (GTK_COMBO_BOX (widg), _(limits[i].label)); gtk_combo_box_set_active (GTK_COMBO_BOX (widg), DEFAULT_LIMIT_INDEX); g_signal_connect (widg, "changed", (GCallback)fftune_gui_cb_limit_changed, fftunegui); widg = gtk_toggle_button_new_with_label (_("Window")); gtk_widget_show (widg); gtk_box_pack_start (GTK_BOX (box), widg, FALSE, FALSE, 0); g_signal_connect (widg, "toggled", (GCallback)fftune_gui_cb_window_toggled, fftunegui); lbl = gtk_label_new (_("Root note")); gtk_widget_show (lbl); gtk_box_pack_start (GTK_BOX (box), lbl, FALSE, FALSE, 0); fftunegui->root_notesel = swamigui_note_selector_new (); gtk_widget_show (fftunegui->root_notesel); gtk_box_pack_start (GTK_BOX (box), fftunegui->root_notesel, FALSE, FALSE, 0); /* create root note control and add to GUI queue */ adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (fftunegui->root_notesel)); fftunegui->root_note_ctrl = SWAMI_CONTROL (swamigui_control_adj_new (adj)); lbl = gtk_label_new (_("Fine tune")); gtk_widget_show (lbl); gtk_box_pack_start (GTK_BOX (box), lbl, FALSE, FALSE, 0); adj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -99.0, 99.0, 1.0, 5.0, 0.0)); fftunegui->fine_tune = gtk_spin_button_new (adj, 1.0, 0); gtk_widget_show (fftunegui->fine_tune); gtk_box_pack_start (GTK_BOX (box), fftunegui->fine_tune, FALSE, FALSE, 0); /* create fine tune control */ fftunegui->fine_tune_ctrl = SWAMI_CONTROL (swamigui_control_adj_new (adj)); widg = gtk_vseparator_new (); gtk_widget_show (widg); gtk_box_pack_start (GTK_BOX (box), widg, FALSE, FALSE, 0); fftunegui->revert_button = gtk_button_new_with_mnemonic (_("_Revert")); gtk_widget_set_tooltip_text (fftunegui->revert_button, _("Revert to original tuning values")); gtk_widget_show (fftunegui->revert_button); gtk_box_pack_start (GTK_BOX (box), fftunegui->revert_button, FALSE, FALSE, 0); g_signal_connect (fftunegui->revert_button, "clicked", G_CALLBACK (fftunegui_cb_revert_clicked), fftunegui); /* vbox to set vertical spacing of upper outtie frame */ vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2); /* upper outtie frame, with spectrum data selector, etc */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); gtk_container_set_border_width (GTK_CONTAINER (frame), 0); gtk_widget_show (frame); gtk_box_pack_start (GTK_BOX (fftunegui), frame, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); /* lower inset frame for spectrum canvas */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 0); gtk_widget_show (frame); gtk_box_pack_start (GTK_BOX (fftunegui), frame, TRUE, TRUE, 0); /* attach a horizontal scrollbar to the spectrum view */ fftunegui->hscrollbar = gtk_hscrollbar_new (NULL); gtk_widget_show (fftunegui->hscrollbar); gtk_box_pack_start (GTK_BOX (fftunegui), fftunegui->hscrollbar, FALSE, FALSE, 0); g_signal_connect_after (gtk_range_get_adjustment (GTK_RANGE (fftunegui->hscrollbar)), "value-changed", G_CALLBACK (fftune_gui_cb_scroll), fftunegui); /* hbox for frequency list and desired root note selector */ hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox); gtk_container_add (GTK_CONTAINER (frame), hbox); /* create frequency suggestion store */ fftunegui->freq_store = gtk_list_store_new (COL_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); /* scroll window for frequency suggestion list */ scrollwin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_widget_show (scrollwin); gtk_box_pack_start (GTK_BOX (hbox), scrollwin, FALSE, FALSE, 0); /* create frequency suggestion list */ fftunegui->freq_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (fftunegui->freq_store)); /* Disable search, since it breaks piano key playback */ g_object_set (fftunegui->freq_list, "enable-search", FALSE, NULL); gtk_widget_show (fftunegui->freq_list); gtk_container_add (GTK_CONTAINER (scrollwin), GTK_WIDGET (fftunegui->freq_list)); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (fftunegui->freq_list)), "changed", G_CALLBACK (fftunegui_cb_freq_list_sel_changed), fftunegui); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Power"), renderer, "text", COL_POWER, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (fftunegui->freq_list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Frequency"), renderer, "text", COL_FREQ, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (fftunegui->freq_list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Note"), renderer, "text", COL_NOTE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (fftunegui->freq_list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Cents"), renderer, "text", COL_CENTS, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (fftunegui->freq_list), column); /* create canvas */ fftunegui->canvas = GNOME_CANVAS (gnome_canvas_new ()); gnome_canvas_set_center_scroll_region (GNOME_CANVAS (fftunegui->canvas), FALSE); gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (fftunegui->canvas), TRUE, TRUE, 0); g_signal_connect (fftunegui->canvas, "event", G_CALLBACK (fftune_gui_cb_spectrum_canvas_event), fftunegui); g_signal_connect (fftunegui->canvas, "size-allocate", G_CALLBACK (fftune_gui_cb_canvas_size_allocate), fftunegui); /* change background color of canvas to black */ style = gtk_style_copy (gtk_widget_get_style (GTK_WIDGET (fftunegui->canvas))); style->bg[GTK_STATE_NORMAL] = style->black; gtk_widget_set_style (GTK_WIDGET (fftunegui->canvas), style); gtk_widget_show (GTK_WIDGET (fftunegui->canvas)); /* create spectrum canvas item */ fftunegui->spectrum = gnome_canvas_item_new (gnome_canvas_root (fftunegui->canvas), SWAMIGUI_TYPE_SPECTRUM_CANVAS, "adjustment", gtk_range_get_adjustment (GTK_RANGE (fftunegui->hscrollbar)), NULL); /* create snap line */ fftunegui->snap_line = gnome_canvas_item_new (gnome_canvas_root (fftunegui->canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", fftunegui->snap_line_color, "width-pixels", 2, NULL); gnome_canvas_item_hide (fftunegui->snap_line); /* vertical scale for setting amplitude zoom */ fftunegui->vscale = gtk_vscale_new_with_range (1.0, 100.0, 0.5); gtk_scale_set_draw_value (GTK_SCALE (fftunegui->vscale), FALSE); gtk_range_set_inverted (GTK_RANGE (fftunegui->vscale), TRUE); adj = gtk_range_get_adjustment (GTK_RANGE (fftunegui->vscale)); g_signal_connect (adj, "value-changed", G_CALLBACK (fftune_gui_cb_ampl_zoom_value_changed), fftunegui); gtk_widget_show (fftunegui->vscale); gtk_box_pack_start (GTK_BOX (hbox), fftunegui->vscale, FALSE, FALSE, 0); } static void fftunegui_cb_revert_clicked (GtkButton *button, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); IpatchSample *sample; g_object_get (fftunegui->spectra, "sample", &sample, NULL); /* ++ ref */ if (!sample) return; g_object_set (sample, "root-note", fftunegui->orig_root_note, "fine-tune", fftunegui->orig_fine_tune, NULL); g_object_unref (sample); /* -- unref */ } static void fftunegui_cb_freq_list_sel_changed (GtkTreeSelection *selection, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); IpatchSample *sample; GtkTreeIter iter; char *notestr, *centstr; int note, finetune; if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) return; g_object_get (fftunegui->spectra, "sample", &sample, NULL); /* ++ ref */ if (!sample) return; gtk_tree_model_get (GTK_TREE_MODEL (fftunegui->freq_store), &iter, COL_NOTE, ¬estr, /* ++ alloc */ COL_CENTS, ¢str, /* ++ alloc */ -1); note = strtol (notestr, NULL, 10); finetune = -roundf (strtof (centstr, NULL)); /* Invert cents to get finetune adjustment */ g_object_set (sample, "root-note", note, "fine-tune", finetune, NULL); g_object_unref (sample); /* -- unref */ } static void fftune_gui_cb_spectrum_change (FFTuneSpectra *spectra, guint size, double *spectrum, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); int width, height; double zoom; swamigui_spectrum_canvas_set_data (SWAMIGUI_SPECTRUM_CANVAS (fftunegui->spectrum), spectrum, size, NULL); if (fftunegui->recalc_zoom) { g_object_get (fftunegui->spectrum, "width", &width, "height", &height, NULL); if (width == 0.0) zoom = 0.0; else zoom = size / (double)width; g_object_set (fftunegui->spectrum, "zoom", zoom, NULL); fftunegui->recalc_zoom = FALSE; } } static void fftune_gui_cb_tunings_change (FFTuneSpectra *spectra, guint count, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); GtkTreeIter iter; double power, max_power = 0.0, freq, cents; char powerstr[6], freqstr[32], notestr[11], centsstr[16]; int note; guint i; gtk_list_store_clear (fftunegui->freq_store); for (i = 0; i < count; i++) { /* select the current tuning index */ g_object_set (spectra, "tune-select", i, NULL); /* get frequency and power of the current tuning suggestion */ g_object_get (spectra, "tune-freq", &freq, "tune-power", &power, NULL); if (i == 0) max_power = power; /* first tuning is max power */ cents = ipatch_unit_hertz_to_cents (freq); note = (int)(cents / 100.0 + 0.5); cents -= note * 100; sprintf (powerstr, "%0.2f", power / max_power); sprintf (freqstr, "%0.2f", freq); sprintf (centsstr, "%0.2f", cents); if (note < 0) strcpy (notestr, "<0"); else if (note > 127) strcpy (notestr, ">127"); else { sprintf (notestr, "%d | ", note); swami_util_midi_note_to_str (note, notestr + strlen (notestr)); } gtk_list_store_append (fftunegui->freq_store, &iter); gtk_list_store_set (fftunegui->freq_store, &iter, COL_POWER, powerstr, COL_FREQ, freqstr, COL_NOTE, notestr, COL_CENTS, centsstr, -1); } } /* callback when sample mode combo box changes */ static void fftune_gui_cb_mode_menu_changed (GtkComboBox *combo, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); int active; active = gtk_combo_box_get_active (combo); if (active != -1) g_object_set (fftunegui->spectra, "sample-mode", active, NULL); } static void fftune_gui_cb_limit_changed (GtkComboBox *combo, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); int active; active = gtk_combo_box_get_active (combo); if (active != -1) g_object_set (fftunegui->spectra, "limit", limits[active].limit, NULL); } static void fftune_gui_cb_window_toggled (GtkToggleButton *button, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); g_object_set (fftunegui->spectra, "enable-window", gtk_toggle_button_get_active (button), NULL); } static void fftune_gui_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); GnomeCanvas *canvas = GNOME_CANVAS (widget); int width, height; width = GTK_WIDGET (canvas)->allocation.width; height = GTK_WIDGET (canvas)->allocation.height; /* update size of spectrum canvas item */ g_object_set (fftunegui->spectrum, "width", width, "height", height, NULL); } /* horizontal scroll bar value changed callback */ static void fftune_gui_cb_scroll (GtkAdjustment *adj, gpointer user_data) { // FFTuneGui *fftunegui = FFTUNE_GUI (user_data); // marker_control_update_all (fftunegui); /* update all markers */ } static gboolean fftune_gui_cb_spectrum_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data) { FFTuneGui *fftunegui = FFTUNE_GUI (data); GnomeCanvasPoints *points; // MarkerInfo *marker_info; GdkEventScroll *scroll_event; GdkEventButton *btn_event; GdkEventMotion *motion_event; double scale, zoom; int height; int ofs, val; guint32 timed = G_MAXUINT; switch (event->type) { case GDK_MOTION_NOTIFY: motion_event = (GdkEventMotion *)event; #if 0 if (fftunegui->sel_marker != -1) { marker_info = g_list_nth_data (fftunegui->markers, fftunegui->sel_marker); if (!marker_info) break; val = swamigui_spectrum_canvas_pos_to_sample (SWAMIGUI_SPECTRUM_CANVAS (track_info->sample_view), motion_event->x); g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, val); swami_control_transmit_value (marker_info->ctrl, &value); g_value_unset (&value); /* update the marker */ marker_info->sample_pos = val; marker_control_update (marker_info); break; } else /* no active marker selection, see if mouse cursor over marker */ { /* see if mouse is over a marker */ marker_info = fftune_gui_xpos_is_marker (fftunegui, motion_event->x); /* see if cursor should be changed */ if ((marker_info != NULL) != fftunegui->marker_cursor) { if (marker_info) cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); /* ++ ref */ else cursor = gdk_cursor_new (GDK_LEFT_PTR); gdk_window_set_cursor (GTK_WIDGET (fftunegui->canvas)->window, cursor); gdk_cursor_unref (cursor); fftunegui->marker_cursor = marker_info != NULL; } } #endif if (!fftunegui->snap_active) break; ofs = (int)(motion_event->x - fftunegui->snap_pos); val = ABS (ofs); if (val > SNAP_TIMEOUT_PIXEL_RANGE) val = SNAP_TIMEOUT_PIXEL_RANGE; if (ofs != 0) fftunegui->snap_interval = (SNAP_TIMEOUT_PIXEL_RANGE - val) * (SNAP_TIMEOUT_MAX - SNAP_TIMEOUT_MIN) / (SNAP_TIMEOUT_PIXEL_RANGE - 1) + SNAP_TIMEOUT_MIN; else fftunegui->snap_interval = 0; /* add a timeout callback for zoom/scroll if not already added */ if (!fftunegui->snap_timeout_handler && fftunegui->snap_interval > 0) { fftunegui->snap_timeout_handler = g_timeout_add_full (SNAP_TIMEOUT_PRIORITY, fftunegui->snap_interval, fftune_gui_snap_timeout, fftunegui, NULL); } if (motion_event->state & GDK_SHIFT_MASK) { fftunegui->scroll_active = TRUE; g_object_get (fftunegui->spectrum, "zoom", &zoom, NULL); if (ofs >= 0) val = SNAP_SCROLL_MIN; else val = -SNAP_SCROLL_MIN; fftunegui->scroll_amt = (int)(zoom * (ofs * SNAP_SCROLL_MULT + val)); } else fftunegui->scroll_active = FALSE; if (motion_event->state & GDK_CONTROL_MASK) { fftunegui->zoom_active = TRUE; if (ofs != 0) { fftunegui->zoom_amt = val * (SNAP_ZOOM_MAX - SNAP_ZOOM_MIN) / (SNAP_TIMEOUT_PIXEL_RANGE - 1) + SNAP_ZOOM_MIN; if (ofs < 0) fftunegui->zoom_amt = 1.0 / fftunegui->zoom_amt; } else fftunegui->zoom_amt = 1.0; } else fftunegui->zoom_active = FALSE; break; case GDK_BUTTON_PRESS: btn_event = (GdkEventButton *)event; /* make sure its button 1 */ if (btn_event->button != 1) break; #if 0 /* is it a marker click? */ marker_info = fftune_gui_xpos_is_marker (fftunegui, btn_event->x); if (marker_info) { fftunegui->sel_marker = g_list_index (fftunegui->markers, marker_info); break; } #endif if (!(btn_event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) break; fftunegui->snap_active = TRUE; fftunegui->snap_pos = (int)btn_event->x; height = GTK_WIDGET (fftunegui->canvas)->allocation.height; points = gnome_canvas_points_new (2); points->coords[0] = fftunegui->snap_pos; points->coords[1] = 0; points->coords[2] = fftunegui->snap_pos; points->coords[3] = height - 1; g_object_set (fftunegui->snap_line, "points", points, NULL); gnome_canvas_item_show (fftunegui->snap_line); gnome_canvas_points_free (points); break; case GDK_BUTTON_RELEASE: if (!fftunegui->snap_active) break; // if (!fftunegui->snap_active && fftunegui->sel_marker == -1) break; btn_event = (GdkEventButton *)event; if (btn_event->button != 1) break; // fftunegui->sel_marker = -1; fftunegui->snap_active = FALSE; if (fftunegui->snap_timeout_handler != 0) { g_source_remove (fftunegui->snap_timeout_handler); fftunegui->snap_timeout_handler = 0; } fftunegui->scroll_active = FALSE; fftunegui->zoom_active = FALSE; gnome_canvas_item_hide (fftunegui->snap_line); break; case GDK_SCROLL: /* mouse wheel zooming */ scroll_event = (GdkEventScroll *)event; if (scroll_event->direction != GDK_SCROLL_UP && scroll_event->direction != GDK_SCROLL_DOWN) break; if (scroll_event->direction == fftunegui->last_wheel_dir) timed = scroll_event->time - fftunegui->last_wheel_time; timed = CLAMP (timed, WHEEL_ZOOM_MIN_TIME, WHEEL_ZOOM_MAX_TIME); timed -= WHEEL_ZOOM_MIN_TIME; scale = WHEEL_ZOOM_MAX_SPEED + (double)timed / WHEEL_ZOOM_MAX_TIME * WHEEL_ZOOM_RANGE; if (scroll_event->direction == GDK_SCROLL_DOWN) scale = 1 / scale; fftune_gui_zoom_ofs (fftunegui, scale, (int)scroll_event->x); fftunegui->last_wheel_dir = scroll_event->direction; fftunegui->last_wheel_time = scroll_event->time; break; default: break; } return (FALSE); } static gboolean fftune_gui_snap_timeout (gpointer data) { FFTuneGui *fftunegui = FFTUNE_GUI (data); if (fftunegui->scroll_active && fftunegui->scroll_amt != 0) fftune_gui_scroll_ofs (fftunegui, fftunegui->scroll_amt); if (fftunegui->zoom_active && fftunegui->zoom_amt != 1.0) fftune_gui_zoom_ofs (fftunegui, fftunegui->zoom_amt, fftunegui->snap_pos); /* add timeout for next interval */ if (fftunegui->snap_interval > 0) { fftunegui->snap_timeout_handler = g_timeout_add_full (SNAP_TIMEOUT_PRIORITY, fftunegui->snap_interval, fftune_gui_snap_timeout, fftunegui, NULL); } else fftunegui->snap_timeout_handler = 0; return (FALSE); /* remove this timeout */ } static void fftune_gui_cb_ampl_zoom_value_changed (GtkAdjustment *adj, gpointer user_data) { FFTuneGui *fftunegui = FFTUNE_GUI (user_data); /* set vertical zoom */ g_object_set (fftunegui->spectrum, "zoom-ampl", adj->value, NULL); } #if 0 /* check if a given xpos is on a marker */ static MarkerInfo * fftune_gui_xpos_is_marker (FFTuneGui *fftunegui, int xpos) { MarkerInfo *marker_info, *match = NULL; SwamiguiSampleCanvas *sample_view; int x, ofs = -1; GList *p; if (!fftunegui->markers || !fftunegui->tracks) return (NULL); sample_view = SWAMIGUI_SPECTRUM_CANVAS (((TrackInfo *)(fftunegui->tracks->data))->sample_view); for (p = fftunegui->markers; p; p = g_list_next (p)) { marker_info = (MarkerInfo *)(p->data); x = swamigui_spectrum_canvas_sample_to_pos (sample_view, marker_info->sample_pos); if (x != -1) { x = ABS (xpos - x); if (x <= MARKER_CLICK_DISTANCE && (ofs == -1 || x < ofs)) { match = marker_info; ofs = x; } } } return (match); } #endif /* Zoom the spectrum canvas the specified offset amount and modify the * start index position to keep the given X coordinate stationary. */ static void fftune_gui_zoom_ofs (FFTuneGui *fftunegui, double zoom_amt, int zoom_xpos) { double zoom; guint spectrum_size; int start, width, index_ofs; g_return_if_fail (FFTUNE_IS_GUI (fftunegui)); /* get current values from spectrum canvas view */ g_object_get (fftunegui->spectrum, "zoom", &zoom, "start", &start, "width", &width, NULL); index_ofs = (int)(zoom_xpos * zoom); /* index to zoom xpos */ zoom *= zoom_amt; spectrum_size = SWAMIGUI_SPECTRUM_CANVAS (fftunegui->spectrum)->spectrum_size; /* do some bounds checking on the zoom value */ if (zoom < SPECTRUM_CANVAS_MIN_ZOOM) zoom = SPECTRUM_CANVAS_MIN_ZOOM; else if (width * zoom > spectrum_size) { /* view exceeds spectrum data */ start = 0; zoom = spectrum_size / (double)width; } else { index_ofs -= (int)(zoom_xpos * zoom); /* subtract new zoom offset */ if (index_ofs < 0 && -index_ofs > start) start = 0; else start += index_ofs; /* make sure spectrum doesn't end in the middle of the display */ if (start + width * zoom > spectrum_size) start = (int)(spectrum_size - width * zoom); } g_object_set (fftunegui->spectrum, "zoom", zoom, "start", start, NULL); } /* Scroll the spectrum canvas by a given offset. */ static void fftune_gui_scroll_ofs (FFTuneGui *fftunegui, int index_ofs) { double zoom; guint start_index; int newstart, width; guint spectrum_size; int last_index; g_return_if_fail (FFTUNE_IS_GUI (fftunegui)); if (index_ofs == 0) return; g_object_get (fftunegui->spectrum, "start", &start_index, "zoom", &zoom, "width", &width, NULL); spectrum_size = SWAMIGUI_SPECTRUM_CANVAS (fftunegui->spectrum)->spectrum_size; last_index = (int)(spectrum_size - zoom * width); if (last_index < 0) return; /* spectrum too small for current zoom? */ newstart = (int)start_index + index_ofs; newstart = CLAMP (newstart, 0, last_index); g_object_set (fftunegui->spectrum, "start", newstart, NULL); } swami-2.2.0/src/plugins/fftune_gui.def000066400000000000000000000000421361104770400177150ustar00rootroot00000000000000LIBRARY EXPORTS swami_plugin_info swami-2.2.0/src/plugins/fftune_gui.h000066400000000000000000000071001361104770400174100ustar00rootroot00000000000000/* * fftune_gui.h - FFTune GUI object (Fast Fourier sample tuning GUI) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __FFTUNE_GUI_H__ #define __FFTUNE_GUI_H__ #include #include #include typedef struct _FFTuneGui FFTuneGui; typedef struct _FFTuneGuiClass FFTuneGuiClass; /* Defined in fftune.h */ typedef struct _FFTuneSpectra FFTuneSpectra; #define FFTUNE_TYPE_GUI (fftune_gui_get_type ()) #define FFTUNE_GUI(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), FFTUNE_TYPE_GUI, FFTuneGui)) #define FFTUNE_GUI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), FFTUNE_TYPE_GUI, FFTuneGuiClass)) #define FFTUNE_IS_GUI(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FFTUNE_TYPE_GUI)) #define FFTUNE_IS_GUI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), FFTUNE_TYPE_GUI)) /* Sample view object */ struct _FFTuneGui { GtkVBox parent; /* derived from GtkVBox */ FFTuneSpectra *spectra; /* FFTuneSpectra object (fftune.[ch]) */ gboolean snap_active; /* set to TRUE when a snap zoom/scroll is active */ int snap_pos; /* xpos of zoom/scroll snap line */ guint snap_timeout_handler; /* snap timeout callback handler ID */ guint snap_interval; /* interval in milliseconds for timeout handler */ gboolean scroll_active; /* TRUE if SHIFT scrolling is active */ gboolean zoom_active; /* TRUE if CTRL zooming is active */ int scroll_amt; /* scroll amount for each timeout in samples */ double zoom_amt; /* zoom multiplier for each timeout */ /* for mouse wheel scrolling */ GdkScrollDirection last_wheel_dir; /* last wheel direction */ guint32 last_wheel_time; /* last time stamp of mouse wheel */ GnomeCanvas *canvas; /* canvas */ GnomeCanvasItem *spectrum; /* SwamiguiSpectrumCanvas item */ GnomeCanvasItem *snap_line; /* zoom/scroll snap line */ gboolean recalc_zoom; /* TRUE to recalc full zoom (after sample change) */ GtkWidget *mode_menu; /* data selection menu */ GtkWidget *hscrollbar; /* horizontal scrollbar widget */ GtkListStore *freq_store; /* store for freq tuning list */ GtkWidget *freq_list; /* GtkTreeView list of freq tuning suggestions */ GtkWidget *vscale; /* Vertical scale widget */ GtkWidget *root_notesel; /* root note selector widget */ GtkWidget *fine_tune; /* fine tune spin button */ GtkWidget *revert_button; /* Revert button */ SwamiControl *root_note_ctrl; /* root note Swami control */ SwamiControl *fine_tune_ctrl; /* fine tune Swami control */ guint8 orig_root_note; /* Original root note value */ guint8 orig_fine_tune; /* Original fine tune value */ guint snap_line_color; /* zoom/scroll snap line color */ }; /* Sample view object class */ struct _FFTuneGuiClass { GtkVBoxClass parent_class; }; GType fftune_gui_get_type (void); GtkWidget *fftune_gui_new (void); #endif swami-2.2.0/src/plugins/fftune_i18n.h000066400000000000000000000006461361104770400174130ustar00rootroot00000000000000#ifndef __SWAMIPLUGIN_FFTUNE_I18N_H__ #define __SWAMIPLUGIN_FFTUNE_I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) dgettext("SwamiPlugin-FFTune", x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif swami-2.2.0/src/plugins/fluidsynth.c000066400000000000000000002110511361104770400174430ustar00rootroot00000000000000/* * fluidsynth.c - Swami plugin for FluidSynth * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include #include #include #include "fluidsynth_i18n.h" /* max voices per instrument (voices exceeding this will not sound) */ #define MAX_INST_VOICES 128 /* maximum # of voices under real time control (voices exceeding this number just wont be controllable in real time, no fatal problems though) */ #define MAX_REALTIME_VOICES 64 /* maximum realtime effect parameter updates for a single property change */ #define MAX_REALTIME_UPDATES 128 /* default number of synth channels (FIXME - dynamic?) */ #define DEFAULT_CHANNEL_COUNT 16 /* max length of reverb/chorus preset names (including '\0') */ #define PRESET_NAME_LEN 21 typedef struct _WavetblFluidSynth WavetblFluidSynth; typedef struct _WavetblFluidSynthClass WavetblFluidSynthClass; #define WAVETBL_TYPE_FLUIDSYNTH (wavetbl_type) #define WAVETBL_FLUIDSYNTH(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), WAVETBL_TYPE_FLUIDSYNTH, \ WavetblFluidSynth)) #define WAVETBL_FLUIDSYNTH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), WAVETBL_TYPE_FLUIDSYNTH, \ WavetblFluidSynthClass)) #define WAVETBL_IS_FLUIDSYNTH(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WAVETBL_TYPE_FLUIDSYNTH)) #define WAVETBL_IS_FLUIDSYNTH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), WAVETBL_TYPE_FLUIDSYNTH)) typedef struct _realtime_noteon_t realtime_noteon_t; #define INTERPOLATION_TYPE (interp_mode_type) /* structure for storing reverb parameters */ typedef struct { char name[PRESET_NAME_LEN]; /* for presets */ double room_size; double damp; double width; double level; } ReverbParams; /* structure for storing chorus parameters */ typedef struct { char name[PRESET_NAME_LEN]; /* for presets */ int count; double level; double freq; double depth; int waveform; } ChorusParams; #define CHORUS_WAVEFORM_TYPE (chorus_waveform_type) /* FluidSynth SwamiWavetbl object */ struct _WavetblFluidSynth { SwamiWavetbl object; /* derived from SwamiWavetbl */ fluid_synth_t *synth; /* the FluidSynth handle */ fluid_settings_t *settings; /* to free on close */ fluid_audio_driver_t *audio; /* FluidSynth audio driver */ fluid_midi_driver_t *midi; /* FluidSynth MIDI driver */ fluid_midi_router_t *midi_router; /* FluidSynth MIDI router */ SwamiControlMidi *midi_ctrl; /* MIDI control */ guint prop_callback_handler_id; /* property change handler ID */ GSList *mods; /* session modulators */ int channel_count; /* number of MIDI channels */ guint8 *banks; /* bank numbers for each channel */ guint8 *programs; /* program numbers for each channel */ int interp; /* interpolation type */ gboolean reverb_update; /* TRUE if reverb needs update */ ReverbParams reverb_params; /* current reverb parameters */ gboolean chorus_update; /* TRUE if chorus needs update */ ChorusParams chorus_params; /* current chorus parameters */ /* active item is the focus, allow realtime control of most recent note of active item. */ IpatchItem *active_item; /* active audible instrument */ IpatchItem *solo_item; /* child of active item to solo or NULL */ IpatchSF2VoiceCache *rt_cache; /* voice cache of active_item */ int rt_sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; /* sel criteria of note */ fluid_voice_t *rt_voices[MAX_REALTIME_VOICES]; /* FluidSynth voices */ int rt_count; /* count for rt_index_array and rt_voices */ }; /* FluidSynth wavetbl class */ struct _WavetblFluidSynthClass { SwamiWavetblClass parent_class; /* derived from SwamiWavetblClass */ }; enum { WTBL_PROP_0, WTBL_PROP_INTERP, WTBL_PROP_REVERB_PRESET, WTBL_PROP_REVERB_ROOM_SIZE, WTBL_PROP_REVERB_DAMP, WTBL_PROP_REVERB_WIDTH, WTBL_PROP_REVERB_LEVEL, WTBL_PROP_CHORUS_PRESET, WTBL_PROP_CHORUS_COUNT, WTBL_PROP_CHORUS_LEVEL, WTBL_PROP_CHORUS_FREQ, WTBL_PROP_CHORUS_DEPTH, WTBL_PROP_CHORUS_WAVEFORM, WTBL_PROP_ACTIVE_ITEM, WTBL_PROP_SOLO_ITEM, WTBL_PROP_MODULATORS }; /* number to use for first dynamic (FluidSynth settings) property */ #define FIRST_DYNAMIC_PROP 256 /* FluidSynth MIDI event types (MIDI control codes) */ enum { WAVETBL_FLUID_NOTE_OFF = 0x80, WAVETBL_FLUID_NOTE_ON = 0x90, WAVETBL_FLUID_CONTROL_CHANGE = 0xb0, WAVETBL_FLUID_PROGRAM_CHANGE = 0xc0, WAVETBL_FLUID_PITCH_BEND = 0xe0 }; #define _SYNTH_OK 0 /* FLUID_OK not defined in header */ /* additional data for sfloader patch base objects */ typedef struct { WavetblFluidSynth *wavetbl; /* wavetable object */ IpatchBase *base_item; /* IpatchBase object */ } sfloader_sfont_data_t; typedef struct { WavetblFluidSynth *wavetbl; /* wavetable object */ IpatchItem *item; /* instrument item */ } sfloader_preset_data_t; /* FluidSynth property flags (for exceptions such as string booleans) */ typedef enum { PROP_STRING_BOOL = 1 << 0 } PropFlags; static gboolean plugin_fluidsynth_init (SwamiPlugin *plugin, GError **err); static gboolean plugin_fluidsynth_save_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err); static gboolean plugin_fluidsynth_load_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err); static GType wavetbl_register_type (SwamiPlugin *plugin); static GType interp_mode_register_type (SwamiPlugin *plugin); static GType chorus_waveform_register_type (SwamiPlugin *plugin); static void wavetbl_fluidsynth_class_init (WavetblFluidSynthClass *klass); static void settings_foreach_count (void *data, const char *name, int type); static void settings_foreach_option_count (void *data, const char *name, const char *option); static void settings_foreach_func (void *data, const char *name, int type); static void settings_foreach_option_func (void *data, const char *name, const char *option); static int cmpstringp (const void *p1, const void *p2); static void wavetbl_fluidsynth_init (WavetblFluidSynth *wavetbl); static void wavetbl_fluidsynth_finalize (GObject *object); static void wavetbl_fluidsynth_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void wavetbl_fluidsynth_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void wavetbl_fluidsynth_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs); static void wavetbl_fluidsynth_midi_ctrl_callback (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static gboolean wavetbl_fluidsynth_open (SwamiWavetbl *swami_wavetbl, GError **err); static void wavetbl_fluidsynth_prop_callback (IpatchItemPropNotify *notify); static int wavetbl_fluidsynth_handle_midi_event (void* data, fluid_midi_event_t* event); static void wavetbl_fluidsynth_close (SwamiWavetbl *swami_wavetbl); static SwamiControlMidi * wavetbl_fluidsynth_get_control (SwamiWavetbl *swami_wavetbl, int index); static gboolean wavetbl_fluidsynth_load_patch (SwamiWavetbl *swami_wavetbl, IpatchItem *patch, GError **err); static gboolean wavetbl_fluidsynth_load_active_item (SwamiWavetbl *swami_wavetbl, IpatchItem *item, GError **err); static gboolean wavetbl_fluidsynth_check_update_item (SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop); static void wavetbl_fluidsynth_update_item (SwamiWavetbl *wavetbl, IpatchItem *item); static void wavetbl_fluidsynth_update_reverb (WavetblFluidSynth *wavetbl); static int find_reverb_preset (const char *name); static void wavetbl_fluidsynth_update_chorus (WavetblFluidSynth *wavetbl); static int find_chorus_preset (const char *name); static fluid_sfont_t *sfloader_load_sfont (fluid_sfloader_t *loader, const char *filename); static int sfloader_sfont_free (fluid_sfont_t *sfont); static const char *sfloader_sfont_get_name (fluid_sfont_t *sfont); static fluid_preset_t *sfloader_sfont_get_preset (fluid_sfont_t *sfont, int bank, int prenum); static void sfloader_preset_free (fluid_preset_t *preset); static void sfloader_active_preset_free (fluid_preset_t *preset); static const char *sfloader_preset_get_name (fluid_preset_t *preset); static const char *sfloader_active_preset_get_name (fluid_preset_t *preset); static int sfloader_preset_get_banknum (fluid_preset_t *preset); static int sfloader_active_preset_get_banknum (fluid_preset_t *preset); static int sfloader_preset_get_num (fluid_preset_t *preset); static int sfloader_active_preset_get_num (fluid_preset_t *preset); static int sfloader_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); static int sfloader_active_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); static void cache_instrument (WavetblFluidSynth *wavetbl, IpatchItem *item); static int cache_instrument_noteon (WavetblFluidSynth *wavetbl, IpatchItem *item, fluid_synth_t *synth, int chan, int key, int vel); static void active_item_realtime_update (WavetblFluidSynth *wavetbl, IpatchItem *item, GParamSpec *pspec, const GValue *value); /* FluidSynth settings boolean exceptions (yes/no string values) */ static const char *settings_str_bool[] = { "audio.jack.multi", "synth.chorus.active", "synth.dump", "synth.ladspa.active", "synth.reverb.active", "synth.verbose", NULL }; /* types are defined as global vars */ static GType wavetbl_type = 0; static GType interp_mode_type = 0; static GType chorus_waveform_type = 0; static GObjectClass *wavetbl_parent_class = NULL; /* last dynamic property ID (incremented for each dynamically installed prop) */ static guint last_property_id = FIRST_DYNAMIC_PROP; /* stores all dynamic FluidSynth setting names for mapping between property ID and FluidSynth setting */ static char **dynamic_prop_names; /* stores PropFlags for property exceptions (string booleans, etc) */ static guint8 *dynamic_prop_flags; /* Quark key used for assigning FluidSynth options string arrays to GParamSpecs */ GQuark wavetbl_fluidsynth_options_quark; /* keeps a hash of patch objects to SF2VoiceCache objects */ G_LOCK_DEFINE_STATIC (voice_cache_hash); static GHashTable *voice_cache_hash = NULL; /* Reverb and Chorus preset tables (index 0 contains default values) */ G_LOCK_DEFINE_STATIC (preset_tables); /* lock for reverb and chorus tables */ static ReverbParams *reverb_presets = NULL; static ChorusParams *chorus_presets = NULL; static int reverb_presets_count; /* count of reverb presets */ static int chorus_presets_count; /* count of chorus presets */ /* count of built in reverb and chorus presets */ #define REVERB_PRESETS_BUILTIN 1 #define CHORUS_PRESETS_BUILTIN 1 /* set plugin information */ SWAMI_PLUGIN_INFO (plugin_fluidsynth_init, NULL); /* --- functions --- */ /* plugin init function (one time initialize of SwamiPlugin) */ static gboolean plugin_fluidsynth_init (SwamiPlugin *plugin, GError **err) { fluid_settings_t *settings = NULL; int idef; double ddef; /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain ("SwamiPlugin-fluidsynth", LOCALEDIR); #endif plugin->save_xml = plugin_fluidsynth_save_xml; plugin->load_xml = plugin_fluidsynth_load_xml; g_object_set (plugin, "name", "FluidSynth", "version", "1.1", "author", "Element Green", "copyright", "Copyright (C) 2002-2014", "descr", N_("FluidSynth software wavetable synth plugin"), "license", "GPL", NULL); /* initialize voice cache hash */ voice_cache_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); /* initialize built-in reverb and chorus presets * !! Make sure that name field ends in NULLs (strncpy does this). * !! If not, then a potential multi-thread string crash could occur. */ reverb_presets = g_new (ReverbParams, REVERB_PRESETS_BUILTIN); chorus_presets = g_new (ChorusParams, CHORUS_PRESETS_BUILTIN); settings = new_fluid_settings(); if(voice_cache_hash == NULL || reverb_presets == NULL || chorus_presets == NULL || settings == NULL) { g_hash_table_unref(voice_cache_hash); voice_cache_hash = NULL; g_free(reverb_presets); reverb_presets = NULL; g_free(chorus_presets); chorus_presets = NULL; delete_fluid_settings(settings); return FALSE; } strncpy (reverb_presets[0].name, N_("Default"), PRESET_NAME_LEN); fluid_settings_getnum_default(settings, "synth.reverb.room-size", &ddef); reverb_presets[0].room_size = ddef; fluid_settings_getnum_default(settings, "synth.reverb.damp", &ddef); reverb_presets[0].damp = ddef; fluid_settings_getnum_default(settings, "synth.reverb.width", &ddef); reverb_presets[0].width = ddef; fluid_settings_getnum_default(settings, "synth.reverb.level", &ddef); reverb_presets[0].level = ddef; strncpy (chorus_presets[0].name, N_("Default"), PRESET_NAME_LEN); fluid_settings_getint_default(settings, "synth.chorus.nr", &idef); chorus_presets[0].count = idef; fluid_settings_getnum_default(settings, "synth.chorus.level", &ddef); chorus_presets[0].level = ddef; fluid_settings_getnum_default(settings, "synth.chorus.speed", &ddef); chorus_presets[0].freq = ddef; fluid_settings_getnum_default(settings, "synth.chorus.depth", &ddef); chorus_presets[0].depth = ddef; chorus_presets[0].waveform = FLUID_CHORUS_MOD_SINE; delete_fluid_settings(settings); /* initialize types */ wavetbl_type = wavetbl_register_type (plugin); interp_mode_type = interp_mode_register_type (plugin); chorus_waveform_type = chorus_waveform_register_type (plugin); return (TRUE); } static gboolean plugin_fluidsynth_save_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err) { WavetblFluidSynth *wavetbl; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); if (!swamigui_root || !swamigui_root->wavetbl || !WAVETBL_IS_FLUIDSYNTH (swamigui_root->wavetbl)) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_FAIL, "Failure saving FluidSynth preferences: No FluidSynth object"); return (FALSE); } wavetbl = WAVETBL_FLUIDSYNTH (swamigui_root->wavetbl); return (ipatch_xml_encode_object (xmlnode, G_OBJECT (wavetbl), FALSE, err)); } /* load fluidsynth preference from xml file. Warning: current locale should be already set (by calling setlocale(LC_ALL, "")). This will ensure that when loading float numbers, decimal part values are properly decoded accordling to the LC_NUMERIC separator. Otherwise there is risk that decimal part will be ignored, leading in previous float preferences being read as integer value. For example, let chorus speed preference saved as 0,30 in preferences.xml file. At the next invocation of swami this value is loaded and decoded (using sscanf). If the current locale isn't set, sscanf will expect a point separator which lead to ignore the decimal part, chorus speed value will be decoded as 0,0 instead of 0,30. */ static gboolean plugin_fluidsynth_load_xml (SwamiPlugin *plugin, GNode *xmlnode, GError **err) { WavetblFluidSynth *wavetbl; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); if (!swamigui_root || !swamigui_root->wavetbl || !WAVETBL_IS_FLUIDSYNTH (swamigui_root->wavetbl)) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_FAIL, "Failure loading FluidSynth preferences: No FluidSynth object"); return (FALSE); } wavetbl = WAVETBL_FLUIDSYNTH (swamigui_root->wavetbl); return (ipatch_xml_decode_object (xmlnode, G_OBJECT (wavetbl), err)); } static GType wavetbl_register_type (SwamiPlugin *plugin) { static const GTypeInfo obj_info = { sizeof (WavetblFluidSynthClass), NULL, NULL, (GClassInitFunc) wavetbl_fluidsynth_class_init, NULL, NULL, sizeof (WavetblFluidSynth), 0, (GInstanceInitFunc) wavetbl_fluidsynth_init, }; return (g_type_module_register_type (G_TYPE_MODULE (plugin), SWAMI_TYPE_WAVETBL, "WavetblFluidSynth", &obj_info, 0)); } static GType interp_mode_register_type (SwamiPlugin *plugin) { static const GEnumValue values[] = { { FLUID_INTERP_NONE, "WAVETBL_FLUIDSYNTH_INTERP_NONE", "None" }, { FLUID_INTERP_LINEAR, "WAVETBL_FLUIDSYNTH_INTERP_LINEAR", "Linear" }, { FLUID_INTERP_4THORDER, "WAVETBL_FLUIDSYNTH_INTERP_4THORDER", "4th Order" }, { FLUID_INTERP_7THORDER, "WAVETBL_FLUIDSYNTH_INTERP_7THORDER", "7th Order" }, { 0, NULL, NULL } }; static GTypeInfo enum_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }; /* initialize the type info structure for the enum type */ g_enum_complete_type_info (G_TYPE_ENUM, &enum_info, values); return (g_type_module_register_type (G_TYPE_MODULE (plugin), G_TYPE_ENUM, "WavetblFluidSynthInterpType", &enum_info, 0)); } static GType chorus_waveform_register_type (SwamiPlugin *plugin) { static const GEnumValue values[] = { { FLUID_CHORUS_MOD_SINE, "WAVETBL_FLUID_CHORUS_MOD_SINE", "Sine" }, { FLUID_CHORUS_MOD_TRIANGLE, "WAVETBL_FLUID_CHORUS_MOD_TRIANGLE", "Triangle" }, { 0, NULL, NULL } }; static GTypeInfo enum_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }; /* initialize the type info structure for the enum type */ g_enum_complete_type_info (G_TYPE_ENUM, &enum_info, values); return (g_type_module_register_type (G_TYPE_MODULE (plugin), G_TYPE_ENUM, "WavetblFluidSynthChorusWaveform", &enum_info, 0)); } /* used for passing multiple values to FluidSynth foreach function */ typedef struct { fluid_settings_t *settings; GObjectClass *klass; int count; } ForeachBag; static void wavetbl_fluidsynth_class_init (WavetblFluidSynthClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); SwamiWavetblClass *wavetbl_class; ForeachBag bag; wavetbl_parent_class = g_type_class_peek_parent (klass); obj_class->finalize = wavetbl_fluidsynth_finalize; obj_class->set_property = wavetbl_fluidsynth_set_property; obj_class->get_property = wavetbl_fluidsynth_get_property; obj_class->dispatch_properties_changed = wavetbl_fluidsynth_dispatch_properties_changed; wavetbl_class = SWAMI_WAVETBL_CLASS (klass); wavetbl_class->open = wavetbl_fluidsynth_open; wavetbl_class->close = wavetbl_fluidsynth_close; wavetbl_class->get_control = wavetbl_fluidsynth_get_control; wavetbl_class->load_patch = wavetbl_fluidsynth_load_patch; wavetbl_class->load_active_item = wavetbl_fluidsynth_load_active_item; wavetbl_class->check_update_item = wavetbl_fluidsynth_check_update_item; wavetbl_class->update_item = wavetbl_fluidsynth_update_item; wavetbl_fluidsynth_options_quark = g_quark_from_static_string ("FluidSynth-options"); /* used for dynamically installing settings (required for settings queries) */ bag.settings = new_fluid_settings (); bag.klass = obj_class; bag.count = 0; /* count the number of FluidSynth properties + options properties */ fluid_settings_foreach (bag.settings, &bag, settings_foreach_count); /* have to keep a mapping of property IDs to FluidSynth setting names, since GObject properties get mangled ('.' turns to '-') */ dynamic_prop_names = g_malloc (bag.count * sizeof (char *)); /* allocate array for property exception flags */ dynamic_prop_flags = g_malloc0 (bag.count * sizeof (guint8)); /* add all FluidSynth settings as class properties */ fluid_settings_foreach (bag.settings, &bag, settings_foreach_func); delete_fluid_settings (bag.settings); /* not needed anymore */ g_object_class_install_property (obj_class, WTBL_PROP_INTERP, g_param_spec_enum ("interp", _("Interpolation"), _("Interpolation type"), INTERPOLATION_TYPE, FLUID_INTERP_DEFAULT, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_REVERB_PRESET, g_param_spec_string ("reverb-preset", _("Reverb preset"), _("Reverb preset"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_REVERB_ROOM_SIZE, g_param_spec_double ("reverb-room-size", _("Reverb room size"), _("Reverb room size"), 0.0, 1.0, reverb_presets[0].room_size, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_REVERB_DAMP, g_param_spec_double ("reverb-damp", _("Reverb damp"), _("Reverb damp"), 0.0, 1.0, reverb_presets[0].damp, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_REVERB_WIDTH, g_param_spec_double ("reverb-width", _("Reverb width"), _("Reverb width"), 0.0, 100.0, reverb_presets[0].width, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_REVERB_LEVEL, g_param_spec_double ("reverb-level", _("Reverb level"), _("Reverb level"), 0.0, 1.0, reverb_presets[0].level, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_PRESET, g_param_spec_string ("chorus-preset", _("Chorus preset"), _("Chorus preset"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_COUNT, g_param_spec_int ("chorus-count", _("Chorus count"), _("Number of chorus delay lines"), 1, 99, chorus_presets[0].count, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_LEVEL, g_param_spec_double ("chorus-level", _("Chorus level"), _("Output level of each chorus line"), 0.0, 10.0, chorus_presets[0].level, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_FREQ, g_param_spec_double ("chorus-freq", _("Chorus freq"), _("Chorus modulation frequency (Hz)"), 0.3, 5.0, chorus_presets[0].freq, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_DEPTH, g_param_spec_double ("chorus-depth", _("Chorus depth"), _("Chorus depth"), 0.0, 20.0, chorus_presets[0].depth, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_CHORUS_WAVEFORM, g_param_spec_enum ("chorus-waveform", _("Chorus waveform"), _("Chorus waveform type"), CHORUS_WAVEFORM_TYPE, FLUID_CHORUS_MOD_SINE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, WTBL_PROP_ACTIVE_ITEM, g_param_spec_object ("active-item", _("Active item"), _("Active focused audible item"), IPATCH_TYPE_ITEM, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, WTBL_PROP_SOLO_ITEM, g_param_spec_object ("solo-item", _("Solo item"), _("Child of active item to solo"), IPATCH_TYPE_ITEM, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, WTBL_PROP_MODULATORS, g_param_spec_boxed ("modulators", _("Modulators"), _("Modulators"), IPATCH_TYPE_SF2_MOD_LIST, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); } /* for counting the number of FluidSynth settings properties */ static void settings_foreach_count (void *data, const char *name, int type) { ForeachBag *bag = (ForeachBag *)data; int optcount = 0; bag->count = bag->count + 1; /* if there are string options, add one for an "-options" property */ if (type == FLUID_STR_TYPE) { fluid_settings_foreach_option (bag->settings, name, &optcount, settings_foreach_option_count); if (optcount > 0) bag->count = bag->count + 1; } } /* function to count FluidSynth string options for a parameter */ static void settings_foreach_option_count (void *data, const char *name, const char *option) { int *optcount = data; *optcount = *optcount + 1; } /* add each FluidSynth setting as a GObject property */ static void settings_foreach_func (void *data, const char *name, int type) { ForeachBag *bag = (ForeachBag *)data; GStrv options = NULL, optionp; GParamSpec *spec; double dmin, dmax, ddef; int imin, imax, idef, hint; gboolean bdef; char *defstr = NULL; const char **sp; int optcount = 0; char *optname; /* check if this property is on the string boolean list */ for (sp = settings_str_bool; *sp; sp++) if (type == FLUID_STR_TYPE && strcmp (name, *sp) == 0) break; if (*sp) /* string boolean value? */ { bdef = fluid_settings_str_equal (bag->settings, name, "yes"); spec = g_param_spec_boolean (name, name, name, bdef, G_PARAM_READWRITE); /* set PROP_STRING_BOOL property flag */ dynamic_prop_flags[last_property_id - FIRST_DYNAMIC_PROP] |= PROP_STRING_BOOL; } else { switch (type) { case FLUID_NUM_TYPE: fluid_settings_getnum_range (bag->settings, name, &dmin, &dmax); fluid_settings_getnum_default (bag->settings, name, &ddef); spec = g_param_spec_double (name, name, name, dmin, dmax, ddef, G_PARAM_READWRITE); break; case FLUID_INT_TYPE: fluid_settings_getint_range (bag->settings, name, &imin, &imax); fluid_settings_getint_default (bag->settings, name, &idef); fluid_settings_get_hints(bag->settings, name, &hint); if ((hint & FLUID_HINT_TOGGLED) != 0) /* boolean parameter? */ spec = g_param_spec_boolean (name, name, name, idef != 0, G_PARAM_READWRITE); else spec = g_param_spec_int (name, name, name, imin, imax, idef, G_PARAM_READWRITE); break; case FLUID_STR_TYPE: fluid_settings_getstr_default (bag->settings, name, &defstr); spec = g_param_spec_string (name, name, name, defstr, G_PARAM_READWRITE); /* count options for this string parameter (if any) */ fluid_settings_foreach_option (bag->settings, name, &optcount, settings_foreach_option_count); /* if there are any options, create a string array and assign them */ if (optcount > 0) { options = g_new (char *, optcount + 1); /* ++ alloc string array */ optionp = options; fluid_settings_foreach_option (bag->settings, name, &optionp, settings_foreach_option_func); options[optcount] = NULL; /* Sort the options alphabetically */ qsort (options, optcount, sizeof (options[0]), cmpstringp); } break; case FLUID_SET_TYPE: g_warning ("Enum not handled for property '%s'", name); return; default: return; } } /* install the property */ g_object_class_install_property (bag->klass, last_property_id, spec); /* store the name to the property name array */ dynamic_prop_names[last_property_id - FIRST_DYNAMIC_PROP] = g_strdup (name); last_property_id++; /* advance the last dynamic property ID */ /* install an options parameter if there are any string options */ if (options) { optname = g_strconcat (name, "-options", NULL); /* ++ alloc */ spec = g_param_spec_boxed (optname, optname, optname, G_TYPE_STRV, G_PARAM_READABLE); /* !! GParamSpec takes over allocation of options array */ g_param_spec_set_qdata (spec, wavetbl_fluidsynth_options_quark, options); g_object_class_install_property (bag->klass, last_property_id, spec); /* !! takes over allocation */ dynamic_prop_names[last_property_id - FIRST_DYNAMIC_PROP] = optname; last_property_id++; /* advance the last dynamic property ID */ } } /* function to iterate over FluidSynth string options for string parameters * and fill a string array */ static void settings_foreach_option_func (void *data, const char *name, const char *option) { GStrv *poptions = data; **poptions = g_strdup (option); *poptions = *poptions + 1; } /* qsort function to sort array of option strings */ static int cmpstringp (const void *p1, const void *p2) { return strcmp (*(char * const *)p1, *(char * const *)p2); } static void wavetbl_fluidsynth_init (WavetblFluidSynth *wavetbl) { wavetbl->synth = NULL; wavetbl->settings = new_fluid_settings (); wavetbl->midi_ctrl = swami_control_midi_new (); /* ++ ref */ swami_control_midi_set_callback (wavetbl->midi_ctrl, wavetbl_fluidsynth_midi_ctrl_callback, wavetbl); wavetbl->channel_count = DEFAULT_CHANNEL_COUNT; wavetbl->banks = g_new0 (guint8, wavetbl->channel_count); wavetbl->programs = g_new0 (guint8, wavetbl->channel_count); wavetbl->reverb_params = reverb_presets[0]; wavetbl->chorus_params = chorus_presets[0]; wavetbl->active_item = NULL; wavetbl->rt_cache = NULL; wavetbl->rt_count = 0; } static void wavetbl_fluidsynth_finalize (GObject *object) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (object); g_free (wavetbl->banks); g_free (wavetbl->programs); if (wavetbl->midi_ctrl) g_object_unref (wavetbl->midi_ctrl); /* -- unref */ if (wavetbl->settings) delete_fluid_settings (wavetbl->settings); G_OBJECT_CLASS (wavetbl_parent_class)->finalize (object); } static void wavetbl_fluidsynth_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (object); GSList *oldmods, *newmods; IpatchItem *item, *active_item; char *name; const char *s; int retval; int index; /* is it one of the dynamically installed properties? */ if (property_id >= FIRST_DYNAMIC_PROP && property_id < last_property_id) { name = dynamic_prop_names[property_id - FIRST_DYNAMIC_PROP]; switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) { case G_TYPE_INT: retval = fluid_settings_setint (wavetbl->settings, name, g_value_get_int (value)); break; case G_TYPE_DOUBLE: retval = fluid_settings_setnum (wavetbl->settings, name, g_value_get_double (value)); break; case G_TYPE_STRING: retval = fluid_settings_setstr (wavetbl->settings, name, (char *)g_value_get_string (value)); break; case G_TYPE_BOOLEAN: /* check if its a string boolean property */ if (dynamic_prop_flags[property_id - FIRST_DYNAMIC_PROP] & PROP_STRING_BOOL) retval = fluid_settings_setstr (wavetbl->settings, name, g_value_get_boolean (value) ? "yes" : "no"); else retval = fluid_settings_setint (wavetbl->settings, name, g_value_get_boolean (value)); break; default: g_critical ("Unexpected FluidSynth dynamic property type"); return; } if (retval == FLUID_FAILED) g_critical ("Failed to set FluidSynth property '%s'", name); return; } switch (property_id) { case WTBL_PROP_INTERP: wavetbl->interp = g_value_get_enum (value); SWAMI_LOCK_WRITE (wavetbl); if (wavetbl->synth) fluid_synth_set_interp_method (wavetbl->synth, -1, wavetbl->interp); SWAMI_UNLOCK_WRITE (wavetbl); break; case WTBL_PROP_REVERB_PRESET: s = g_value_get_string (value); index = 0; if (s && s[0] != '\0') /* valid preset name? */ { G_LOCK (preset_tables); index = find_reverb_preset (s); if (index != 0) wavetbl->reverb_params = reverb_presets[index]; G_UNLOCK (preset_tables); } if (index == 0) wavetbl->reverb_params = reverb_presets[0]; wavetbl->reverb_update = TRUE; break; case WTBL_PROP_REVERB_ROOM_SIZE: wavetbl->reverb_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->reverb_params.room_size = g_value_get_double (value); wavetbl->reverb_update = TRUE; break; case WTBL_PROP_REVERB_DAMP: wavetbl->reverb_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->reverb_params.damp = g_value_get_double (value); wavetbl->reverb_update = TRUE; break; case WTBL_PROP_REVERB_WIDTH: wavetbl->reverb_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->reverb_params.width = g_value_get_double (value); wavetbl->reverb_update = TRUE; break; case WTBL_PROP_REVERB_LEVEL: wavetbl->reverb_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->reverb_params.level = g_value_get_double (value); wavetbl->reverb_update = TRUE; break; case WTBL_PROP_CHORUS_PRESET: s = g_value_get_string (value); index = 0; if (s && s[0] != '\0') /* valid preset name? */ { G_LOCK (preset_tables); index = find_chorus_preset (s); if (index != 0) wavetbl->chorus_params = chorus_presets[index]; G_UNLOCK (preset_tables); } if (index == 0) wavetbl->chorus_params = chorus_presets[0]; wavetbl->chorus_update = TRUE; break; case WTBL_PROP_CHORUS_COUNT: wavetbl->chorus_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->chorus_params.count = g_value_get_int (value); wavetbl->chorus_update = TRUE; break; case WTBL_PROP_CHORUS_LEVEL: wavetbl->chorus_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->chorus_params.level = g_value_get_double (value); wavetbl->chorus_update = TRUE; break; case WTBL_PROP_CHORUS_FREQ: wavetbl->chorus_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->chorus_params.freq = g_value_get_double (value); wavetbl->chorus_update = TRUE; break; case WTBL_PROP_CHORUS_DEPTH: wavetbl->chorus_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->chorus_params.depth = g_value_get_double (value); wavetbl->chorus_update = TRUE; break; case WTBL_PROP_CHORUS_WAVEFORM: wavetbl->chorus_params.name[0] = '\0'; /* clear preset name (if any) */ wavetbl->chorus_params.waveform = g_value_get_enum (value); wavetbl->chorus_update = TRUE; break; case WTBL_PROP_ACTIVE_ITEM: item = g_value_get_object (value); SWAMI_LOCK_WRITE (wavetbl); wavetbl_fluidsynth_load_active_item ((SwamiWavetbl *)wavetbl, item, NULL); SWAMI_UNLOCK_WRITE (wavetbl); break; case WTBL_PROP_SOLO_ITEM: item = g_value_get_object (value); SWAMI_LOCK_WRITE (wavetbl); if (wavetbl->solo_item) g_object_unref (wavetbl->solo_item); wavetbl->solo_item = g_value_dup_object (value); active_item = g_object_ref (wavetbl->active_item); /* ++ ref active item */ SWAMI_UNLOCK_WRITE (wavetbl); wavetbl_fluidsynth_update_item ((SwamiWavetbl *)wavetbl, active_item); g_object_unref (active_item); /* -- unref active item */ break; case WTBL_PROP_MODULATORS: newmods = g_value_dup_boxed (value); SWAMI_LOCK_WRITE (wavetbl); oldmods = wavetbl->mods; wavetbl->mods = newmods; SWAMI_UNLOCK_WRITE (wavetbl); if (oldmods) ipatch_sf2_mod_list_free (oldmods, TRUE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wavetbl_fluidsynth_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (object); GSList *mods; char s[256]; char *name; double d; int retval; int i; GStrv strv; /* is it one of the dynamically installed properties? */ if (property_id >= FIRST_DYNAMIC_PROP && property_id < last_property_id) { name = dynamic_prop_names[property_id - FIRST_DYNAMIC_PROP]; switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) { case G_TYPE_INT: retval = fluid_settings_getint (wavetbl->settings, name, &i); if (retval != FLUID_FAILED) g_value_set_int (value, i); break; case G_TYPE_DOUBLE: retval = fluid_settings_getnum (wavetbl->settings, name, &d); if (retval != FLUID_FAILED) g_value_set_double (value, d); break; case G_TYPE_STRING: retval = fluid_settings_copystr (wavetbl->settings, name, s, sizeof(s)); if (retval != FLUID_FAILED) g_value_set_string (value, s); break; case G_TYPE_BOOLEAN: /* check if its a string boolean property */ if (dynamic_prop_flags[property_id - FIRST_DYNAMIC_PROP] & PROP_STRING_BOOL) { i = fluid_settings_str_equal (wavetbl->settings, name, "yes"); g_value_set_boolean (value, i); retval = FLUID_OK; } else { retval = fluid_settings_getint (wavetbl->settings, name, &i); if (retval != FLUID_FAILED) g_value_set_boolean (value, i); } break; default: /* For FluidSynth string -options parameters */ if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_STRV) { strv = g_param_spec_get_qdata (pspec, wavetbl_fluidsynth_options_quark); g_value_set_boxed (value, strv); retval = FLUID_OK; } else { g_critical ("Unexpected FluidSynth dynamic property type"); return; } } if (retval == FLUID_FAILED) g_critical ("Failed to get FluidSynth property '%s'", name); return; } switch (property_id) { case WTBL_PROP_INTERP: g_value_set_enum (value, wavetbl->interp); break; case WTBL_PROP_REVERB_PRESET: /* don't need to lock since buffer is static and will always contain a * NULL terminator (a partially updated string could occur though). */ g_value_set_string (value, wavetbl->reverb_params.name); break; case WTBL_PROP_REVERB_ROOM_SIZE: g_value_set_double (value, wavetbl->reverb_params.room_size); break; case WTBL_PROP_REVERB_DAMP: g_value_set_double (value, wavetbl->reverb_params.damp); break; case WTBL_PROP_REVERB_WIDTH: g_value_set_double (value, wavetbl->reverb_params.width); break; case WTBL_PROP_REVERB_LEVEL: g_value_set_double (value, wavetbl->reverb_params.level); break; case WTBL_PROP_CHORUS_PRESET: /* don't need to lock since buffer is static and will always contain a * NULL terminator (a partially updated string could occur though). */ g_value_set_string (value, wavetbl->chorus_params.name); break; case WTBL_PROP_CHORUS_COUNT: g_value_set_int (value, wavetbl->chorus_params.count); break; case WTBL_PROP_CHORUS_LEVEL: g_value_set_double (value, wavetbl->chorus_params.level); break; case WTBL_PROP_CHORUS_FREQ: g_value_set_double (value, wavetbl->chorus_params.freq); break; case WTBL_PROP_CHORUS_DEPTH: g_value_set_double (value, wavetbl->chorus_params.depth); break; case WTBL_PROP_CHORUS_WAVEFORM: g_value_set_enum (value, wavetbl->chorus_params.waveform); break; case WTBL_PROP_ACTIVE_ITEM: SWAMI_LOCK_READ (wavetbl); g_value_set_object (value, wavetbl->active_item); SWAMI_UNLOCK_READ (wavetbl); break; case WTBL_PROP_SOLO_ITEM: SWAMI_LOCK_READ (wavetbl); g_value_set_object (value, wavetbl->solo_item); SWAMI_UNLOCK_READ (wavetbl); break; case WTBL_PROP_MODULATORS: SWAMI_LOCK_READ (wavetbl); mods = ipatch_sf2_mod_list_duplicate (wavetbl->mods); SWAMI_UNLOCK_READ (wavetbl); g_value_take_boxed (value, mods); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* used to group reverb and/or chorus property updates, when changing multiple * properties, to prevent excess calculation */ static void wavetbl_fluidsynth_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (object); if (wavetbl->reverb_update || wavetbl->chorus_update) { SWAMI_LOCK_WRITE (wavetbl); if (wavetbl->reverb_update) wavetbl_fluidsynth_update_reverb (wavetbl); if (wavetbl->chorus_update) wavetbl_fluidsynth_update_chorus (wavetbl); SWAMI_UNLOCK_WRITE (wavetbl); } G_OBJECT_CLASS (wavetbl_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); } /* MIDI control callback */ static void wavetbl_fluidsynth_midi_ctrl_callback (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (((SwamiControlFunc *)control)->user_data); fluid_synth_t *synth; GArray *valarray = NULL; SwamiMidiEvent *midi; int i, count = 1; /* default for single values */ if (!wavetbl->synth) return; synth = wavetbl->synth; /* if its multiple values, fetch the value array */ if (G_VALUE_TYPE (value) == G_TYPE_ARRAY) { valarray = g_value_get_boxed (value); count = valarray->len; } i = 0; while (i < count) { if (valarray) value = &g_array_index (valarray, GValue, i); if (G_VALUE_TYPE (value) == SWAMI_TYPE_MIDI_EVENT && (midi = g_value_get_boxed (value))) { switch (midi->type) { case SWAMI_MIDI_NOTE_ON: fluid_synth_noteon (synth, midi->channel, midi->data.note.note, midi->data.note.velocity); break; case SWAMI_MIDI_NOTE_OFF: fluid_synth_noteoff (synth, midi->channel, midi->data.note.note); break; case SWAMI_MIDI_PITCH_BEND: /* FluidSynth uses 0-16383 */ fluid_synth_pitch_bend (synth, midi->channel, midi->data.control.value + 8192); break; case SWAMI_MIDI_CONTROL: fluid_synth_cc (synth, midi->channel, midi->data.control.param, midi->data.control.value); break; case SWAMI_MIDI_CONTROL14: if (midi->data.control.param == SWAMI_MIDI_CC_BANK_MSB) { /* update channel bank # */ if (midi->channel < wavetbl->channel_count) wavetbl->banks[midi->channel] = midi->data.control.value; fluid_synth_bank_select (synth, midi->channel, midi->data.control.value); } else fluid_synth_cc (synth, midi->channel, midi->data.control.param, midi->data.control.value); break; case SWAMI_MIDI_PROGRAM_CHANGE: /* update channel program # */ if (midi->channel < wavetbl->channel_count) wavetbl->programs[midi->channel] = midi->data.control.value; fluid_synth_program_change (synth, midi->channel, midi->data.control.value); break; default: break; } } i++; } } /** init function for FluidSynth Swami wavetable driver */ static gboolean wavetbl_fluidsynth_open (SwamiWavetbl *swami_wavetbl, GError **err) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); fluid_sfloader_t *loader; int i; SWAMI_LOCK_WRITE (wavetbl); if (swami_wavetbl->active) { SWAMI_UNLOCK_WRITE (wavetbl); return (TRUE); } /* create new FluidSynth */ wavetbl->synth = new_fluid_synth (wavetbl->settings); if (!wavetbl->synth) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_FAIL, _("Failed to create FluidSynth context")); SWAMI_UNLOCK_WRITE (wavetbl); return (FALSE); } /* hook our sfloader */ loader = new_fluid_sfloader(sfloader_load_sfont, delete_fluid_sfloader); if(loader == NULL) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_FAIL, _("Failed to create FluidSynth sfloader")); SWAMI_UNLOCK_WRITE (wavetbl); return (FALSE); } fluid_sfloader_set_data(loader, wavetbl); fluid_synth_add_sfloader (wavetbl->synth, loader); wavetbl->audio = new_fluid_audio_driver (wavetbl->settings, wavetbl->synth); /* Load dummy SoundFont to make active items work - sfloader_load_sfont */ fluid_synth_sfload (wavetbl->synth, "!", FALSE); /* create MIDI router to send MIDI to FluidSynth */ wavetbl->midi_router = new_fluid_midi_router (wavetbl->settings, wavetbl_fluidsynth_handle_midi_event, (void *)wavetbl); if (wavetbl->midi_router) { wavetbl->midi = new_fluid_midi_driver (wavetbl->settings, fluid_midi_router_handle_midi_event, (void *)(wavetbl->midi_router)); if (!wavetbl->midi) g_warning (_("Failed to create FluidSynth MIDI input driver")); } else g_warning (_("Failed to create MIDI input router")); /* update reverb */ wavetbl->reverb_update = TRUE; wavetbl_fluidsynth_update_reverb (wavetbl); /* update chorus */ wavetbl->chorus_update = TRUE; wavetbl_fluidsynth_update_chorus (wavetbl); /* load active item if set */ if (wavetbl->active_item) wavetbl_fluidsynth_load_active_item (swami_wavetbl, wavetbl->active_item, NULL); /* restore bank and program channel selections */ for (i = 0; i < wavetbl->channel_count; i++) { fluid_synth_bank_select (wavetbl->synth, i, wavetbl->banks[i]); fluid_synth_program_change (wavetbl->synth, i, wavetbl->programs[i]); } /* monitor all property changes */ wavetbl->prop_callback_handler_id = ipatch_item_prop_connect (NULL, NULL, wavetbl_fluidsynth_prop_callback, NULL, wavetbl); swami_wavetbl->active = TRUE; SWAMI_UNLOCK_WRITE (wavetbl); return (TRUE); } /* called for every property change */ static void wavetbl_fluidsynth_prop_callback (IpatchItemPropNotify *notify) { WavetblFluidSynth *wavetbl = (WavetblFluidSynth *)(notify->user_data); /* quick check to see if property has SYNTH flag set */ if (!(notify->pspec->flags & IPATCH_PARAM_SYNTH)) return; /* check if changed item is a dependent of active audible (for realtime fx) */ SWAMI_LOCK_READ (wavetbl); if (notify->item == wavetbl->active_item && notify->pspec->flags & IPATCH_PARAM_SYNTH_REALTIME) active_item_realtime_update (wavetbl, notify->item, notify->pspec, notify->new_value); SWAMI_UNLOCK_READ (wavetbl); /* see if property change affects any loaded instruments */ if (wavetbl_fluidsynth_check_update_item ((SwamiWavetbl *)wavetbl, notify->item, notify->pspec)) wavetbl_fluidsynth_update_item ((SwamiWavetbl *)wavetbl, notify->item); } /* Called for each event received from the FluidSynth MIDI router */ static int wavetbl_fluidsynth_handle_midi_event (void* data, fluid_midi_event_t* event) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (data); int type = fluid_midi_event_get_type (event); int chan = fluid_midi_event_get_channel (event); int retval; retval = fluid_synth_handle_midi_event (wavetbl->synth, event); switch (type) { case WAVETBL_FLUID_NOTE_ON: swami_control_midi_transmit (wavetbl->midi_ctrl, SWAMI_MIDI_NOTE_ON, chan, fluid_midi_event_get_key (event), fluid_midi_event_get_velocity (event)); break; case WAVETBL_FLUID_NOTE_OFF: swami_control_midi_transmit (wavetbl->midi_ctrl, SWAMI_MIDI_NOTE_OFF, chan, fluid_midi_event_get_key (event), fluid_midi_event_get_velocity (event)); break; case WAVETBL_FLUID_CONTROL_CHANGE: /* update current wavetable bank? */ if (fluid_midi_event_get_control (event) == SWAMI_MIDI_CC_BANK_MSB && chan < wavetbl->channel_count) wavetbl->banks[chan] = fluid_midi_event_get_value (event); swami_control_midi_transmit (wavetbl->midi_ctrl, SWAMI_MIDI_CONTROL, chan, fluid_midi_event_get_control (event), fluid_midi_event_get_value (event)); break; case WAVETBL_FLUID_PROGRAM_CHANGE: if (chan < wavetbl->channel_count) wavetbl->programs[chan] = fluid_midi_event_get_program (event); swami_control_midi_transmit (wavetbl->midi_ctrl, SWAMI_MIDI_PROGRAM_CHANGE, chan, fluid_midi_event_get_program (event), 0); break; case WAVETBL_FLUID_PITCH_BEND: /* FluidSynth uses 0-16383 */ swami_control_midi_transmit (wavetbl->midi_ctrl, SWAMI_MIDI_PITCH_BEND, chan, fluid_midi_event_get_pitch (event) - 8192, 0); break; } return retval; } /* close function for FluidSynth driver */ static void wavetbl_fluidsynth_close (SwamiWavetbl *swami_wavetbl) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); SWAMI_LOCK_WRITE (wavetbl); if (!swami_wavetbl->active) { SWAMI_UNLOCK_WRITE (wavetbl); return; } /* remove our property change callback */ ipatch_item_prop_disconnect (wavetbl->prop_callback_handler_id); if (wavetbl->midi) delete_fluid_midi_driver (wavetbl->midi); if (wavetbl->midi_router) delete_fluid_midi_router (wavetbl->midi_router); if (wavetbl->audio) delete_fluid_audio_driver (wavetbl->audio); if (wavetbl->synth) delete_fluid_synth (wavetbl->synth); if (wavetbl->rt_cache) g_object_unref (wavetbl->rt_cache); wavetbl->midi = NULL; wavetbl->midi_router = NULL; wavetbl->audio = NULL; wavetbl->synth = NULL; wavetbl->rt_cache = NULL; wavetbl->rt_count = 0; swami_wavetbl->active = FALSE; SWAMI_UNLOCK_WRITE (wavetbl); } /* get MIDI control method */ static SwamiControlMidi * wavetbl_fluidsynth_get_control (SwamiWavetbl *swami_wavetbl, int index) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); if (index == 0) return (wavetbl->midi_ctrl); return (NULL); } /* patch load function for FluidSynth driver */ static gboolean wavetbl_fluidsynth_load_patch (SwamiWavetbl *swami_wavetbl, IpatchItem *patch, GError **err) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); char s[16]; /* enough space to store printf "&%p" */ if (!IPATCH_IS_BASE (patch)) { g_set_error (err, SWAMI_ERROR, SWAMI_ERROR_UNSUPPORTED, _("Unsupported item type '%s' for FluidSynth patch load"), G_OBJECT_TYPE_NAME (patch)); return (FALSE); } SWAMI_LOCK_WRITE (wavetbl); /* make sure synth has been initialized */ if (swami_log_if_fail (swami_wavetbl->active)) { SWAMI_UNLOCK_WRITE (wavetbl); return (FALSE); } /* load patch by pointer (our FluidSynth sfloader plugin will use it) */ g_strdup_printf (s, "&%p", (void *)patch); fluid_synth_sfload (wavetbl->synth, s, FALSE); SWAMI_UNLOCK_WRITE (wavetbl); return (TRUE); } /* active item load function for FluidSynth driver */ static gboolean wavetbl_fluidsynth_load_active_item (SwamiWavetbl *swami_wavetbl, IpatchItem *item, GError **err) { WavetblFluidSynth *wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); /* only set as active item if its convertable to a SF2 voice cache */ if (item && ipatch_find_converter (G_OBJECT_TYPE (item), IPATCH_TYPE_SF2_VOICE_CACHE)) { SWAMI_LOCK_WRITE (wavetbl); if (wavetbl->active_item) /* remove reference to any current active item */ g_object_unref (wavetbl->active_item); wavetbl->active_item = g_object_ref (item); /* ++ add reference to item */ if (wavetbl->rt_cache) { g_object_unref (wavetbl->rt_cache); wavetbl->rt_cache = NULL; } wavetbl->rt_count = 0; cache_instrument (wavetbl, item); /* cache the instrument voices */ SWAMI_UNLOCK_WRITE (wavetbl); } return (TRUE); } /* SwamiWavetbl method to check if an item needs to update its synthesis cache */ static gboolean wavetbl_fluidsynth_check_update_item (SwamiWavetbl *wavetbl, IpatchItem *item, GParamSpec *prop) { IpatchSF2VoiceCache *cache; /* if parameter doesn't have the SYNTH flag set, then no update needed */ if (!(prop->flags & IPATCH_PARAM_SYNTH)) return (FALSE); /* check if item is cached */ G_LOCK (voice_cache_hash); cache = g_hash_table_lookup (voice_cache_hash, item); G_UNLOCK (voice_cache_hash); return (cache != NULL); } /* SwamiWavetbl method to update an item's synthesis cache */ static void wavetbl_fluidsynth_update_item (SwamiWavetbl *wavetbl, IpatchItem *item) { SWAMI_LOCK_WRITE (wavetbl); cache_instrument (WAVETBL_FLUIDSYNTH (wavetbl), item); SWAMI_UNLOCK_WRITE (wavetbl); } static void wavetbl_fluidsynth_update_reverb (WavetblFluidSynth *wavetbl) { g_return_if_fail (WAVETBL_IS_FLUIDSYNTH (wavetbl)); if (!wavetbl->synth || !wavetbl->reverb_update) return; wavetbl->reverb_update = FALSE; fluid_synth_set_reverb (wavetbl->synth, wavetbl->reverb_params.room_size, wavetbl->reverb_params.damp, wavetbl->reverb_params.width, wavetbl->reverb_params.level); } /* Lock preset_tables before calling this function */ static int find_reverb_preset (const char *name) { int i; for (i = 0; i < reverb_presets_count; i++) { if (strcmp (reverb_presets[i].name, name) == 0) return (i); } return (0); } static void wavetbl_fluidsynth_update_chorus (WavetblFluidSynth *wavetbl) { g_return_if_fail (WAVETBL_IS_FLUIDSYNTH (wavetbl)); if (!wavetbl->synth || !wavetbl->chorus_update) return; wavetbl->chorus_update = FALSE; fluid_synth_set_chorus (wavetbl->synth, wavetbl->chorus_params.count, wavetbl->chorus_params.level, wavetbl->chorus_params.freq, wavetbl->chorus_params.depth, wavetbl->chorus_params.waveform); } /* Lock preset_tables before calling this function */ static int find_chorus_preset (const char *name) { int i; for (i = 0; i < chorus_presets_count; i++) { if (strcmp (chorus_presets[i].name, name) == 0) return (i); } return (0); } /* FluidSynth sfloader functions */ /** FluidSynth sfloader "load" function */ static fluid_sfont_t * sfloader_load_sfont (fluid_sfloader_t *loader, const char *filename) { fluid_sfont_t *sfont; sfloader_sfont_data_t *sfont_data; IpatchItem *item = NULL; /* file name should be a string in the printf form "&%p" where the pointer is a pointer to a IpatchBase object, or "!" for dummy SoundFont to get active preset item to work when no SoundFont banks loaded */ if (filename[0] == '&') { sscanf (filename, "&%p", (void **)(&item)); if (!item) return (NULL); g_object_ref (item); /* ++ Add a reference to the patch object */ } else if (filename[0] != '!') return (NULL); /* didn't begin with '&' or '!' */ sfont_data = g_malloc0 (sizeof (sfloader_sfont_data_t)); sfont_data->wavetbl = (WavetblFluidSynth *)(fluid_sfloader_get_data(loader)); sfont_data->base_item = IPATCH_BASE (item); sfont = new_fluid_sfont(sfloader_sfont_get_name, sfloader_sfont_get_preset, NULL, NULL, sfloader_sfont_free); fluid_sfont_set_data(sfont, sfont_data); return (sfont); } /* sfloader callback to clean up an fluid_sfont_t structure */ static int sfloader_sfont_free (fluid_sfont_t *sfont) { sfloader_sfont_data_t *sfont_data; sfont_data = (sfloader_sfont_data_t *)(fluid_sfont_get_data(sfont)); if (sfont_data->base_item) /* -- remove reference */ g_object_unref (IPATCH_ITEM (sfont_data->base_item)); g_free (sfont_data); delete_fluid_sfont (sfont); return (_SYNTH_OK); } /* sfloader callback to get a patch file name */ static const char * sfloader_sfont_get_name (fluid_sfont_t *sfont) { sfloader_sfont_data_t *sfont_data; static char buf[256]; /* using static buffer so info string can be freed */ char *s; sfont_data = (sfloader_sfont_data_t *)(fluid_sfont_get_data(sfont)); if (sfont_data->base_item) { g_object_get (sfont_data->base_item, "file-name", &s, NULL); g_strlcpy (buf, s, sizeof (buf)); g_free (s); } else buf[0] = '\0'; return (buf); } /* sfloader callback to get a preset (instrument) by bank and preset number */ static fluid_preset_t * sfloader_sfont_get_preset (fluid_sfont_t *sfont, int bank, int prenum) { sfloader_sfont_data_t *sfont_data; sfloader_preset_data_t *preset_data; fluid_preset_t* preset; int b, p; sfont_data = (sfloader_sfont_data_t *)(fluid_sfont_get_data(sfont)); /* active item bank:preset requested? */ swami_wavetbl_get_active_item_locale (SWAMI_WAVETBL (sfont_data->wavetbl), &b, &p); if (bank == b && prenum == p) { g_object_ref (G_OBJECT (sfont_data->wavetbl)); /* ++ inc wavetbl ref */ preset = new_fluid_preset(sfont, sfloader_active_preset_get_name, sfloader_active_preset_get_banknum, sfloader_active_preset_get_num, sfloader_active_preset_noteon, sfloader_active_preset_free); fluid_preset_set_data(preset, sfont_data->wavetbl); } else /* regular preset request */ { IpatchItem *item; if (!sfont_data->base_item) /* for active preset SoundFont HACK */ return (NULL); /* ++ ref found MIDI instrument object */ item = ipatch_base_find_item_by_midi_locale (sfont_data->base_item, bank, prenum); if (!item) return (NULL); preset_data = g_malloc0 (sizeof (sfloader_preset_data_t)); g_object_ref (G_OBJECT (sfont_data->wavetbl)); /* ++ inc wavetbl ref */ preset_data->wavetbl = sfont_data->wavetbl; preset_data->item = item; /* !! item already referenced by find */ preset = new_fluid_preset(sfont, sfloader_preset_get_name, sfloader_preset_get_banknum, sfloader_preset_get_num, sfloader_preset_noteon, sfloader_preset_free); fluid_preset_set_data(preset, preset_data); } return (preset); } /* sfloader callback to clean up an fluid_preset_t structure */ static void sfloader_preset_free (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data; preset_data = fluid_preset_get_data(preset); /* -- remove item reference */ g_object_unref (IPATCH_ITEM (preset_data->item)); /* remove wavetable object reference */ g_object_unref (G_OBJECT (preset_data->wavetbl)); g_free (preset_data); delete_fluid_preset (preset); } /* sfloader callback to clean up a active item preset structure */ static void sfloader_active_preset_free (fluid_preset_t *preset) { g_object_unref (G_OBJECT (fluid_preset_get_data(preset))); /* -- remove wavetbl obj ref */ delete_fluid_preset (preset); } /* sfloader callback to get the name of a preset */ static const char * sfloader_preset_get_name (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); static char buf[256]; /* return string is static */ char *name; g_object_get (preset_data->item, "name", &name, NULL); g_strlcpy (buf, name, sizeof (buf)); g_free (name); return (buf); } /* sfloader callback to get name of active preset */ static const char * sfloader_active_preset_get_name (fluid_preset_t *preset) { return (_("")); } /* sfloader callback to get the bank number of a preset */ static int sfloader_preset_get_banknum (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); int bank; g_object_get (preset_data->item, "bank", &bank, NULL); return (bank); } /* sfloader callback to get the bank number of active preset */ static int sfloader_active_preset_get_banknum (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); int bank; g_object_get (preset_data->wavetbl, "active-bank", &bank, NULL); return (bank); } /* sfloader callback to get the preset number of a preset */ static int sfloader_preset_get_num (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); int program; g_object_get (preset_data->item, "program", &program, NULL); return (program); } /* sfloader callback to get the preset number of active preset */ static int sfloader_active_preset_get_num (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); int psetnum; g_object_get (preset_data->wavetbl, "active-program", &psetnum, NULL); return (psetnum); } /* sfloader callback for a noteon event */ static int sfloader_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { sfloader_preset_data_t *preset_data = fluid_preset_get_data(preset); WavetblFluidSynth *wavetbl = fluid_preset_get_data(preset); /* No item matches the bank:program? */ if (!preset_data->item) return (_SYNTH_OK); SWAMI_LOCK_WRITE (wavetbl); cache_instrument_noteon (wavetbl, preset_data->item, synth, chan, key, vel); SWAMI_UNLOCK_WRITE (wavetbl); return (_SYNTH_OK); } /* handles noteon event for active item */ static int sfloader_active_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { WavetblFluidSynth *wavetbl = fluid_preset_get_data(preset); SWAMI_LOCK_WRITE (wavetbl); if (!wavetbl->active_item) { SWAMI_UNLOCK_WRITE (wavetbl); return (_SYNTH_OK); /* no active item? Do nothing.. */ } cache_instrument_noteon (wavetbl, wavetbl->active_item, synth, chan, key, vel); SWAMI_UNLOCK_WRITE (wavetbl); return (_SYNTH_OK); } /* caches an instrument item into SoundFont voices for faster processing at * note-on time in cache_instrument_noteon(). * MT-NOTE: Caller is responsible for wavetbl object locking. */ static void cache_instrument (WavetblFluidSynth *wavetbl, IpatchItem *item) { IpatchConverter *conv; IpatchSF2Voice *voice; IpatchSF2VoiceCache *cache; IpatchItem *solo_item = NULL; int i, count; /* ++ ref - create SF2 voice cache converter */ conv = ipatch_create_converter (G_OBJECT_TYPE (item), IPATCH_TYPE_SF2_VOICE_CACHE); /* no SF2 voice cache converter for this item type? */ if (!conv) return; SWAMI_LOCK_READ (wavetbl); if (wavetbl->solo_item) solo_item = g_object_ref (wavetbl->solo_item); /* ++ ref solo item */ SWAMI_UNLOCK_READ (wavetbl); g_object_set (conv, "solo-item", solo_item, NULL); cache = ipatch_sf2_voice_cache_new (NULL, 0); /* ++ ref voice cache */ /* copy session modulators to voice cache */ cache->override_mods = ipatch_sf2_mod_list_duplicate (wavetbl->mods); ipatch_converter_add_input (conv, G_OBJECT (item)); ipatch_converter_add_output (conv, G_OBJECT (cache)); /* Convert item to SF2 voice cache and assign solo-item (if any) * ++ ref list */ if (!ipatch_converter_convert (conv, NULL)) { g_object_unref (cache); /* -- unref voice cache object */ if (solo_item) g_object_unref (solo_item); /* -- unref solo item */ g_object_unref (conv); /* -- unref converter */ return; } if (solo_item) g_object_unref (solo_item); /* -- unref solo item */ g_object_unref (conv); /* -- unref converter */ /* Use voice->user_data to close open cached stores */ cache->voice_user_data_destroy = (GDestroyNotify)ipatch_sample_store_cache_close; /* loop over voices and load sample data into RAM */ count = cache->voices->len; for (i = 0; i < count; i++) { voice = &g_array_index (cache->voices, IpatchSF2Voice, i); ipatch_sf2_voice_cache_sample_data (voice, NULL); /* Keep sample store cached by doing a dummy open */ ipatch_sample_store_cache_open ((IpatchSampleStoreCache *)voice->sample_store); voice->user_data = voice->sample_store; } /* !! hash takes over voice cache reference */ G_LOCK (voice_cache_hash); g_hash_table_insert (voice_cache_hash, item, cache); G_UNLOCK (voice_cache_hash); } /* noteon event function for cached instruments. * MT-NOTE: Caller is responsible for wavetbl object locking. */ static int cache_instrument_noteon (WavetblFluidSynth *wavetbl, IpatchItem *item, fluid_synth_t *synth, int chan, int key, int vel) { guint16 index_array[MAX_INST_VOICES]; /* voice index array */ int sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; fluid_voice_t *fluid_voices[MAX_REALTIME_VOICES]; IpatchSF2VoiceCache *cache; IpatchSF2VoiceSelInfo *sel_info; IpatchSF2GenArray *gen_array; fluid_voice_t *flvoice; fluid_sample_t *wusample; fluid_mod_t *wumod; IpatchSF2Mod *mod; IpatchSF2Voice *voice; int i, voice_count, voice_num; GSList *p; G_LOCK (voice_cache_hash); cache = g_hash_table_lookup (voice_cache_hash, item); if (cache) g_object_ref (cache); /* ++ ref cache object */ G_UNLOCK (voice_cache_hash); if (!cache) return (_SYNTH_OK); /* instrument not yet cached? */ for (i = 0; i < cache->sel_count; i++) { sel_info = &cache->sel_info[i]; switch (sel_info->type) { case IPATCH_SF2_VOICE_SEL_NOTE: sel_values[i] = key; break; case IPATCH_SF2_VOICE_SEL_VELOCITY: sel_values[i] = vel; break; default: sel_values[i] = 127; /* FIXME */ break; } } voice_count = ipatch_sf2_voice_cache_select (cache, sel_values, index_array, MAX_INST_VOICES); /* loop over matching voice indexes */ for (voice_num = 0; voice_num < voice_count; voice_num++) { voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE (cache, index_array[voice_num]); if (!voice->sample_store) continue; /* For ROM and other non-readable samples */ /* FIXME - pool of wusamples? */ wusample = new_fluid_sample(); fluid_sample_set_sound_data(wusample, ipatch_sample_store_cache_get_location((IpatchSampleStoreCache *)(voice->sample_store)), NULL, voice->sample_size, voice->rate, FALSE ); fluid_sample_set_loop(wusample, voice->loop_start, voice->loop_end); fluid_sample_set_pitch(wusample, voice->root_note, voice->fine_tune); /* allocate the FluidSynth voice */ flvoice = fluid_synth_alloc_voice (synth, wusample, chan, key, vel); if (!flvoice) { delete_fluid_sample (wusample); g_object_unref (cache); /* -- unref cache */ return (TRUE); } /* set only those generator parameters that are set */ gen_array = &voice->gen_array; for (i = 0; i < IPATCH_SF2_GEN_COUNT; i++) if (IPATCH_SF2_GEN_ARRAY_TEST_FLAG (gen_array, i)) fluid_voice_gen_set (flvoice, i, (float)(gen_array->values[i].sword)); /* set modulators in fvoice internal list */ /* Note: here modulators are assumed non-linked modulators */ wumod = g_alloca(fluid_mod_sizeof()); /* clear fields is more safe.( in particular next field ) */ memset(wumod,0,fluid_mod_sizeof()); p = voice->mod_list; while (p) { mod = (IpatchSF2Mod *)(p->data); fluid_mod_set_dest(wumod, mod->dest); fluid_mod_set_source1(wumod, mod->src & IPATCH_SF2_MOD_MASK_CONTROL, ((mod->src & (IPATCH_SF2_MOD_MASK_DIRECTION | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_TYPE)) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) | ((mod->src & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); fluid_mod_set_source2(wumod, mod->amtsrc & IPATCH_SF2_MOD_MASK_CONTROL, ((mod->amtsrc & (IPATCH_SF2_MOD_MASK_DIRECTION | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_TYPE)) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) | ((mod->amtsrc & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); fluid_mod_set_amount(wumod, mod->amount); fluid_voice_add_mod (flvoice, wumod, FLUID_VOICE_OVERWRITE); p = p->next; } fluid_synth_start_voice (synth, flvoice); /* let 'er rip */ /* voice pointers are only used for realtime note on, but not much CPU */ if (voice_num < MAX_REALTIME_VOICES) fluid_voices[voice_num] = flvoice; /* !! store reference taken over by wusample structure */ } g_object_unref (cache); /* -- unref cache */ /* check if item is the active audible, and update realtime vars if so */ if (item == wavetbl->active_item) { if (wavetbl->rt_cache) g_object_unref (wavetbl->rt_cache); wavetbl->rt_cache = g_object_ref (cache); /* store selection criteria and FluidSynth voices for note event */ memcpy (wavetbl->rt_sel_values, sel_values, cache->sel_count * sizeof (sel_values[0])); memcpy (wavetbl->rt_voices, fluid_voices, MIN (voice_count, MAX_REALTIME_VOICES) * sizeof (fluid_voices[0])); wavetbl->rt_count = voice_count; } return (_SYNTH_OK); } /* perform a realtime update on the active audible. * MT-NOTE: Wavetbl instance must be locked by caller. */ static void active_item_realtime_update (WavetblFluidSynth *wavetbl, IpatchItem *item, GParamSpec *pspec, const GValue *value) { IpatchSF2VoiceUpdate updates[MAX_REALTIME_UPDATES], *upd; int count, i, rt_count; rt_count = wavetbl->rt_count; if (!wavetbl->rt_cache || rt_count == 0) return; count = ipatch_sf2_voice_cache_update (wavetbl->rt_cache, wavetbl->rt_sel_values, (GObject *)(wavetbl->active_item), (GObject *)item, pspec, value, updates, MAX_REALTIME_UPDATES); /* loop over updates and apply to FluidSynth voices */ for (i = 0; i < count; i++) { upd = &updates[i]; if (upd->voice < rt_count) fluid_voice_gen_set (wavetbl->rt_voices[upd->voice], upd->genid, upd->ival); } /* update parameters (do separately so things are "more" atomic) */ for (i = 0; i < count; i++) { upd = &updates[i]; if (upd->voice < rt_count) fluid_voice_update_param (wavetbl->rt_voices[upd->voice], upd->genid); } } swami-2.2.0/src/plugins/fluidsynth.ui000066400000000000000000003243271361104770400176510ustar00rootroot00000000000000 4096 16 16 1 10 64 10 0 1 0.01 0 0 99 1 2 1 0 1 512 1 10 1 10 64 192000 8000 10 1 10 44100 16 2 4 1 10 3 5 0 0 0.01 0 0 1.2 0 0 0.001 0 0 1 0 0 0.001 0 0 100 0 0 0.1 0 0 30 -30 0.1 0.01 0 -30 10 0 1 0.01 0 0 5 0.29 1 0.01 0 0.29 True Preferences GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False True False 4 True False 6 True 0 0.5 GTK_SHADOW_ETCHED_IN 2 True False 2 2 True 3 2 False 2 2 True Type False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 0 1 fill True Device False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 1 2 fill True Buffer Size False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 2 3 fill True False 2 True True 1 0 False GTK_UPDATE_ALWAYS False False adjustment1 0 True True True Buffer Count False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 False False True True 1 0 True GTK_UPDATE_ALWAYS False False adjustment2 0 True True 1 2 2 3 fill fill True 0 0.5 0 1 True True 0 True True AUTO True 1 2 0 1 fill True False 2 True True True True 0 True * False 0 True True True True Default True GTK_RELIEF_NORMAL False False True 0 False False 1 2 1 2 fill 0 True True True Audio Driver False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False True 0 0.5 GTK_SHADOW_ETCHED_IN 2 True False 2 2 True 2 2 False 2 2 True Type False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 0 1 fill True Device False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 1 2 fill True 0 0.5 0 1 True True 0 True True NONE True 1 2 0 1 True False 2 True True True True 0 True * False 0 True True True True Default True GTK_RELIEF_NORMAL False False True 0 False False 1 2 1 2 0 True True True MIDI Driver False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False True False 4 True True GTK_RELIEF_NORMAL True 0.5 0.5 0 0 True False 2 True gtk-refresh 4 0.5 0.5 0 0 0 False False True Restart True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False True For changes to take effect False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 False False 0 False False True FluidSynth Control GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False True False 4 True False 4 True True True True GTK_POS_TOP False False 4 True False 2 True False 0 True Master Gain False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 False False True True False GTK_POS_TOP 3 GTK_UPDATE_CONTINUOUS False adjustment3 0 True True True True Default True GTK_RELIEF_NORMAL False False True 0 False False 0 False False True 0 0.5 GTK_SHADOW_ETCHED_IN 2 True 5 2 False 2 4 True Level False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 4 5 fill True Width False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 3 4 fill True Damp False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 2 3 fill True Room Size False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 1 2 fill True True False GTK_POS_RIGHT 3 GTK_UPDATE_CONTINUOUS False adjustment4 1 2 1 2 fill True True False GTK_POS_RIGHT 3 GTK_UPDATE_CONTINUOUS False adjustment5 1 2 2 3 fill True True False GTK_POS_RIGHT 3 GTK_UPDATE_CONTINUOUS False adjustment6 1 2 3 4 fill True True False GTK_POS_RIGHT 3 GTK_UPDATE_CONTINUOUS False adjustment7 1 2 4 5 fill True False 2 True False True False True False True True True True 0 True * False True GTK_SELECTION_BROWSE 0 True True True True GTK_RELIEF_NORMAL True 0.5 0.5 0 0 True False 2 True gtk-save 4 0.5 0.5 0 0 0 False False True Save True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False 1 2 0 1 fill True Preset False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 0 1 fill True Reverb False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False True 0 0.5 GTK_SHADOW_ETCHED_IN 2 True 6 2 False 2 4 True Waveform False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 5 6 fill True Depth False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 4 5 fill True Frequency False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 3 4 fill True Level False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 2 3 fill True Count False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 1 2 fill True False 2 True False True False True False True True True True 0 True * False True GTK_SELECTION_BROWSE 0 True True True True GTK_RELIEF_NORMAL True 0.5 0.5 0 0 True False 2 True gtk-save 4 0.5 0.5 0 0 0 False False True Save True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False 1 2 0 1 fill True Preset False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 0 1 fill True 0 0.5 0 1 True True 0 True True Sine True True Triangle True 1 2 5 6 fill expand True True False GTK_POS_TOP 3 GTK_UPDATE_CONTINUOUS False adjustment8 1 2 4 5 True True False GTK_POS_TOP 3 GTK_UPDATE_CONTINUOUS False adjustment9 1 2 3 4 True True False GTK_POS_TOP 3 GTK_UPDATE_CONTINUOUS False adjustment10 1 2 2 3 True True False GTK_POS_TOP 0 GTK_UPDATE_CONTINUOUS False adjustment11 1 2 1 2 True Chorus False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False False True True Synthesis False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 4 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True False False True False True True MIDI Channels False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 4 True 3 2 False 2 2 True Polyphony False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 1 2 fill True Sample Rate False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 2 3 fill True Interpolation False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 1 0 1 fill True False 2 True True 0 True None True True Linear True True 4th Order True True 7th Order True 0 False False True True Default True GTK_RELIEF_NORMAL False False True 0 False False 1 2 0 1 fill True 0 0.5 0 1 True True 1 0 True GTK_UPDATE_ALWAYS False False adjustment12 1 2 1 2 True 0 0.5 0 1 True True 1 0 True GTK_UPDATE_ALWAYS False False adjustment13 1 2 2 3 False False True Performance False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 True True True 1 0.5 0 1 True True GTK_RELIEF_NORMAL True 0.5 0.5 0 0 True False 2 True gtk-close 4 0.5 0.5 0 0 0 False False True Close True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False swami-2.2.0/src/plugins/fluidsynth_gui.c000066400000000000000000000263171361104770400203200ustar00rootroot00000000000000/* * fluidsynth_gui.c - GUI widgets for FluidSynth Swami plugin. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include "fluidsynth_gui_i18n.h" typedef struct { GtkVBox parent_instance; GtkWidget *ctrl_widg; } FluidSynthGuiControl; typedef struct { GtkVBoxClass parent_class; } FluidSynthGuiControlClass; static int plugin_fluidsynth_gui_init (SwamiPlugin *plugin, GError **err); static GtkWidget *fluid_synth_pref_handler (void); static void fluid_synth_gui_audio_driver_changed (GtkComboBox *combo, gpointer user_data); static void fluid_synth_gui_midi_driver_changed (GtkComboBox *combo, gpointer user_data); static GType fluid_synth_gui_control_register_type (SwamiPlugin *plugin); static void fluid_synth_gui_control_class_init (FluidSynthGuiControlClass *klass); static void fluid_synth_gui_control_init (FluidSynthGuiControl *fsctrl); SWAMI_PLUGIN_INFO (plugin_fluidsynth_gui_init, NULL); /* List of audio and MIDI drivers that have Glade widgets */ const char *audio_driver_widgets[] = { "alsa", "jack", "oss", "dsound", NULL }; const char *midi_driver_widgets[] = { "alsa_seq", "alsa_raw", "oss", NULL }; /* --- functions --- */ /* plugin init function (one time initialize of SwamiPlugin) */ static gboolean plugin_fluidsynth_gui_init (SwamiPlugin *plugin, GError **err) { /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain ("SwamiPlugin-fluidsynth-gui", LOCALEDIR); #endif g_object_set (plugin, "name", "FluidSynthGui", "version", "1.1", "author", "Element Green", "copyright", "Copyright (C) 2007-2014", "descr", N_("FluidSynth software wavetable synth GUI plugin"), "license", "GPL", NULL); /* initialize types */ fluid_synth_gui_control_register_type (plugin); // fluid_synth_gui_map_register_type (plugin); // fluid_synth_gui_channels_register_type (plugin); swamigui_register_pref_handler ("FluidSynth", GTK_STOCK_MEDIA_PLAY, SWAMIGUI_PREF_ORDER_NAME, fluid_synth_pref_handler); return (TRUE); } /* preferences handler */ static GtkWidget * fluid_synth_pref_handler (void) { GtkWidget *fluid_widg; GtkWidget *widg; GtkListStore *store; GtkCellRenderer *cell; char **options, **optionp; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); fluid_widg = swamigui_util_glade_create ("FluidSynthPrefs"); if (swamigui_root->wavetbl) { /* Initialize audio driver list */ widg = swamigui_util_glade_lookup (fluid_widg, "ComboAudioDriver"); store = gtk_list_store_new (1, G_TYPE_STRING); gtk_combo_box_set_model (GTK_COMBO_BOX (widg), GTK_TREE_MODEL (store)); g_object_unref (store); cell = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widg), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widg), cell, "text", 0, NULL); g_object_get (swamigui_root->wavetbl, "audio.driver-options", &options, NULL); /* ++ alloc */ for (optionp = options; *optionp; optionp++) gtk_combo_box_append_text (GTK_COMBO_BOX (widg), *optionp); g_boxed_free (G_TYPE_STRV, options); /* -- free */ /* Connect to changed signal of audio driver combo box */ g_signal_connect (widg, "changed", G_CALLBACK (fluid_synth_gui_audio_driver_changed), fluid_widg); /* Connect the audio combo box to the "audio.driver" property */ swamigui_control_prop_connect_widget (G_OBJECT (swamigui_root->wavetbl), "audio.driver", G_OBJECT (widg)); /* Initialize MIDI driver list */ widg = swamigui_util_glade_lookup (fluid_widg, "ComboMidiDriver"); store = gtk_list_store_new (1, G_TYPE_STRING); gtk_combo_box_set_model (GTK_COMBO_BOX (widg), GTK_TREE_MODEL (store)); g_object_unref (store); cell = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widg), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widg), cell, "text", 0, NULL); g_object_get (swamigui_root->wavetbl, "midi.driver-options", &options, NULL); /* ++ alloc */ for (optionp = options; *optionp; optionp++) gtk_combo_box_append_text (GTK_COMBO_BOX (widg), *optionp); g_boxed_free (G_TYPE_STRV, options); /* -- free */ /* Connect to changed signal of MIDI driver combo box */ g_signal_connect (widg, "changed", G_CALLBACK (fluid_synth_gui_midi_driver_changed), fluid_widg); /* Connect the MIDI combo box to the "midi.driver" property */ swamigui_control_prop_connect_widget (G_OBJECT (swamigui_root->wavetbl), "midi.driver", G_OBJECT (widg)); /* Connect widgets to FluidSynth properties */ swamigui_control_glade_prop_connect (fluid_widg, G_OBJECT (swamigui_root->wavetbl)); } gtk_widget_show (fluid_widg); return (fluid_widg); } /* Callback when audio driver combo box changes */ static void fluid_synth_gui_audio_driver_changed (GtkComboBox *combo, gpointer user_data) { GtkWidget *fluid_widg = GTK_WIDGET (user_data); GtkWidget *driverwidg; GtkWidget *vbox; char *driver, *widgname; const char **sptr; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); driver = gtk_combo_box_get_active_text (combo); /* ++ alloc */ vbox = swamigui_util_glade_lookup (fluid_widg, "VBoxAudioDriver"); /* Remove existing child, if any */ gtk_container_foreach (GTK_CONTAINER (vbox), (GtkCallback)gtk_object_destroy, NULL); /* See if the driver has a widget */ for (sptr = audio_driver_widgets; *sptr; sptr++) if (strcmp (driver, *sptr) == 0) break; if (*sptr) { widgname = g_strconcat ("FluidSynth-Audio:", driver, NULL); /* ++ alloc */ driverwidg = swamigui_util_glade_create (widgname); g_free (widgname); /* -- free */ if (driverwidg) { swamigui_control_glade_prop_connect (driverwidg, G_OBJECT (swamigui_root->wavetbl)); gtk_box_pack_start (GTK_BOX (vbox), driverwidg, FALSE, FALSE, 0); } } } /* Callback when MIDI driver combo box changes */ static void fluid_synth_gui_midi_driver_changed (GtkComboBox *combo, gpointer user_data) { GtkWidget *fluid_widg = GTK_WIDGET (user_data); GtkWidget *driverwidg; GtkWidget *vbox; char *driver, *widgname; const char **sptr; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); driver = gtk_combo_box_get_active_text (combo); /* ++ alloc */ vbox = swamigui_util_glade_lookup (fluid_widg, "VBoxMidiDriver"); /* Remove existing child, if any */ gtk_container_foreach (GTK_CONTAINER (vbox), (GtkCallback)gtk_object_destroy, NULL); /* See if the driver has a widget */ for (sptr = midi_driver_widgets; *sptr; sptr++) if (strcmp (driver, *sptr) == 0) break; if (*sptr) { widgname = g_strconcat ("FluidSynth-MIDI:", driver, NULL); /* ++ alloc */ driverwidg = swamigui_util_glade_create (widgname); g_free (widgname); /* -- free */ if (driverwidg) { swamigui_control_glade_prop_connect (driverwidg, G_OBJECT (swamigui_root->wavetbl)); gtk_box_pack_start (GTK_BOX (vbox), driverwidg, FALSE, FALSE, 0); } } } static GType fluid_synth_gui_control_register_type (SwamiPlugin *plugin) { static const GTypeInfo obj_info = { sizeof (FluidSynthGuiControlClass), NULL, NULL, (GClassInitFunc)fluid_synth_gui_control_class_init, NULL, NULL, sizeof (FluidSynthGuiControl), 0, (GInstanceInitFunc) fluid_synth_gui_control_init, }; return (g_type_module_register_type (G_TYPE_MODULE (plugin), GTK_TYPE_VBOX, "FluidSynthGuiControl", &obj_info, 0)); } static void fluid_synth_gui_control_class_init (FluidSynthGuiControlClass *klass) { } static void fluid_synth_gui_control_init (FluidSynthGuiControl *fsctrl) { SwamiWavetbl *wavetbl; char namebuf[32]; const char *knobnames[] = { "Gain", "ReverbLevel", "ReverbRoom", "ReverbWidth", "ReverbDamp", "ChorusLevel", "ChorusCount", "ChorusFreq", "ChorusDepth" }; const char *propnames[] = { "synth-gain", "reverb-level", "reverb-room-size", "reverb-width", "reverb-damp", "chorus-level", "chorus-count", "chorus-freq", "chorus-depth" }; SwamiControl *propctrl, *widgctrl; GtkAdjustment *adj; GtkWidget *widg; GType type; int i; /* get swamigui_root from swamigui library */ SwamiguiRoot * swamigui_root = swamigui_get_swamigui_root (); fsctrl->ctrl_widg = swamigui_util_glade_create ("FluidSynth"); gtk_widget_show (fsctrl->ctrl_widg); gtk_box_pack_start (GTK_BOX (fsctrl), fsctrl->ctrl_widg, FALSE, FALSE, 0); wavetbl = (SwamiWavetbl *)swami_object_get_by_type (G_OBJECT (swamigui_root), "WavetblFluidSynth"); // ++ ref wavetbl if (!wavetbl) return; for (i = 0; i < G_N_ELEMENTS (knobnames); i++) { strcpy (namebuf, "Knob"); strcat (namebuf, knobnames[i]); widg = swamigui_util_glade_lookup (fsctrl->ctrl_widg, namebuf); adj = swamigui_knob_get_adjustment (SWAMIGUI_KNOB (widg)); propctrl = swami_get_control_prop_by_name (G_OBJECT (wavetbl), propnames[i]); widgctrl = SWAMI_CONTROL (swamigui_control_adj_new (adj)); swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); } propctrl = swami_get_control_prop_by_name (G_OBJECT (wavetbl), "synth.reverb.active"); widg = swamigui_util_glade_lookup (fsctrl->ctrl_widg, "BtnReverb"); widgctrl = swamigui_control_new_for_widget (G_OBJECT (widg)); swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); propctrl = swami_get_control_prop_by_name (G_OBJECT (wavetbl), "synth.chorus.active"); widg = swamigui_util_glade_lookup (fsctrl->ctrl_widg, "BtnChorus"); widgctrl = swamigui_control_new_for_widget (G_OBJECT (widg)); swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); widg = swamigui_util_glade_lookup (fsctrl->ctrl_widg, "ComboChorusType"); propctrl = swami_get_control_prop_by_name (G_OBJECT (wavetbl), "chorus-waveform"); type = g_type_from_name ("WavetblFluidSynthChorusWaveform"); widgctrl = swamigui_control_new_for_widget_full (G_OBJECT (widg), type, NULL, 0); swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); g_object_unref (wavetbl); // -- unref wavetbl } swami-2.2.0/src/plugins/fluidsynth_gui.def000066400000000000000000000000421361104770400206170ustar00rootroot00000000000000LIBRARY EXPORTS swami_plugin_info swami-2.2.0/src/plugins/fluidsynth_gui_i18n.h000066400000000000000000000006761361104770400211640ustar00rootroot00000000000000#ifndef __SWAMIPLUGIN_FLUIDSYNTH_GUI_I18N_H__ #define __SWAMIPLUGIN_FLUIDSYNTH_GUI_I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) dgettext("SwamiPlugin-fluidsynth-gui", x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif swami-2.2.0/src/plugins/fluidsynth_i18n.c000066400000000000000000000024001361104770400202760ustar00rootroot00000000000000/* * Translatable strings file generated by Glade. * Add this file to your project's POTFILES.in. * DO NOT compile it as part of your application. */ gchar *s = N_("Preferences"); gchar *s = N_("Restart drivers for changes to take effect"); gchar *s = N_("Type"); gchar *s = N_("Device"); gchar *s = N_("Buffer Size"); gchar *s = N_("Buffer Count"); gchar *s = N_("AUTO"); gchar *s = N_("*"); gchar *s = N_("Default"); gchar *s = N_("Audio Driver"); gchar *s = N_("Type"); gchar *s = N_("Device"); gchar *s = N_("NONE"); gchar *s = N_("*"); gchar *s = N_("Default"); gchar *s = N_("MIDI Driver"); gchar *s = N_("FluidSynth Control"); gchar *s = N_("Master Gain"); gchar *s = N_("Default"); gchar *s = N_("Disabled"); gchar *s = N_("Default *"); gchar *s = N_("Custom"); gchar *s = N_("Room Size"); gchar *s = N_("Damp"); gchar *s = N_("Width"); gchar *s = N_("Level"); gchar *s = N_("Reverb"); gchar *s = N_("Disabled"); gchar *s = N_("Default *"); gchar *s = N_("Custom"); gchar *s = N_("N"); gchar *s = N_("Level"); gchar *s = N_("Frequency"); gchar *s = N_("Depth"); gchar *s = N_("Waveform"); gchar *s = N_("Sine"); gchar *s = N_("Triangle"); gchar *s = N_("Chorus"); gchar *s = N_("* marks options that require a driver restart to take effect"); gchar *s = N_("Close"); swami-2.2.0/src/plugins/fluidsynth_i18n.h000066400000000000000000000006621361104770400203130ustar00rootroot00000000000000#ifndef __SWAMIPLUGIN_FLUIDSYNTH_I18N_H__ #define __SWAMIPLUGIN_FLUIDSYNTH_I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) dgettext("SwamiPlugin-fluidsynth", x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif swami-2.2.0/src/plugins/fluidsynth_plugin.def000066400000000000000000000000421361104770400213310ustar00rootroot00000000000000LIBRARY EXPORTS swami_plugin_info swami-2.2.0/src/swamigui/000077500000000000000000000000001361104770400152525ustar00rootroot00000000000000swami-2.2.0/src/swamigui/CMakeLists.txt000066400000000000000000000170401361104770400200140ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # include_directories ( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${GUI_INCLUDEDIR} ${GUI_INCLUDE_DIRS} ${LIBGLADE_INCLUDEDIR} ${LIBGLADE_INCLUDE_DIRS} ${LIBINSTPATCH_INCLUDEDIR} ${LIBINSTPATCH_INCLUDE_DIRS} ) # ************ swamigui Library ************ set ( libswamigui_public_HEADERS builtin_enums.h SwamiguiBar.h SwamiguiBarPtr.h SwamiguiCanvasMod.h SwamiguiComboEntry.h SwamiguiControl.h SwamiguiControlAdj.h SwamiguiControlMidiKey.h SwamiguiItemMenu.h SwamiguiKnob.h SwamiguiLoopFinder.h SwamiguiMenu.h SwamiguiModEdit.h SwamiguiMultiSave.h SwamiguiNoteSelector.h SwamiguiPanel.h SwamiguiPanelSelector.h SwamiguiPanelSF2Gen.h SwamiguiPanelSF2GenEnv.h SwamiguiPanelSF2GenMisc.h SwamiguiPaste.h SwamiguiPiano.h SwamiguiPref.h SwamiguiProp.h SwamiguiRoot.h SwamiguiSampleCanvas.h SwamiguiSampleEditor.h SwamiguiSpectrumCanvas.h SwamiguiSpinScale.h SwamiguiSplits.h SwamiguiStatusbar.h SwamiguiTree.h SwamiguiTreeStore.h SwamiguiTreeStoreConfig.h SwamiguiTreeStorePatch.h help.h icons.h patch_funcs.h splash.h util.h widgets/combo-box.h widgets/icon-combo.h ) set ( libswamigui_SOURCES builtin_enums.c SwamiguiBar.c SwamiguiBarPtr.c SwamiguiCanvasMod.c SwamiguiComboEntry.c SwamiguiControl.c SwamiguiControl_widgets.c SwamiguiControlAdj.c SwamiguiControlMidiKey.c SwamiguiItemMenu.c SwamiguiItemMenu_actions.c SwamiguiKnob.c SwamiguiLoopFinder.c SwamiguiMenu.c SwamiguiModEdit.c SwamiguiMultiSave.c SwamiguiNoteSelector.c SwamiguiPanel.c SwamiguiPanelSelector.c SwamiguiPanelSF2Gen.c SwamiguiPanelSF2GenEnv.c SwamiguiPanelSF2GenMisc.c SwamiguiPaste.c SwamiguiPiano.c SwamiguiPref.c SwamiguiProp.c SwamiguiRoot.c SwamiguiSampleCanvas.c SwamiguiSampleEditor.c SwamiguiSpectrumCanvas.c SwamiguiSpinScale.c SwamiguiSplits.c SwamiguiStatusbar.c SwamiguiTree.c SwamiguiTreeStore.c SwamiguiTreeStoreConfig.c SwamiguiTreeStorePatch.c help.c icons.c patch_funcs.c splash.c util.c widgets/combo-box.c widgets/icon-combo.c ) set ( public_main_HEADER swamigui.h ) link_directories ( ${GUI_LIBDIR} ${GUI_LIBRARY_DIRS} ${LIBGLADE_LIBDIR} ${LIBGLADE_LIBRARY_DIRS} ${LIBINSTPATCH_LIBDIR} ${LIBINSTPATCH_LIBRARY_DIRS} ) add_definitions ( -DLOCALEDIR=\"${CMAKE_INSTALL_LOCALEDIR}\" -DG_LOG_DOMAIN=\"libswamigui\" ) set (DEFINITION_FILE "") # Options for WINDOWS only if( MSVC ) # disable deprecation warnings add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) if (BUILD_SHARED_LIBS) # adding a module definition file for shared libs will export symbols and produce import library set (DEFINITION_FILE libswamigui.def) endif(BUILD_SHARED_LIBS) endif( MSVC ) add_library ( libswamigui ${CMAKE_CURRENT_BINARY_DIR}/marshals.c ${libswamigui_SOURCES} ${DEFINITION_FILE} ) if (UNIX) set(MATH_LIB m) endif (UNIX) target_link_libraries ( libswamigui libswami ${GUI_LIBRARIES} ${LIBGLADE_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ${COREFOUNDATION} ${MATH_LIB} ) install ( FILES ${libswamigui_public_HEADERS} ${public_main_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/swami/libswamigui ) if (APPLE) set_property ( TARGET libswamigui APPEND PROPERTY INCLUDE_DIRECTORIES "${COREFOUNDATION}/Headers" ) endif (APPLE) # ************ Swami GUI application ************ add_executable ( swami main.c ) if ( SWAMI_CPPFLAGS ) set_target_properties ( swami PROPERTIES COMPILE_FLAGS ${SWAMI_CPPFLAGS} ) endif ( SWAMI_CPPFLAGS ) target_link_libraries ( swami libswamigui libswami ${GUI_LIBRARIES} ${LIBGLADE_LIBRARIES} ${LIBINSTPATCH_LIBRARIES} ) # Process subdirectories add_subdirectory ( images ) find_program (GLIB2_MKENUMS glib-mkenums) add_custom_target (libswamigui-enums COMMAND ${GLIB2_MKENUMS} --fhead \"\#ifndef __SWAMIGUI_BUILTIN_ENUMS_H__\\n\" --fhead \"\#define __SWAMIGUI_BUILTIN_ENUMS_H__\\n\\n\" --fhead \"\#include \\n\\n\" --fhead \"G_BEGIN_DECLS\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\\n\" --vhead \"GType @enum_name@_get_type \(void\)\;\\n\" --vhead \"\#define SWAMIGUI_TYPE_@ENUMSHORT@ \(@enum_name@_get_type\(\)\)\\n\" --ftail \"G_END_DECLS\\n\\n\" --ftail \"\#endif /* __SWAMIGUI_BUILTIN_ENUMS_H__ */\" ${libswamigui_public_HEADERS} > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.h COMMAND ${GLIB2_MKENUMS} --fhead \"\#include \\"swamigui.h\\"\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\" --vhead \"static const G@Type@Value _@enum_name@_values[] = {\" --vprod \" { @VALUENAME@, \\"@VALUENAME@\\", \\"@valuenick@\\" },\" --vtail \" { 0, NULL, NULL }\\n}\;\\n\\n\" --vtail \"GType\\n@enum_name@_get_type \(void\)\\n{\\n\" --vtail \" static GType type = 0\;\\n\\n\" --vtail \" if \(G_UNLIKELY \(type == 0\)\)\\n\" --vtail \" type = g_\@type\@_register_static \(\\"@EnumName@\\", _@enum_name@_values\)\;\\n\\n\" --vtail \" return type\;\\n}\\n\\n\" ${libswamigui_public_HEADERS} > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${libswamigui_public_HEADERS} ) find_program (GLIB2_GENMARSHAL glib-genmarshal) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.c COMMAND ${GLIB2_GENMARSHAL} ARGS --body --prefix=swamigui_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list >${CMAKE_CURRENT_BINARY_DIR}/marshals.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ${CMAKE_CURRENT_BINARY_DIR}/marshals.h ) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.h COMMAND ${GLIB2_GENMARSHAL} ARGS --header --prefix=swamigui_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list >${CMAKE_CURRENT_BINARY_DIR}/marshals.h WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ) if ( MACOSX_FRAMEWORK ) set_property ( SOURCE ${libswami_public_HEADERS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/libswamigui ) set_target_properties ( libswamigui PROPERTIES OUTPUT_NAME "libswamigui" FRAMEWORK TRUE PUBLIC_HEADER "${public_main_HEADER}" FRAMEWORK_VERSION "${LIB_VERSION_CURRENT}" INSTALL_NAME_DIR ${FRAMEWORK_INSTALL_DIR} VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif( MINGW OR WIN32 ) set_target_properties ( libswamigui PROPERTIES PREFIX "" OUTPUT_NAME "libswamigui" VERSION ${LIB_VERSION_INFO} ) else ( MINGW OR WIN32 ) set_target_properties ( libswamigui PROPERTIES PREFIX "lib" OUTPUT_NAME "swamigui" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) endif ( MACOSX_FRAMEWORK ) install ( TARGETS swami libswamigui RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} BUNDLE DESTINATION ${BUNDLE_INSTALL_DIR} ) install ( FILES swami-2.ui DESTINATION ${UIXML_DIR} ) swami-2.2.0/src/swamigui/SwamiguiBar.c000066400000000000000000000332201361104770400176300ustar00rootroot00000000000000/** * SECTION:SwamiguiBar * @short_description: Canvas item for displaying multiple pointers or ranges. * @see_also: #SwamiguiBarPtr * * Horizontal bar canvas item for displaying multiple pointers and/or ranges. * A #SwamiguiBar is composed of one or more #SwamiguiBarPtr items. */ /* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "libswami/swami_priv.h" #include "SwamiguiBar.h" #include "i18n.h" enum { PROP_0, PROP_MIN_HEIGHT, /* min height of pointers (top of stack) */ PROP_MAX_HEIGHT /* max height of pointers (bottom of stack) */ }; /* structure which defines an interface pointer */ typedef struct { char *id; /* id of this item */ GnomeCanvasItem *barptr; /* pointer canvas item */ SwamiguiBar *bar; /* parent bar object */ gboolean mouse_sel; /* TRUE if mouse selection is in progress */ } PtrInfo; static void swamigui_bar_class_init (SwamiguiBarClass *klass); static void swamigui_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_bar_init (SwamiguiBar *bar); static void swamigui_bar_finalize (GObject *object); static gboolean swamigui_bar_cb_ptr_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data); static void swamigui_bar_update_ptr_heights (SwamiguiBar *bar); static GObjectClass *parent_class = NULL; GType swamigui_bar_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiBarClass), NULL, NULL, (GClassInitFunc) swamigui_bar_class_init, NULL, NULL, sizeof (SwamiguiBar), 0, (GInstanceInitFunc) swamigui_bar_init, }; obj_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "SwamiguiBar", &obj_info, 0); } return (obj_type); } static void swamigui_bar_class_init (SwamiguiBarClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_bar_set_property; obj_class->get_property = swamigui_bar_get_property; obj_class->finalize = swamigui_bar_finalize; g_object_class_install_property (obj_class, PROP_MIN_HEIGHT, g_param_spec_int ("min-height", _("Min height"), _("Minimum height of pointers"), 1, G_MAXINT, 16, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MAX_HEIGHT, g_param_spec_int ("max-height", _("Max height"), _("Maximum height of pointers"), 1, G_MAXINT, 48, G_PARAM_READWRITE)); } static void swamigui_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiBar *bar = SWAMIGUI_BAR (object); switch (property_id) { case PROP_MIN_HEIGHT: bar->min_height = g_value_get_int (value); swamigui_bar_update_ptr_heights (bar); break; case PROP_MAX_HEIGHT: bar->max_height = g_value_get_int (value); swamigui_bar_update_ptr_heights (bar); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiBar *bar = SWAMIGUI_BAR (object); switch (property_id) { case PROP_MIN_HEIGHT: g_value_set_int (value, bar->min_height); break; case PROP_MAX_HEIGHT: g_value_set_int (value, bar->max_height); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_bar_init (SwamiguiBar *bar) { bar->min_height = 16; bar->max_height = 48; bar->ptrlist = NULL; } static void swamigui_bar_finalize (GObject *object) { SwamiguiBar *bar = SWAMIGUI_BAR (object); PtrInfo *info; GList *p; for (p = bar->ptrlist; p; p = g_list_delete_link (p, p)) { info = (PtrInfo *)(p->data); g_free (info->id); g_free (p->data); } if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize)(object); } /* callback for SwamiguiBarPtr events */ static gboolean swamigui_bar_cb_ptr_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data) { PtrInfo *ptrinfo = (PtrInfo *)data; SwamiguiBar *bar = SWAMIGUI_BAR (ptrinfo->bar); GdkEventButton *bevent; switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *)event; if (bevent->button != 1) break; ptrinfo->mouse_sel = TRUE; /* make sure the item is raised to the top */ swamigui_bar_raise_pointer_to_top (bar, ptrinfo->id); gnome_canvas_item_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, bevent->time); return (TRUE); case GDK_BUTTON_RELEASE: if (!ptrinfo->mouse_sel) break; /* not selected? */ ptrinfo->mouse_sel = FALSE; gnome_canvas_item_ungrab (item, event->button.time); break; case GDK_MOTION_NOTIFY: if (!ptrinfo->mouse_sel) break; /* not selected? */ break; default: break; } return (FALSE); } /* update heights of all pointers based on current stacking order */ static void swamigui_bar_update_ptr_heights (SwamiguiBar *bar) { PtrInfo *info; int count, height_diff, min_height, max_height; double xform[6]; GList *p; int height; int i; count = g_list_length (bar->ptrlist); if (count == 0) return; /* just in case they are swapped */ min_height = MIN (bar->min_height, bar->max_height); max_height = MAX (bar->min_height, bar->max_height); height_diff = max_height - min_height; /* set height and y-position of pointers */ for (i = 0, p = bar->ptrlist; i < count; i++, p = p->next) { info = (PtrInfo *)(p->data); /* set the height of the pointer between min-height and max-height */ if (count != 1) height = min_height + (height_diff * i) / (count - 1); else height = max_height; g_object_set (info->barptr, "height", height, NULL); /* set the y pos of pointer so they line up on the bottom */ gnome_canvas_item_i2w_affine (info->barptr, xform); xform[5] = max_height - height; gnome_canvas_item_affine_absolute (info->barptr, xform); } } /** * swamigui_bar_create_pointer: * @bar: Bar canvas item * @id: String identifier to use for this pointer * @first_property_name: Name of first #SwamiguiBarPtr property to assign to or * %NULL to not set any pointer properties. * @...: First value to assign to @first_property_name followed by additional * name/value pairs, terminated with a %NULL name argument. * * Creates a new #SwamiguiBarPtr, sets properties and adds it to a bar canvas * item. */ void swamigui_bar_create_pointer (SwamiguiBar *bar, const char *id, const char *first_property_name, ...) { SwamiguiBarPtr *barptr; va_list args; g_return_if_fail (SWAMIGUI_IS_BAR (bar)); g_return_if_fail (id != NULL); barptr = g_object_new (SWAMIGUI_TYPE_BAR_PTR, NULL); va_start (args, first_property_name); g_object_set_valist (G_OBJECT (barptr), first_property_name, args); va_end (args); swamigui_bar_add_pointer (bar, barptr, id); } /** * swamigui_bar_add_pointer: * @bar: Bar canvas item * @barptr: Existing bar pointer to add to bar canvas item. * @id: String identifier to use for this pointer * * Add an existing bar pointer to a bar canvas item. */ void swamigui_bar_add_pointer (SwamiguiBar *bar, SwamiguiBarPtr *barptr, const char *id) { PtrInfo *info; g_return_if_fail (SWAMIGUI_IS_BAR (bar)); g_return_if_fail (SWAMIGUI_IS_BAR_PTR (barptr)); g_return_if_fail (id != NULL); info = g_slice_new (PtrInfo); info->id = g_strdup (id); info->barptr = GNOME_CANVAS_ITEM (barptr); info->bar = bar; info->mouse_sel = FALSE; bar->ptrlist = g_list_append (bar->ptrlist, info); gnome_canvas_item_reparent (GNOME_CANVAS_ITEM (barptr), GNOME_CANVAS_GROUP (bar)); gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM (barptr)); g_signal_connect (G_OBJECT (barptr), "event", G_CALLBACK (swamigui_bar_cb_ptr_event), info); swamigui_bar_update_ptr_heights (bar); } /** * @bar: Bar canvas item * @id: String identifier of pointer to find * * Get a bar pointer object in a bar canvas item identified by its string ID. * * Returns: The bar pointer object with the given string @id or %NULL if not * found. */ GnomeCanvasItem * swamigui_bar_get_pointer (SwamiguiBar *bar, const char *id) { PtrInfo *info; GList *p; g_return_val_if_fail (SWAMIGUI_IS_BAR (bar), NULL); g_return_val_if_fail (id != NULL, NULL); for (p = bar->ptrlist; p; p = p->next) { info = (PtrInfo *)(p->data); if (strcmp (info->id, id) == 0) break; } return (p ? info->barptr : NULL); } /** * swamigui_bar_set_pointer_position: * @bar: Bar canvas item * @id: String identifier of pointer to set position of * @position: Position in pixels to set pointer to * * Set the position of a #SwamiguiBarPtr item in a bar canvas item. Pointer * is centered on the given @position regardless of mode (position or range). */ void swamigui_bar_set_pointer_position (SwamiguiBar *bar, const char *id, int position) { GnomeCanvasItem *barptr; double xform[6]; int width; barptr = swamigui_bar_get_pointer (bar, id); g_return_if_fail (barptr != NULL); g_object_get (barptr, "width", &width, NULL); position -= width / 2; gnome_canvas_item_i2w_affine (barptr, xform); xform[4] = position; gnome_canvas_item_affine_absolute (barptr, xform); } /** * swamigui_bar_set_pointer_range: * @bar: Bar canvas item * @id: String identifier of pointer to set position of * @start: Position in pixels of start of range * @end: Position in pixels of end of range * * Set the range of a #SwamiguiBarPtr item in a bar canvas item. Pointer is * set to the range defined by @start and @end. */ void swamigui_bar_set_pointer_range (SwamiguiBar *bar, const char *id, int start, int end) { GnomeCanvasItem *barptr; double xform[6]; int temp; barptr = swamigui_bar_get_pointer (bar, id); g_return_if_fail (barptr != NULL); if (start > end) /* swap if backwards */ { temp = start; start = end; end = temp; } g_object_set (barptr, "width", end - start + 1, NULL); gnome_canvas_item_i2w_affine (barptr, xform); xform[4] = start; gnome_canvas_item_affine_absolute (barptr, xform); } /** * swamigui_bar_get_pointer_order: * @bar: Bar canvas item * @id: String identifier of pointer to get stacking order position of * * Get the stacking order of a pointer. * * Returns: Stacking order of pointer (0 = top, -1 if pointer not found). */ int swamigui_bar_get_pointer_order (SwamiguiBar *bar, const char *id) { PtrInfo *info; GList *p; int i; g_return_val_if_fail (SWAMIGUI_IS_BAR (bar), -1); g_return_val_if_fail (id != NULL, -1); for (p = bar->ptrlist, i = 0; p; p = p->next, i++) { info = (PtrInfo *)(p->data); if (strcmp (info->id, id) == 0) break; } return (p ? i : -1); } /** * swamigui_bar_set_pointer_order: * @bar: Bar canvas item * @id: String identifier of pointer to set stacking order position of * @pos: Absolute position in stacking order (0 = top, -1 = bottom) * * Set the stacking order of a pointer to @pos. */ void swamigui_bar_set_pointer_order (SwamiguiBar *bar, const char *id, int pos) { PtrInfo *info; GList *p; int i; g_return_if_fail (SWAMIGUI_IS_BAR (bar)); g_return_if_fail (id != NULL); for (p = bar->ptrlist, i = 0; p; p = p->next, i++) { info = (PtrInfo *)(p->data); if (strcmp (info->id, id) == 0) break; } g_return_if_fail (p != NULL); /* already at the requested position? */ if (pos == i || (pos == -1 && !p->next)) return; /* re-order the element */ bar->ptrlist = g_list_delete_link (bar->ptrlist, p); bar->ptrlist = g_list_insert (bar->ptrlist, info, pos); /* set the stacking order of the canvas item */ if (pos != -1) { /* raise to top and then lower to the given position */ gnome_canvas_item_raise_to_top (info->barptr); gnome_canvas_item_lower (info->barptr, pos); } else gnome_canvas_item_lower_to_bottom (info->barptr); swamigui_bar_update_ptr_heights (bar); /* update the pointer heights */ } /** * swamigui_bar_raise_pointer_to_top: * @bar: Bar canvas item * @id: String identifier of pointer to raise to top of stacking order * * Raise a pointer to the top of the stacking order. */ void swamigui_bar_raise_pointer_to_top (SwamiguiBar *bar, const char *id) { swamigui_bar_set_pointer_order (bar, id, 0); } /** * swamigui_bar_lower_pointer_to_bottom: * @bar: Bar canvas item * @id: String identifier of pointer to lower to bottom of stacking order * * Lower a pointer to the bottom of the stacking order. */ void swamigui_bar_lower_pointer_to_bottom (SwamiguiBar *bar, const char *id) { swamigui_bar_set_pointer_order (bar, id, -1); } swami-2.2.0/src/swamigui/SwamiguiBar.h000066400000000000000000000054511361104770400176420ustar00rootroot00000000000000/* * SwamiguiBar.h - Bar canvas item * A horizontal bar canvas item for displaying pointers or ranges. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_BAR_H__ #define __SWAMIGUI_BAR_H__ #include #include typedef struct _SwamiguiBar SwamiguiBar; typedef struct _SwamiguiBarClass SwamiguiBarClass; #include #define SWAMIGUI_TYPE_BAR (swamigui_bar_get_type ()) #define SWAMIGUI_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_BAR, SwamiguiBar)) #define SWAMIGUI_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_BAR, SwamiguiBarClass)) #define SWAMIGUI_IS_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_BAR)) #define SWAMIGUI_IS_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_BAR)) /* Bar Object */ struct _SwamiguiBar { GnomeCanvasGroup parent_instance; /* derived from GnomeCanvasGroup */ /*< private >*/ int min_height; /* minimum height of pointer items (top) */ int max_height; /* maximum height of pointer items (bottom) */ GList *ptrlist; /* list of PtrInfo structs (SwamiguiBar.c) */ }; struct _SwamiguiBarClass { GnomeCanvasGroupClass parent_class; }; GType swamigui_bar_get_type (void); void swamigui_bar_create_pointer (SwamiguiBar *bar, const char *id, const char *first_property_name, ...); void swamigui_bar_add_pointer (SwamiguiBar *bar, SwamiguiBarPtr *barptr, const char *id); GnomeCanvasItem *swamigui_bar_get_pointer (SwamiguiBar *bar, const char *id); void swamigui_bar_set_pointer_position (SwamiguiBar *bar, const char *id, int position); void swamigui_bar_set_pointer_range (SwamiguiBar *bar, const char *id, int start, int end); int swamigui_bar_get_pointer_order (SwamiguiBar *bar, const char *id); void swamigui_bar_set_pointer_order (SwamiguiBar *bar, const char *id, int pos); void swamigui_bar_raise_pointer_to_top (SwamiguiBar *bar, const char *id); void swamigui_bar_lower_pointer_to_bottom (SwamiguiBar *bar, const char *id); #endif swami-2.2.0/src/swamigui/SwamiguiBarPtr.c000066400000000000000000000230471361104770400203240ustar00rootroot00000000000000/** * SECTION:SwamiguiBarPtr * @short_description: Canvas item used as a position or range indicator. * @see_also: #SwamiguiBar * * This canvas item is used by #SwamiguiBar to display position and range * indicators. A #SwamiguiBar is composed of one or more #SwamiguiBarPtr * items. */ /* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiguiBarPtr.h" #include "builtin_enums.h" #include "i18n.h" enum { PROP_0, PROP_WIDTH, /* width in pixels or rectangle and pointer */ PROP_HEIGHT, /* total height in pixels (including pointer if any) */ PROP_POINTER_HEIGHT, /* height of pointer (must be less than height) */ PROP_TYPE, PROP_INTERACTIVE, PROP_COLOR, PROP_LABEL, PROP_TOOLTIP }; static void swamigui_bar_ptr_class_init (SwamiguiBarPtrClass *klass); static void swamigui_bar_ptr_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_bar_ptr_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_bar_ptr_init (SwamiguiBarPtr *barptr); static void swamigui_bar_ptr_finalize (GObject *object); static void swamigui_bar_ptr_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags); static GObjectClass *parent_class = NULL; GType swamigui_bar_ptr_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiBarPtrClass), NULL, NULL, (GClassInitFunc) swamigui_bar_ptr_class_init, NULL, NULL, sizeof (SwamiguiBarPtr), 0, (GInstanceInitFunc) swamigui_bar_ptr_init, }; obj_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "SwamiguiBarPtr", &obj_info, 0); } return (obj_type); } static void swamigui_bar_ptr_class_init (SwamiguiBarPtrClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_bar_ptr_set_property; obj_class->get_property = swamigui_bar_ptr_get_property; obj_class->finalize = swamigui_bar_ptr_finalize; item_class->update = swamigui_bar_ptr_update; g_object_class_install_property (obj_class, PROP_WIDTH, g_param_spec_int ("width", _("Width"), _("Width in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_HEIGHT, g_param_spec_int ("height", _("Height"), _("Height in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_POINTER_HEIGHT, g_param_spec_int ("pointer-height", _("Pointer height"), _("Height of pointer in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_enum ("type", _("Type"), _("Pointer type"), SWAMIGUI_TYPE_BAR_PTR_TYPE, SWAMIGUI_BAR_PTR_POSITION, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_INTERACTIVE, g_param_spec_boolean ("interactive", _("Interactive"), _("Interactive"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_COLOR, g_param_spec_uint ("color", _("Color"), _("Color"), 0, G_MAXUINT, 0x00FFFFFF, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LABEL, g_param_spec_string ("label", _("Label"), _("Label"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TOOLTIP, g_param_spec_string ("tooltip", _("Tooltip"), _("Tooltip"), NULL, G_PARAM_READWRITE)); } static void swamigui_bar_ptr_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiBarPtr *barptr = SWAMIGUI_BAR_PTR (object); GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object); switch (property_id) { case PROP_WIDTH: barptr->width = g_value_get_int (value); gnome_canvas_item_request_update (item); break; case PROP_HEIGHT: barptr->height = g_value_get_int (value); gnome_canvas_item_request_update (item); break; case PROP_POINTER_HEIGHT: barptr->pointer_height = g_value_get_int (value); gnome_canvas_item_request_update (item); break; case PROP_TYPE: barptr->type = g_value_get_enum (value); gnome_canvas_item_request_update (item); break; case PROP_INTERACTIVE: barptr->interactive = g_value_get_boolean (value); break; case PROP_COLOR: barptr->color = g_value_get_uint (value); gnome_canvas_item_request_update (item); break; case PROP_LABEL: barptr->label = g_value_dup_string (value); gnome_canvas_item_request_update (item); break; case PROP_TOOLTIP: barptr->tooltip = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_bar_ptr_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiBarPtr *barptr = SWAMIGUI_BAR_PTR (object); switch (property_id) { case PROP_WIDTH: g_value_set_int (value, barptr->width); break; case PROP_HEIGHT: g_value_set_int (value, barptr->height); break; case PROP_POINTER_HEIGHT: g_value_set_int (value, barptr->pointer_height); break; case PROP_TYPE: g_value_set_enum (value, barptr->type); break; case PROP_INTERACTIVE: g_value_set_boolean (value, barptr->interactive); break; case PROP_COLOR: g_value_set_uint (value, barptr->color); break; case PROP_LABEL: g_value_set_string (value, barptr->label); break; case PROP_TOOLTIP: g_value_set_string (value, barptr->tooltip); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_bar_ptr_init (SwamiguiBarPtr *barptr) { barptr->type = SWAMIGUI_BAR_PTR_POSITION; barptr->interactive = TRUE; barptr->color = 0x00FFFFFF; barptr->label = NULL; barptr->tooltip = NULL; } static void swamigui_bar_ptr_finalize (GObject *object) { SwamiguiBarPtr *barptr = SWAMIGUI_BAR_PTR (object); g_free (barptr->label); g_free (barptr->tooltip); if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize)(object); } /** * swamigui_bar_ptr_new: * * Create a new bar pointer object for adding to a #SwamiguiBar object. * * Returns: New bar pointer with a refcount of 1 which the caller owns. */ GnomeCanvasItem * swamigui_bar_ptr_new (void) { return (GNOME_CANVAS_ITEM (g_object_new (SWAMIGUI_TYPE_BAR_PTR, NULL))); } /* update bar pointer graphic primitives */ static void swamigui_bar_ptr_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { SwamiguiBarPtr *barptr = SWAMIGUI_BAR_PTR (item); GnomeCanvasPoints *points; if (barptr->type == SWAMIGUI_BAR_PTR_RANGE) /* range pointer? */ { if (!barptr->rect) barptr->rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP (barptr), GNOME_TYPE_CANVAS_RECT, "x1", (gdouble)0.0, "y1", (gdouble)0.0, NULL); if (barptr->ptr) /* pointer rectangle not needed for range */ { gtk_object_destroy (GTK_OBJECT (barptr->ptr)); barptr->ptr = NULL; } g_object_set (barptr->rect, "x2", (double)(barptr->width), "y2", (double)(barptr->height), "fill-color-rgba", barptr->color, NULL); } else /* position pointer mode */ { int rheight = barptr->height - barptr->pointer_height; /* rectangle height */ if (!barptr->rect) barptr->rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP (barptr), GNOME_TYPE_CANVAS_RECT, "x1", (gdouble)0.0, "y1", (gdouble)0.0, NULL); if (!barptr->ptr) barptr->rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP (barptr), GNOME_TYPE_CANVAS_POLYGON, NULL); g_object_set (barptr->rect, "x2", (double)(barptr->width), "y2", (double)(rheight), "fill-color-rgba", barptr->color, NULL); points = gnome_canvas_points_new (3); /* configure as a triangle on the bottom of the rectangle */ points->coords[0] = 0; points->coords[1] = rheight; points->coords[2] = barptr->width / 2.0; points->coords[3] = barptr->height; points->coords[4] = barptr->width; points->coords[5] = rheight; g_object_set (barptr->ptr, "points", points, "fill-color-rgba", barptr->color, NULL); gnome_canvas_points_free (points); } if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) GNOME_CANVAS_ITEM_CLASS (parent_class)->update (item, affine, clip_path, flags); } swami-2.2.0/src/swamigui/SwamiguiBarPtr.h000066400000000000000000000047601361104770400203320ustar00rootroot00000000000000/* * SwamiguiBarPtr.h - Pointer object added to SwamiguiBar canvas items * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_BAR_PTR_H__ #define __SWAMIGUI_BAR_PTR_H__ #include #include typedef struct _SwamiguiBarPtr SwamiguiBarPtr; typedef struct _SwamiguiBarPtrClass SwamiguiBarPtrClass; #define SWAMIGUI_TYPE_BAR_PTR (swamigui_bar_ptr_get_type ()) #define SWAMIGUI_BAR_PTR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_BAR_PTR, SwamiguiBarPtr)) #define SWAMIGUI_BAR_PTR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_BAR_PTR, SwamiguiBarPtrClass)) #define SWAMIGUI_IS_BAR_PTR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_BAR_PTR)) #define SWAMIGUI_IS_BAR_PTR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_BAR_PTR)) /* Pointer type */ typedef enum { SWAMIGUI_BAR_PTR_POSITION, /* pointer position indicator */ SWAMIGUI_BAR_PTR_RANGE /* range selection */ } SwamiguiBarPtrType; /* Bar pointer object */ struct _SwamiguiBarPtr { GnomeCanvasGroup parent_instance; /*< private >*/ GnomeCanvasItem *rect; /* pointer rectangle */ GnomeCanvasItem *ptr; /* triangle pointer (if SWAMIGUI_BAR_POINTER) */ GnomeCanvasItem *icon; /* icon for this pointer */ int width; /* width in pixels */ int height; /* height in pixels */ int pointer_height; /* height of pointer in pixels */ SwamiguiBarPtrType type; /* pointer interface type */ gboolean interactive; /* TRUE if user can change this pointer */ guint32 color; char *label; char *tooltip; }; struct _SwamiguiBarPtrClass { GnomeCanvasGroupClass parent_class; }; GType swamigui_bar_ptr_get_type (void); GnomeCanvasItem *swamigui_bar_ptr_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiCanvasMod.c000066400000000000000000000455571361104770400210170ustar00rootroot00000000000000/* * SwamiguiCanvasMod.c - Zoom/Scroll canvas modulation object * Allows scroll/zoom of canvas as a user interface. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiCanvasMod.h" #include "marshals.h" enum { PROP_0, PROP_TIMEOUT_INTERVAL, /* timeout interval in msecs */ PROP_ABS_MIN_ZOOM, /* absolute minimum zoom value */ PROP_SNAP }; /* Equation used for zoom/scroll operations: * * val = CLAMP (mult * pow (inp, power) + ofs, min, max) * * val: Value to use as zoom multiplier or scroll offset amount (zoom multiplier * per second or scroll pixels per second) * mult: A linear multiplier factor * inp: Input source (event time interval for wheel, pixel distance from snap) * power: An exponential factor (1.0 = linear) * ofs: Offset amount * * swamigui_canvas_mod_handle_event() is called with canvas events. This * function checks for events related to zoom/scroll operations (middle * mouse click, mouse wheel, etc). A timeout is installed which sends * periodic updates for the zoom or scroll values using the "update" signal. * The "snap" signal is used for updating the X and/or Y snap lines, which * the user of SwamiguiCanvasZoom is responsible for. * * * Equation used for zoom multiplier to convert it to the timeout interval * from zoom value per second. This is done so that any changes to timeout * interval only change the number of visual updates per second, but doesn't * affect the transformation speed. * * frac_mult = pow (mult, interval) * * frac_mult: Fractional zoom multiplier to use for each timeout callback * mult: The zoom multiplier for 1 second interval (as calculated from above) * interval: The callback interval time in seconds * * * State machine of SwamiguiCanvasMod: * * snap_active: TRUE indicates that a zoom and/or scroll snap is in progress. * last_wheel_dir: When GDK_SCROLL_UP or GDK_SCROLL_DOWN then wheel zoom and/or * scroll is active * * When snap_active == TRUE the following variables are valid: * xsnap/ysnap: Original coordinates of snap (when mouse button was clicked) * cur_xsnap/cur_ysnap: Current coordinates of the mouse * cur_xchange/cur_ychange: Indicate if cur_xsnap/cur_ysnap have changed since * last timeout * * */ #define TIMEOUT_PRIORITY (G_PRIORITY_HIGH_IDLE + 40) #define DEFAULT_ZOOM_MODIFIER GDK_CONTROL_MASK #define DEFAULT_SCROLL_MODIFIER GDK_SHIFT_MASK #define DEFAULT_AXIS_MODIFIER GDK_MOD1_MASK #define DEFAULT_SNAP_BUTTON 2 /* snap mouse button */ #define DEFAULT_ACTION_ZOOM TRUE #define DEFAULT_ZOOM_DEF_AXIS SWAMIGUI_CANVAS_MOD_X #define DEFAULT_SCROLL_DEF_AXIS SWAMIGUI_CANVAS_MOD_X #define DEFAULT_ONE_WHEEL_TIME 250 /* initial wheel event 'inp' val */ #define DEFAULT_MIN_ZOOM 1.0000001 /* minimum zoom/sec */ #define DEFAULT_MAX_ZOOM 1000000000.0 /* maximum zoom/sec */ #define DEFAULT_MIN_SCROLL 1.0 /* minimim scroll/sec */ #define DEFAULT_MAX_SCROLL 100000.0 /* maximum scroll/sec */ #define DEFAULT_TIMEOUT_INTERVAL 20 /* timeout interval in msecs */ #define DEFAULT_WHEEL_TIMEOUT 250 /* wheel taper timeout in msecs */ static const SwamiguiCanvasModVars default_vars[SWAMIGUI_CANVAS_MOD_AXIS_COUNT][SWAMIGUI_CANVAS_MOD_TYPE_COUNT] = { /* { mult, power, ofs } */ { /* { X } */ { 0.5, 4.0, 1.0 }, /* MOD_SNAP_ZOOM */ { 1.0, 2.2, 5.0 }, /* MOD_WHEEL_ZOOM */ { 10.0, 1.8, 200.0 }, /* MOD_SNAP_SCROLL */ { 0.6, 1.6, 400.0 } /* MOD_WHEEL_SCROLL */ }, { /* { Y } */ { 0.5, 4.0, 1.0 }, /* MOD_SNAP_ZOOM */ { 1.0, 2.2, 5.0 }, /* MOD_WHEEL_ZOOM */ { 10.0, 1.8, 200.0 }, /* MOD_SNAP_SCROLL */ { 0.6, 1.6, 400.0 } /* MOD_WHEEL_SCROLL */ } }; enum { UPDATE_SIGNAL, SNAP_SIGNAL, SIGNAL_COUNT }; /* an undefined scroll direction for last_wheel_dir field */ #define WHEEL_INACTIVE 0xFF static guint swamigui_canvas_mod_get_actions (SwamiguiCanvasMod *mod, guint state); static gboolean swamigui_canvas_mod_timeout (gpointer data); G_DEFINE_TYPE (SwamiguiCanvasMod, swamigui_canvas_mod, G_TYPE_OBJECT); static guint signals[SIGNAL_COUNT] = { 0 }; static void swamigui_canvas_mod_class_init (SwamiguiCanvasModClass *klass) { signals[UPDATE_SIGNAL] = g_signal_new ("update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (SwamiguiCanvasModClass, update), NULL, NULL, swamigui_marshal_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE_DOUBLE_DOUBLE, G_TYPE_NONE, 6, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); signals[SNAP_SIGNAL] = g_signal_new ("snap", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (SwamiguiCanvasModClass, snap), NULL, NULL, swamigui_marshal_VOID__UINT_DOUBLE_DOUBLE, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_DOUBLE, G_TYPE_DOUBLE); } static void swamigui_canvas_mod_init (SwamiguiCanvasMod *mod) { mod->zoom_modifier = DEFAULT_ZOOM_MODIFIER; mod->scroll_modifier = DEFAULT_SCROLL_MODIFIER; mod->axis_modifier = DEFAULT_AXIS_MODIFIER; mod->snap_button = DEFAULT_SNAP_BUTTON; mod->def_action_zoom = DEFAULT_ACTION_ZOOM; mod->def_zoom_axis = DEFAULT_ZOOM_DEF_AXIS; mod->def_scroll_axis = DEFAULT_SCROLL_DEF_AXIS; mod->one_wheel_time = DEFAULT_ONE_WHEEL_TIME; mod->min_zoom = DEFAULT_MIN_ZOOM; mod->max_zoom = DEFAULT_MAX_ZOOM; mod->min_scroll = DEFAULT_MIN_SCROLL; mod->max_scroll = DEFAULT_MAX_SCROLL; mod->timeout_interval = DEFAULT_TIMEOUT_INTERVAL; mod->wheel_timeout = DEFAULT_WHEEL_TIMEOUT; mod->last_wheel_dir = WHEEL_INACTIVE; memcpy (mod->vars, default_vars, sizeof (SwamiguiCanvasModVars) * SWAMIGUI_CANVAS_MOD_AXIS_COUNT * SWAMIGUI_CANVAS_MOD_TYPE_COUNT); } /** * swamigui_canvas_mod_new: * * Create a new canvas zoom/scroll modulator object. This object is used for * handling canvas events and intercepting those which apply to zooming or * scrolling operations by the user. * * Returns: New canvas modulator object with a refcount of 1 which the caller * owns. */ SwamiguiCanvasMod * swamigui_canvas_mod_new (void) { return (SWAMIGUI_CANVAS_MOD (g_object_new (SWAMIGUI_TYPE_CANVAS_MOD, NULL))); } /** * swamigui_canvas_mod_set_vars: * @mod: Canvas zoom/scroll modulator * @axis: Axis of variables to assign (X or Y) * @type: Modulator type to assign to (snap zoom, snap scroll, wheel zoom or * wheel scroll) * @mult: Multiplier value to assign to equation * @power: Power value to assign to equation * @ofs: Offset value to assign to equation * * Assigns equation variables for a specific modulator and axis. */ void swamigui_canvas_mod_set_vars (SwamiguiCanvasMod *mod, SwamiguiCanvasModAxis axis, SwamiguiCanvasModType type, double mult, double power, double ofs) { g_return_if_fail (SWAMIGUI_IS_CANVAS_MOD (mod)); g_return_if_fail (axis >= 0 && axis < SWAMIGUI_CANVAS_MOD_AXIS_COUNT); g_return_if_fail (type >= 0 && type < SWAMIGUI_CANVAS_MOD_TYPE_COUNT); mod->vars[axis][type].mult = mult; mod->vars[axis][type].power = power; mod->vars[axis][type].ofs = ofs; } /** * swamigui_canvas_mod_get_vars: * @mod: Canvas zoom/scroll modulator * @axis: Axis of variables to get (X or Y) * @type: Modulator type to get vars from (snap zoom, snap scroll, wheel zoom or * wheel scroll) * @mult: Location to store multiplier value of equation or %NULL * @power: Location to store power value of equation or %NULL * @ofs: Location to store offset value of equation or %NULL * * Gets equation variables for a specific modulator and axis. */ void swamigui_canvas_mod_get_vars (SwamiguiCanvasMod *mod, SwamiguiCanvasModAxis axis, SwamiguiCanvasModType type, double *mult, double *power, double *ofs) { g_return_if_fail (SWAMIGUI_IS_CANVAS_MOD (mod)); g_return_if_fail (axis >= 0 && axis < SWAMIGUI_CANVAS_MOD_AXIS_COUNT); g_return_if_fail (type >= 0 && type < SWAMIGUI_CANVAS_MOD_TYPE_COUNT); if (mult) *mult = mod->vars[axis][type].mult; if (power) *power = mod->vars[axis][type].power; if (ofs) *ofs = mod->vars[axis][type].ofs; } /** * swamigui_canvas_mod_handle_event: * @mod: Canvas zoom/scroll modulator instance * @event: Canvas event to handle * * Processes canvas events and handles events related to zoom/scroll * actions. * * Returns: %TRUE if event was a zoom/scroll related event (was handled), * %FALSE otherwise */ gboolean swamigui_canvas_mod_handle_event (SwamiguiCanvasMod *mod, GdkEvent *event) { GdkEventMotion *motion_event; GdkEventButton *btn_event; GdkEventScroll *scroll_event; guint actions; switch (event->type) { case GDK_BUTTON_PRESS: /* button press only applies to snap */ btn_event = (GdkEventButton *)event; if (btn_event->button != mod->snap_button) return (FALSE); mod->snap_active = TRUE; mod->xsnap = (int)btn_event->x; mod->cur_xsnap = (int)btn_event->x; mod->cur_xchange = TRUE; mod->ysnap = (int)btn_event->y; mod->cur_ysnap = (int)btn_event->y; mod->cur_ychange = TRUE; /* add a timeout callback for zoom/scroll if not already added */ if (!mod->timeout_handler) { mod->timeout_handler = g_timeout_add_full (TIMEOUT_PRIORITY, mod->timeout_interval, swamigui_canvas_mod_timeout, mod, NULL); } actions = swamigui_canvas_mod_get_actions (mod, btn_event->state); g_signal_emit (mod, signals[SNAP_SIGNAL], 0, actions, (double)(mod->xsnap), (double)(mod->ysnap)); break; case GDK_MOTION_NOTIFY: /* motion applies only to snap */ if (!mod->snap_active) return (FALSE); motion_event = (GdkEventMotion *)event; if (mod->cur_xsnap != motion_event->x) { mod->cur_xsnap = (int)motion_event->x; mod->cur_xchange = TRUE; } if (mod->cur_ysnap != motion_event->y) { mod->cur_ysnap = (int)motion_event->y; mod->cur_ychange = TRUE; } break; case GDK_BUTTON_RELEASE: /* button release only applies to snap */ if (!mod->snap_active || ((GdkEventButton *)event)->button != mod->snap_button) return (FALSE); mod->snap_active = FALSE; /* remove timeout if wheel not active */ if (mod->last_wheel_dir == WHEEL_INACTIVE && mod->timeout_handler) { g_source_remove (mod->timeout_handler); mod->timeout_handler = 0; } g_signal_emit (mod, signals[SNAP_SIGNAL], 0, 0, mod->xsnap, mod->ysnap); break; case GDK_SCROLL: /* mouse wheel zooming */ scroll_event = (GdkEventScroll *)event; if (scroll_event->direction != GDK_SCROLL_UP && scroll_event->direction != GDK_SCROLL_DOWN) return (FALSE); /* wheel was previously scrolled in the other direction? - Stop immediately */ if (mod->last_wheel_dir != WHEEL_INACTIVE && mod->last_wheel_dir != scroll_event->direction) { /* remove timeout handler if snap is not also active */ if (!mod->snap_active && mod->timeout_handler) { g_source_remove (mod->timeout_handler); mod->timeout_handler = 0; } mod->last_wheel_dir = WHEEL_INACTIVE; return (TRUE); } /* wheel not yet scrolled? */ if (mod->last_wheel_dir == WHEEL_INACTIVE) { mod->last_wheel_dir = scroll_event->direction; mod->wheel_time = mod->one_wheel_time; mod->xwheel = (int)scroll_event->x; mod->ywheel = (int)scroll_event->y; if (!mod->timeout_handler) { mod->timeout_handler = g_timeout_add_full (TIMEOUT_PRIORITY, mod->timeout_interval, swamigui_canvas_mod_timeout, mod, NULL); } } /* wheel previously scrolled in the same direction */ else mod->wheel_time = scroll_event->time - mod->last_wheel_time; /* get current time for wheel timeout tapering (can't use GDK event time * since there doesn't seem to be a way to arbitrarily get current value) */ g_get_current_time (&mod->last_wheel_real_time); mod->last_wheel_time = scroll_event->time; break; default: return (FALSE); } return (TRUE); } static guint swamigui_canvas_mod_get_actions (SwamiguiCanvasMod *mod, guint state) { gboolean zoom_mod, scroll_mod, axis_mod; guint actions = 0; zoom_mod = (state & mod->zoom_modifier) != 0; scroll_mod = (state & mod->scroll_modifier) != 0; axis_mod = (state & mod->axis_modifier) != 0; /* if ZOOM and SCROLL modifier not active - use default action */ if (!zoom_mod && !scroll_mod) { if (mod->def_action_zoom) zoom_mod = TRUE; else scroll_mod = TRUE; } if (zoom_mod) actions |= 1 << (mod->def_zoom_axis ^ axis_mod); if (scroll_mod) actions |= 1 << ((mod->def_scroll_axis ^ axis_mod) + 2); return (actions); } /* equation calculation for the zoom/scroll operation */ static double calc_val (SwamiguiCanvasMod *mod, double inp, SwamiguiCanvasModAxis axis, SwamiguiCanvasModType type) { SwamiguiCanvasModVars *vars; double val; vars = &mod->vars[axis][type]; val = vars->mult * pow (inp, vars->power) + vars->ofs; return (val); } /* timeout handler which is called at regular intervals to update active * zoom and or scroll */ static gboolean swamigui_canvas_mod_timeout (gpointer data) { SwamiguiCanvasMod *mod = SWAMIGUI_CANVAS_MOD (data); GdkModifierType state; GTimeVal curtime; int lastwheel; /* last wheel event time in milliseconds */ double wheeltaper; /* taper value for wheel timeout */ double val, inp, inpy; double xpos, ypos; guint actions; int wheel_timeout = mod->wheel_timeout; /* get current keyboard modifier state and resulting actions */ gdk_display_get_pointer (gdk_display_get_default (), NULL, NULL, NULL, &state); actions = swamigui_canvas_mod_get_actions (mod, state); mod->xzoom_amt = 1.0; mod->yzoom_amt = 1.0; mod->xscroll_amt = 0.0; mod->yscroll_amt = 0.0; /* mouse wheel operation is active */ if (mod->last_wheel_dir != WHEEL_INACTIVE) { inp = wheel_timeout - mod->wheel_time; inp = CLAMP (inp, 0.0, wheel_timeout); /* calculate the last wheel event time in milliseconds, unfortunately I * don't think we can get the current GDK event time, so we have to use * g_get_current_time(). */ g_get_current_time (&curtime); lastwheel = (curtime.tv_sec - mod->last_wheel_real_time.tv_sec) * 1000; if (curtime.tv_usec > mod->last_wheel_real_time.tv_usec) lastwheel += (curtime.tv_usec - mod->last_wheel_real_time.tv_usec + 500) / 1000; else lastwheel -= (mod->last_wheel_real_time.tv_usec - curtime.tv_usec + 500) / 1000; /* At end of wheel activity timeout? */ if (lastwheel >= wheel_timeout) { mod->last_wheel_dir = WHEEL_INACTIVE; if (mod->snap_active) return (TRUE); /* keep timeout if snap active */ mod->timeout_handler = 0; return (FALSE); } /* wheel timeout taper multiplier (1.0 to 0.0) */ wheeltaper = 1.0 - (wheel_timeout - lastwheel) / (double)(wheel_timeout); if (actions & SWAMIGUI_CANVAS_MOD_ZOOM_X) { val = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_X, SWAMIGUI_CANVAS_MOD_WHEEL_ZOOM); mod->xzoom_amt = 1.0 + (val - 1.0) * wheeltaper; } if (actions & SWAMIGUI_CANVAS_MOD_ZOOM_Y) { val = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_Y, SWAMIGUI_CANVAS_MOD_WHEEL_ZOOM); mod->yzoom_amt = 1.0 + (val - 1.0) * wheeltaper; } if (actions & SWAMIGUI_CANVAS_MOD_SCROLL_X) { val = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_X, SWAMIGUI_CANVAS_MOD_WHEEL_SCROLL); mod->xscroll_amt = val * wheeltaper; } if (actions & SWAMIGUI_CANVAS_MOD_SCROLL_Y) { val = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_Y, SWAMIGUI_CANVAS_MOD_WHEEL_SCROLL); mod->yscroll_amt = val * wheeltaper; } /* set "direction" of values depending on wheel direction */ if (mod->last_wheel_dir == GDK_SCROLL_DOWN) { mod->xzoom_amt = 1.0 / mod->xzoom_amt; mod->yzoom_amt = 1.0 / mod->yzoom_amt; mod->xscroll_amt = -mod->xscroll_amt; /* y scroll is already inverted */ } else mod->yscroll_amt = -mod->yscroll_amt; /* yscroll is inverted */ xpos = mod->xwheel; ypos = mod->ywheel; } else /* snap is active */ { inp = ABS (mod->cur_xsnap - mod->xsnap); inpy = ABS (mod->cur_ysnap - mod->ysnap); /* short circuit 0 case (but keep timeout) */ if (inp == 0 && inpy == 0) return (TRUE); if (actions & SWAMIGUI_CANVAS_MOD_ZOOM_X) mod->xzoom_amt = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_X, SWAMIGUI_CANVAS_MOD_SNAP_ZOOM); if (actions & SWAMIGUI_CANVAS_MOD_ZOOM_Y) mod->yzoom_amt = calc_val (mod, inpy, SWAMIGUI_CANVAS_MOD_Y, SWAMIGUI_CANVAS_MOD_SNAP_ZOOM); if (actions & SWAMIGUI_CANVAS_MOD_SCROLL_X) mod->xscroll_amt = calc_val (mod, inp, SWAMIGUI_CANVAS_MOD_X, SWAMIGUI_CANVAS_MOD_SNAP_SCROLL); if (actions & SWAMIGUI_CANVAS_MOD_SCROLL_Y) mod->yscroll_amt = calc_val (mod, inpy, SWAMIGUI_CANVAS_MOD_Y, SWAMIGUI_CANVAS_MOD_SNAP_SCROLL); /* set "direction" of values depending on snap */ if (mod->cur_xsnap < mod->xsnap) { mod->xzoom_amt = 1.0 / mod->xzoom_amt; mod->yzoom_amt = 1.0 / mod->yzoom_amt; mod->xscroll_amt = -mod->xscroll_amt; /* y scroll is already inverted */ } else mod->yscroll_amt = -mod->yscroll_amt; /* yscroll is inverted */ xpos = mod->xsnap; ypos = mod->ysnap; } if (mod->xzoom_amt != 1.0 || mod->yzoom_amt != 1.0 || mod->xscroll_amt != 0.0 || mod->yscroll_amt != 0.0) { double interval = mod->timeout_interval / 1000.0; /* factor in interval so that zoom/scroll amounts are the same rate * regardless of timeout interval */ mod->xzoom_amt = pow (mod->xzoom_amt, interval); mod->yzoom_amt = pow (mod->yzoom_amt, interval); mod->xscroll_amt *= interval; mod->yscroll_amt *= interval; /* printf ("Update: xzoom:%f yzoom:%f xscroll:%f yscroll:%f xpos:%f ypos:%f\n", mod->xzoom_amt, mod->yzoom_amt, mod->xscroll_amt, mod->yscroll_amt, xpos, ypos); */ g_signal_emit (mod, signals[UPDATE_SIGNAL], 0, mod->xzoom_amt, mod->yzoom_amt, mod->xscroll_amt, mod->yscroll_amt, xpos, ypos); } /* keep timeout if wheel is active or snap is active */ if (mod->last_wheel_dir != WHEEL_INACTIVE || mod->snap_active) return (TRUE); mod->timeout_handler = 0; return (FALSE); } swami-2.2.0/src/swamigui/SwamiguiCanvasMod.h000066400000000000000000000133701361104770400210100ustar00rootroot00000000000000/* * SwamiguiCanvasMod.h - Zoom/Scroll canvas modulation object * Allows scroll/zoom of canvas as a user interface. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_CANVAS_MOD_H__ #define __SWAMIGUI_CANVAS_MOD_H__ #include #include typedef struct _SwamiguiCanvasMod SwamiguiCanvasMod; typedef struct _SwamiguiCanvasModClass SwamiguiCanvasModClass; #define SWAMIGUI_TYPE_CANVAS_MOD (swamigui_canvas_mod_get_type ()) #define SWAMIGUI_CANVAS_MOD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_CANVAS_MOD, \ SwamiguiCanvasMod)) #define SWAMIGUI_CANVAS_MOD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_CANVAS_MOD, \ SwamiguiCanvasModClass)) #define SWAMIGUI_IS_CANVAS_MOD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_CANVAS_MOD)) #define SWAMIGUI_IS_CANVAS_MOD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_CANVAS_MOD)) /* Canvas modulation type */ typedef enum { SWAMIGUI_CANVAS_MOD_SNAP_ZOOM, /* snap line zoom */ SWAMIGUI_CANVAS_MOD_WHEEL_ZOOM, /* wheel zoom */ SWAMIGUI_CANVAS_MOD_SNAP_SCROLL, /* snap line scroll */ SWAMIGUI_CANVAS_MOD_WHEEL_SCROLL /* wheel scroll */ } SwamiguiCanvasModType; #define SWAMIGUI_CANVAS_MOD_TYPE_COUNT 4 /* axis enum */ typedef enum { SWAMIGUI_CANVAS_MOD_X, SWAMIGUI_CANVAS_MOD_Y } SwamiguiCanvasModAxis; #define SWAMIGUI_CANVAS_MOD_AXIS_COUNT 2 typedef enum { SWAMIGUI_CANVAS_MOD_ENABLED = 1 << 0 /* TRUE if modulator is enabled */ } SwamiguiCanvasModFlags; /* Modifier action flags */ typedef enum { SWAMIGUI_CANVAS_MOD_ZOOM_X = 1 << 0, SWAMIGUI_CANVAS_MOD_ZOOM_Y = 1 << 1, SWAMIGUI_CANVAS_MOD_SCROLL_X = 1 << 2, SWAMIGUI_CANVAS_MOD_SCROLL_Y = 1 << 3 } SwamiguiCanvasModActions; /* Variables used for zoom/scroll equation */ typedef struct { double mult; double power; double ofs; } SwamiguiCanvasModVars; /* canvas zoom object */ struct _SwamiguiCanvasMod { GObject parent_instance; guint zoom_modifier; /* GdkModifierType for zooming */ guint scroll_modifier; /* GdkModifierType for scrolling */ guint axis_modifier; /* GdkModifierType to toggle axis */ guint8 snap_button; /* snap mouse button # (1-5) */ guint8 def_action_zoom; /* TRUE: Zoom = def, FALSE: Scroll = def */ guint8 def_zoom_axis; /* default SwamiguiCanvasModAxis for zoom */ guint8 def_scroll_axis; /* default SwamiguiCanvasModAxis for scroll */ guint32 one_wheel_time; /* time in msecs for first mouse wheel event */ double min_zoom; /* minimum zoom value */ double max_zoom; /* maximum zoom value */ double min_scroll; /* minimum scroll value */ double max_scroll; /* maximum scroll value */ /* variables for [modulator][axis] scroll/zoom equations */ SwamiguiCanvasModVars vars[SWAMIGUI_CANVAS_MOD_AXIS_COUNT][SWAMIGUI_CANVAS_MOD_TYPE_COUNT]; guint timeout_handler; /* timeout callback handler ID */ guint timeout_interval; /* timeout handler interval in msecs */ guint wheel_timeout; /* inactive time in msecs of wheel till stop */ /* for mouse wheel zooming */ GdkScrollDirection last_wheel_dir; /* last wheel direction (UP/DOWN/INACTIVE) */ guint32 last_wheel_time; /* last event time stamp of mouse wheel */ guint32 wheel_time; /* current wheel time value (between events) */ int xwheel; /* X coordinate of last wheel event */ int ywheel; /* Y coordinate of last wheel event */ /* also stores the last wheel time, but in clock time since it doesn't appear * we can arbitrarily load the #&^@ing GDK event time in the timeout callback */ GTimeVal last_wheel_real_time; gboolean snap_active; /* TRUE if snap is active */ int xsnap; /* X coordinate of snap */ int ysnap; /* Y coordinate of snap */ int cur_xsnap; /* current X coordinate */ int cur_ysnap; /* current Y coordinate */ gboolean cur_xchange; /* X coordinate has changed? (needs refresh) */ gboolean cur_ychange; /* Y coordinate has changed? (needs refresh) */ double xzoom_amt; /* current X zoom amount */ double yzoom_amt; /* current Y zoom amount */ double xscroll_amt; /* current X scroll amount */ double yscroll_amt; /* current Y scroll amount */ }; struct _SwamiguiCanvasModClass { GObjectClass parent_class; /* zoom/scroll update signal (zoom = 1.0 or scroll = 0.0 for no change) */ void (* update)(SwamiguiCanvasMod *mod, double xzoom, double yzoom, double xscroll, double yscroll, double xpos, double ypos); void (* snap)(SwamiguiCanvasMod *mod, guint actions, double xsnap, double ysnap); }; GType swamigui_canvas_mod_get_type (void); SwamiguiCanvasMod *swamigui_canvas_mod_new (void); void swamigui_canvas_mod_set_vars (SwamiguiCanvasMod *mod, SwamiguiCanvasModAxis axis, SwamiguiCanvasModType type, double mult, double power, double ofs); void swamigui_canvas_mod_get_vars (SwamiguiCanvasMod *mod, SwamiguiCanvasModAxis axis, SwamiguiCanvasModType type, double *mult, double *power, double *ofs); gboolean swamigui_canvas_mod_handle_event (SwamiguiCanvasMod *mod, GdkEvent *event); #endif swami-2.2.0/src/swamigui/SwamiguiComboEntry.c000066400000000000000000000053761361104770400212200ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "SwamiguiComboEntry.h" #include "i18n.h" #if GTK_COMBO_HAS_ENTRY static GObject *swamigui_combo_entry_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties); #endif /* define the combo box entry type, depending on the Gtk version */ #if GTK_COMBO_HAS_ENTRY G_DEFINE_TYPE (SwamiguiComboEntry, swamigui_combo_entry, GTK_TYPE_COMBO_BOX_TEXT); #else G_DEFINE_TYPE (SwamiguiComboEntry, swamigui_combo_entry, GTK_TYPE_COMBO_BOX_ENTRY); #endif static void swamigui_combo_entry_class_init (SwamiguiComboEntryClass *klass) { #if GTK_COMBO_HAS_ENTRY GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->constructor = swamigui_combo_entry_constructor; #endif } #if GTK_COMBO_HAS_ENTRY static GObject * swamigui_combo_entry_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties) { GObjectConstructParam *property; GObject *object; guint i; gchar const *name; // Kind of hackish no? G_PARAM_CONSTRUCT_ONLY properties must be overriden here. for (i = 0, property = properties; i < n_properties; i++, property++) { name = g_param_spec_get_name (property->pspec); if (strcmp (name, "has-entry") == 0) { g_value_set_boolean (property->value, TRUE); break; } } object = G_OBJECT_CLASS (swamigui_combo_entry_parent_class)->constructor (gtype, n_properties, properties); g_object_set (object, "entry-text-column", 0, NULL); return object; } #endif static void swamigui_combo_entry_init (SwamiguiComboEntry *combo) { #if !GTK_COMBO_HAS_ENTRY g_object_set (combo, "text-column", 0, NULL); #endif } /** * swamigui_combo_entry_new: * * Create a new combo box entry widget. * * Returns: New combo box entry widget. */ GtkWidget * swamigui_combo_entry_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_COMBO_ENTRY, NULL))); } swami-2.2.0/src/swamigui/SwamiguiComboEntry.h000066400000000000000000000042001361104770400212060ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_COMBO_ENTRY_H__ #define __SWAMIGUI_COMBO_ENTRY_H__ #include #include typedef struct _SwamiguiComboEntry SwamiguiComboEntry; typedef struct _SwamiguiComboEntryClass SwamiguiComboEntryClass; #define SWAMIGUI_TYPE_COMBO_ENTRY (swamigui_combo_entry_get_type ()) #define SWAMIGUI_COMBO_ENTRY(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_COMBO_ENTRY, SwamiguiComboEntry)) #define SWAMIGUI_COMBO_ENTRY_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_COMBO_ENTRY, \ SwamiguiComboEntryClass)) #define SWAMIGUI_IS_COMBO_ENTRY(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_COMBO_ENTRY)) #define SWAMIGUI_IS_COMBO_ENTRY_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_COMBO_ENTRY)) // Check if Gtk is 2.24.0 or greater (where "has entry" support and GtkComboBoxText was added) #define GTK_COMBO_HAS_ENTRY GTK_CHECK_VERSION(2, 24, 0) /* Combo entry widget */ struct _SwamiguiComboEntry { #if GTK_COMBO_HAS_ENTRY GtkComboBoxText parent_instance; #else GtkComboBoxEntry parent_instance; #endif }; /* Combo entry class */ struct _SwamiguiComboEntryClass { #if GTK_COMBO_HAS_ENTRY GtkComboBoxTextClass parent_class; #else GtkComboBoxEntryClass parent_class; #endif }; GType swamigui_combo_entry_get_type (void); GtkWidget *swamigui_combo_entry_new (); #endif swami-2.2.0/src/swamigui/SwamiguiControl.c000066400000000000000000000543571361104770400205620ustar00rootroot00000000000000/* * SwamiguiControl.c - GUI control system * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiControl.h" #include "SwamiguiRoot.h" #include "libswami/swami_priv.h" /* * Notes about SwamiguiControl * * When a control is attached to a widget the following occurs: * - widget holds a reference on control (widget->control) * - widget uses g_object_set_data_full to associate control to widget, * GDestroyNotify handler calls swami_control_disconnect_unref() on control * - control has a reference on widget * - widget "destroy" signal is caught by control to release reference * * When gtk_object_destroy() is called on the widget the following happens: * - "destroy" signal is caught by widget control handler which then NULLifies * any pointers to widget and removes its reference to the widget * - If all references have been removed from widget, it is finalized * - GDestroyNotify is called from widget's finalize function which calls * swami_control_disconnect_unref() which disconnects control and unrefs it * - Control is finalized if there are no more external references * * When writing new SwamiguiControl handlers its important to note that the * control network may be operating in a multi-thread environment, while the * GUI is single threaded. For this reason controls are added to the GUI * queue, which causes all events to be processed from within the GUI thread. * This means that even after a widget has been destroyed there may still be * queued control events. For this reason its important to lock the control * in value set/get callbacks and check if the widget is still alive and handle * the case where it has been destroyed (ignore events usually). * * One caveat of this is that currently a control cannot be removed from a * widget. The widget must be destroyed. Since there is a 1 to 1 mapping of * a widget and its control, this shouldn't really be a problem. */ #define CONTROL_TABLE_ROW_SPACING 2 #define CONTROL_TABLE_COLUMN_SPACING 4 typedef struct { GType widg_type; GType value_type; int flags; /* rank and control/view flags */ SwamiguiControlHandler handler; } HandlerInfo; static int swamigui_control_GCompare_type (gconstpointer a, gconstpointer b); static int swamigui_control_GCompare_rank (gconstpointer a, gconstpointer b); G_LOCK_DEFINE_STATIC (control_handlers); static GList *control_handlers = NULL; /* quark used to associate a control to a widget using g_object_set_qdata */ GQuark swamigui_control_quark = 0; void _swamigui_control_init (void) { swamigui_control_quark = g_quark_from_static_string ("_SwamiguiControl"); } /** * swamigui_control_new: * @type: A #SwamiControl derived GType of control to create * * Create a control of the given @type which should be a * #SwamiControl derived type. The created control is automatically added * to the #SwamiguiRoot GUI control event queue. Just a convenience function * really. * * Returns: New control with a refcount of 1 which the caller owns. */ SwamiControl * swamigui_control_new (GType type) { SwamiControl *control; g_return_val_if_fail (g_type_is_a (type, SWAMI_TYPE_CONTROL), NULL); control = g_object_new (type, NULL); if (control) swamigui_control_set_queue (control); return (control); } /** * swamigui_control_new_for_widget: * @widget: GUI widget to create a control for * * Creates a new control for a GUI widget. Use * swami_control_new_for_widget_full() for additional parameters. * * Returns: The new control or %NULL if @widget not handled. * The returned control does NOT have a reference since the @widget is * the owner of the control. Destroying the @widget will cause the control * to be disconnected and unref'd, if there are no more references the * control will be freed. */ SwamiControl * swamigui_control_new_for_widget (GObject *widget) { g_return_val_if_fail (G_IS_OBJECT (widget), NULL); return (swamigui_control_new_for_widget_full (widget, 0, NULL, 0)); } /** * swamigui_control_new_for_widget_full: * @widget: GUI widget to control * @value_type: Control value type or 0 for default * @pspec: Parameter spec to define valid ranges and other parameters, * should be a GParamSpec of @value_type or %NULL for defaults * @flags: Flags for creating the new control. To create a display only * control just the #SWAMIGUI_CONTROL_VIEW flag can be specified, 0 means * both control and view (#SWAMIGUI_CONTROL_CTRLVIEW). * * Creates a new control for a GUI widget, provided there is a registered * handler for the @widget type/value type combination. The new control is * automatically assigned to the GUI queue in #swamigui_root. A widget's * control can be retrieved with swamigui_control_lookup(). * If the given @widget already has a control it is returned. The @pspec * parameter allows for additional settings to be applied to the @widget * and/or control (such as a valid range or max string length, etc). * * Returns: The new control or %NULL if @widget/@value_type not handled. * The returned control does NOT have a reference since the @widget is * the owner of the control. Destroying the @widget will cause the control * to be disconnected and unref'd, if there are no more references the * control will be freed. */ SwamiControl * swamigui_control_new_for_widget_full (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiguiControlHandler hfunc = NULL; SwamiControl *control = NULL; GType cmp_widg_type, cmp_value_type; HandlerInfo *hinfo, *bestmatch = NULL; GList *p; g_return_val_if_fail (G_IS_OBJECT (widget), NULL); g_return_val_if_fail (!pspec || G_IS_PARAM_SPEC (pspec), NULL); /* return existing control (if any) */ control = g_object_get_qdata (widget, swamigui_control_quark); if (control) return (control); value_type = swamigui_control_get_alias_value_type (value_type); /* doesn't make sense to request control only */ flags &= SWAMIGUI_CONTROL_CTRLVIEW; if (!(flags & SWAMIGUI_CONTROL_VIEW)) flags = SWAMIGUI_CONTROL_CTRLVIEW; cmp_widg_type = G_OBJECT_TYPE (widget); switch (G_TYPE_FUNDAMENTAL (value_type)) { case G_TYPE_ENUM: case G_TYPE_FLAGS: cmp_value_type = G_TYPE_FUNDAMENTAL (value_type); break; default: cmp_value_type = value_type; break; } G_LOCK (control_handlers); /* loop over widget control handlers */ for (p = control_handlers; p; p = p->next) { hinfo = (HandlerInfo *)(p->data); /* widget type does not match? - Skip */ if (cmp_widg_type != hinfo->widg_type) continue; /* if wildcard value type or handler type matches compare type, we found it */ if (!cmp_value_type || cmp_value_type == hinfo->value_type) break; /* check if compare value and param can be converted to that of the handlers */ if (G_TYPE_IS_VALUE_TYPE (cmp_value_type) && G_TYPE_IS_VALUE_TYPE (hinfo->value_type) && g_value_type_transformable (cmp_value_type, hinfo->value_type)) // && swami_param_type_transformable_value (cmp_value_type, hinfo->value_type)) { /* check if this match is the best so far */ if (!bestmatch || ((bestmatch->flags & SWAMIGUI_CONTROL_RANK_MASK) < (hinfo->flags & SWAMIGUI_CONTROL_RANK_MASK))) bestmatch = hinfo; } } if (!p && bestmatch) hfunc = bestmatch->handler; else if (p) hfunc = ((HandlerInfo *)(p->data))->handler; G_UNLOCK (control_handlers); if (hfunc) control = hfunc (widget, value_type, pspec, flags); /* ++ ref new ctrl */ if (control) swamigui_control_set_queue (control); /* add to GUI queue */ /* associate control to the widget (!! widget takes over reference) */ g_object_set_qdata_full (widget, swamigui_control_quark, control, (GDestroyNotify)swami_control_disconnect_unref); return (control); } /** * swamigui_control_lookup: * @widget: User interface widget to lookup the #SwamiControl of * * Returns: The associated #SwamiControl or NULL if none. The return control * is NOT referenced for the caller (don't unref it). */ SwamiControl * swamigui_control_lookup (GObject *widget) { SwamiControl *control; g_return_val_if_fail (G_IS_OBJECT (widget), NULL); control = g_object_get_qdata (widget, swamigui_control_quark); if (!control) return (NULL); return (SWAMI_CONTROL (control)); /* do a type cast for debugging purposes */ } /** * swamigui_control_prop_connect_widget: * @object: Object with property to connect * @propname: Property of @object to connect * @widg: Widget to control object property * * A convenience function which connects a widget as a control for a given * @object property. Use swamigui_control_prop_connect_widget_full() for * additional options. */ void swamigui_control_prop_connect_widget (GObject *object, const char *propname, GObject *widget) { SwamiControl *propctrl; SwamiControl *widgctrl; GParamSpec *pspec; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (propname != NULL); g_return_if_fail (G_IS_OBJECT (widget)); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), propname); g_return_if_fail (pspec != NULL); propctrl = swami_get_control_prop (object, pspec); /* ++ ref */ g_return_if_fail (propctrl != NULL); /* create widget control using value type from pspec and view only if * read only property */ widgctrl = swamigui_control_new_for_widget_full (widget, G_PARAM_SPEC_VALUE_TYPE (pspec), NULL, (pspec->flags & G_PARAM_READWRITE) == G_PARAM_READABLE ? SWAMIGUI_CONTROL_VIEW : 0); if (swami_log_if_fail (widgctrl != NULL)) { g_object_unref (propctrl); /* -- unref */ return; } swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR_SPEC_INIT); g_object_unref (propctrl); /* -- unref */ } /** * swamigui_control_create_widget: * @widg_type: A base type of new widget (GtkWidget, GnomeCanvasItem, etc) * or 0 for default GtkWidget derived types. * @value_type: Control value type * @pspec: Parameter spec to define valid ranges and other parameters, * should be a GParamSpec of @value_type or %NULL for defaults * @flags: Can be used to specify view only controls by passing * #SWAMIGUI_CONTROL_VIEW in which case view only control handlers will be * preferred. A value of 0 assumes control and view mode * (#SWAMIGUI_CONTROL_CTRLVIEW). * * Creates a GUI widget suitable for controlling values of type @value_type. * The @widg_type parameter is used to specify what base type of widget * to create, GTK_TYPE_WIDGET is assumed if 0. * * Returns: The new GUI widget derived from @widg_type and suitable for * controlling values of type @value_type or %NULL if @value_type/@widg_type * not handled. The new object uses the Gtk reference counting behavior. */ GObject * swamigui_control_create_widget (GType widg_type, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiguiControlHandler hfunc = NULL; GObject *widget = NULL; HandlerInfo *hinfo; GList *p, *found = NULL; gboolean view_only = FALSE; g_return_val_if_fail (value_type != 0, NULL); if (widg_type == 0) widg_type = GTK_TYPE_WIDGET; value_type = swamigui_control_get_alias_value_type (value_type); /* doesn't make sense to request control only */ flags &= SWAMIGUI_CONTROL_CTRLVIEW; if (!(flags & SWAMIGUI_CONTROL_VIEW)) flags = SWAMIGUI_CONTROL_CTRLVIEW; /* view only control requested? */ if ((flags & SWAMIGUI_CONTROL_CTRLVIEW) == SWAMIGUI_CONTROL_VIEW) view_only = TRUE; G_LOCK (control_handlers); /* search for matching control handler */ p = control_handlers; while (p) { hinfo = (HandlerInfo *)(p->data); /* handler widget is derived from @widg_type and of @value_type? */ if (g_type_is_a (hinfo->widg_type, widg_type) && hinfo->value_type == value_type) { if (!found || ((hinfo->flags & SWAMIGUI_CONTROL_CTRLVIEW) == SWAMIGUI_CONTROL_VIEW)) found = p; /* if view only, keep searching for a view only handler */ if (!view_only || ((hinfo->flags & SWAMIGUI_CONTROL_CTRLVIEW) == SWAMIGUI_CONTROL_VIEW)) break; } p = g_list_next (p); } if (found) { hinfo = (HandlerInfo *)(found->data); hfunc = hinfo->handler; widg_type = hinfo->widg_type; } G_UNLOCK (control_handlers); if (found) { widget = g_object_new (widg_type, NULL); /* call handler function to configure UI widget but don't create control */ hfunc (widget, value_type, pspec, flags | SWAMIGUI_CONTROL_NO_CREATE); } return (widget); } /* a GCompareFunc to find a specific widg_type/value_type handler, a 0 value_type is a wildcard */ static int swamigui_control_GCompare_type (gconstpointer a, gconstpointer b) { HandlerInfo *ainfo = (HandlerInfo *)a, *binfo = (HandlerInfo *)b; return (!(ainfo->widg_type == binfo->widg_type && (!binfo->value_type || ainfo->value_type == binfo->value_type))); } /* compares handler info by rank */ static int swamigui_control_GCompare_rank (gconstpointer a, gconstpointer b) { HandlerInfo *ainfo = (HandlerInfo *)a, *binfo = (HandlerInfo *)b; /* sort highest rank first */ return ((binfo->flags & SWAMIGUI_CONTROL_RANK_MASK) - (ainfo->flags & SWAMIGUI_CONTROL_RANK_MASK)); } /** * swamigui_control_set_queue: * @control: Control to assign to the GUI queue * * Set a control to use a GUI queue which is required for all controls * that may be controlled from a non-GUI thread. */ void swamigui_control_set_queue (SwamiControl *control) { g_return_if_fail (SWAMI_IS_CONTROL (control)); swami_control_set_queue (control, swamigui_root->ctrl_queue); } /** * swamigui_control_register: * @widg_type: Type of object control to register * @value_type: The control value type * @handler: Handler function for creating the control * @flags: A rank value between 1:lowest to 63:highest, 0:default (see * SwamiguiControlRank) or'ed with SwamiguiControlFlags defining the * view/control capabilities of this handler. The rank allows * preferred object types to be chosen when there are multiple object * control handlers for the same value and base object types. If * neither #SWAMIGUI_CONTROL_VIEW or #SWAMIGUI_CONTROL_CTRL are specified * then control/view is assumed (#SWAMIGUI_CONTROL_CTRLVIEW). * * This function registers new GUI control types. It is multi-thread safe * and can be called outside of the GUI thread (from a plugin for instance). * If the given @widg_type/@value_type already exists then the new @handler * is used. The @flags parameter specifies the rank to give preference to * handlers with the same @value_type and also contains control/view * capability flags. */ void swamigui_control_register (GType widg_type, GType value_type, SwamiguiControlHandler handler, guint flags) { HandlerInfo *hinfo, cmp; GList *p; g_return_if_fail (g_type_is_a (widg_type, G_TYPE_OBJECT)); g_return_if_fail (G_TYPE_IS_VALUE_TYPE (value_type) || value_type == G_TYPE_ENUM || value_type == G_TYPE_FLAGS); if ((flags & SWAMIGUI_CONTROL_RANK_MASK) == 0) flags |= SWAMIGUI_CONTROL_RANK_DEFAULT; if (!(flags & SWAMIGUI_CONTROL_CTRLVIEW)) flags |= SWAMIGUI_CONTROL_CTRLVIEW; cmp.widg_type = widg_type; cmp.value_type = value_type; G_LOCK (control_handlers); p = g_list_find_custom (control_handlers, &cmp, swamigui_control_GCompare_type); if (!p) { hinfo = g_slice_new0 (HandlerInfo); control_handlers = g_list_insert_sorted (control_handlers, hinfo, swamigui_control_GCompare_rank); } else hinfo = (HandlerInfo *)(p->data); hinfo->widg_type = widg_type; hinfo->value_type = value_type; hinfo->flags = flags; hinfo->handler = handler; G_UNLOCK (control_handlers); } /** * swamigui_control_unregister: * @widg_type: Object type of control handler to unregister * @value_type: The value type of the control handler * * Unregisters a previous @widg_type/@value_type GUI control handler. * It is multi-thread safe and can be called outside of GUI thread (from a * plugin for instance). */ void swamigui_control_unregister (GType widg_type, GType value_type) { HandlerInfo cmp; GList *p; g_return_if_fail (g_type_is_a (widg_type, GTK_TYPE_OBJECT)); g_return_if_fail (G_TYPE_IS_VALUE (value_type)); cmp.widg_type = widg_type; cmp.value_type = value_type; G_LOCK (control_handlers); p = g_list_find_custom (control_handlers, &cmp, swamigui_control_GCompare_type); if (p) { g_slice_free (HandlerInfo, p->data); control_handlers = g_list_delete_link (control_handlers, p); } G_UNLOCK (control_handlers); if (!p) g_warning ("Failed to find widget handler type '%s' value type '%s'", g_type_name (widg_type), g_type_name (value_type)); } /* Recursive function to walk a GtkContainer and add all widgets with GtkBuilder * names beginning with PROP:: to a list */ static void swamigui_control_glade_container_foreach (GtkWidget *widget, gpointer data) { GSList **list = data; const char *name; name = gtk_buildable_get_name (GTK_BUILDABLE (widget)); if (name && strncmp (name, "PROP::", 6) == 0) *list = g_slist_prepend (*list, widget); if (GTK_IS_CONTAINER (widget)) gtk_container_foreach (GTK_CONTAINER (widget), swamigui_control_glade_container_foreach, list); } /** * swamigui_control_glade_prop_connect: * @widget: A GTK widget created by GtkBuilder * @obj: Object to control properties of or %NULL to unset active object * * This function connects a GtkBuilder created @widget, with child widgets whose * names are of the form "PROP::[prop-name]", to the corresponding GObject * properties of @obj ([prop-name] values). An example child widget name would be * "PROP::volume" which would control the "volume" property of an object. * This allows for object GUI interfaces to be created with a minimum of code. * In order to work around issues with duplicate GtkBuilder names, a colon ':' * and any arbitrary text (a number for example) can be used to make the name * unique and is ignored. "PROP::volume:1" for example. */ void swamigui_control_glade_prop_connect (GtkWidget *widget, GObject *obj) { GtkWidget *widg; GParamSpec *pspec = NULL; const char *name; char *propname; SwamiControl *propctrl, *widgctrl; GObjectClass *objclass = NULL; gboolean viewonly = FALSE; GSList *list = NULL, *p; g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (!obj || G_IS_OBJECT (obj)); if (obj) { objclass = g_type_class_peek (G_OBJECT_TYPE (obj)); g_return_if_fail (objclass != NULL); } if (GTK_IS_CONTAINER (widget)) /* Recurse widget tree and add all PROP:: widgets */ gtk_container_foreach (GTK_CONTAINER (widget), swamigui_control_glade_container_foreach, &list); else list = g_slist_prepend (list, widget); /* loop over widgets */ for (p = list; p; p = p->next) { widg = (GtkWidget *)(p->data); name = gtk_buildable_get_name (GTK_BUILDABLE (widg)); name += 6; /* skip "PROP::" prefix */ /* to work around duplicate names, everything following a ':' char is ignored */ if ((propname = strchr (name, ':'))) propname = g_strndup (name, propname - name); /* ++ alloc */ else propname = g_strdup (name); /* ++ alloc */ /* get parameter spec of the property for object (if obj is set) */ if (obj) { pspec = g_object_class_find_property (objclass, propname); if (!pspec) { g_warning ("Object of type %s has no property '%s'", g_type_name (G_OBJECT_TYPE (obj)), propname); g_free (propname); /* -- free */ continue; } viewonly = (pspec->flags & G_PARAM_WRITABLE) == 0; } /* lookup existing control widget (if any) */ widgctrl = swamigui_control_lookup (G_OBJECT (widg)); if (!widgctrl) { if (!obj) { g_free (propname); /* -- free */ continue; /* no widget control to disconnect, continue */ } /* create or lookup widget control (view only if property is read only) */ widgctrl = swamigui_control_new_for_widget_full (G_OBJECT (widg), G_PARAM_SPEC_VALUE_TYPE (pspec), pspec, viewonly ? SWAMIGUI_CONTROL_VIEW : 0); if (!widgctrl) { g_critical ("Failed to create widget control for '%s' of type '%s'", propname, g_type_name (G_OBJECT_TYPE (widg))); g_free (propname); /* -- free */ continue; } } /* disconnect any existing connections */ else swami_control_disconnect_all (widgctrl); if (obj) { /* get a control for the object's property */ propctrl = swami_get_control_prop (obj, pspec); /* ++ ref */ if (propctrl) { /* connect the property control to the widget control */ swami_control_connect (propctrl, widgctrl, (viewonly ? 0 : SWAMI_CONTROL_CONN_BIDIR) | SWAMI_CONTROL_CONN_INIT | SWAMI_CONTROL_CONN_SPEC); g_object_unref (propctrl); /* -- unref */ } } g_free (propname); /* -- free */ } g_slist_free (list); /* -- free list */ } /** * swamigui_control_get_value_alias_type: * @type: Type to get alias type of * * Get the real value type used to control the given @type. * For example, all integer and floating point types are handled by * G_TYPE_DOUBLE controls. * * Returns: The alias type for the @type parameter or the same value if * type has no alias. */ GType swamigui_control_get_alias_value_type (GType type) { switch (type) { case G_TYPE_CHAR: case G_TYPE_UCHAR: case G_TYPE_INT: case G_TYPE_UINT: case G_TYPE_LONG: case G_TYPE_ULONG: case G_TYPE_INT64: case G_TYPE_UINT64: case G_TYPE_FLOAT: return (G_TYPE_DOUBLE); default: return (type); } } swami-2.2.0/src/swamigui/SwamiguiControl.h000066400000000000000000000106531361104770400205560ustar00rootroot00000000000000/* * SwamiguiControl.h - GUI control system * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_CONTROL_H__ #define __SWAMIGUI_CONTROL_H__ #include #include /* some defined rank values for registered handlers */ typedef enum { SWAMIGUI_CONTROL_RANK_LOWEST = 1, SWAMIGUI_CONTROL_RANK_LOW = 16, SWAMIGUI_CONTROL_RANK_NORMAL = 32, SWAMIGUI_CONTROL_RANK_HIGH = 48, SWAMIGUI_CONTROL_RANK_HIGHEST = 63 } SwamiguiControlRank; /* value to use for 0 (default) */ #define SWAMIGUI_CONTROL_RANK_DEFAULT SWAMIGUI_CONTROL_RANK_NORMAL /* rank mask */ #define SWAMIGUI_CONTROL_RANK_MASK 0x3F typedef enum { SWAMIGUI_CONTROL_CTRL = 0x40, /* controls values */ SWAMIGUI_CONTROL_VIEW = 0x80, /* displays values */ SWAMIGUI_CONTROL_NO_CREATE = 0x100 /* don't create control, cfg UI obj only */ } SwamiguiControlFlags; /* convenience for control/view controls */ #define SWAMIGUI_CONTROL_CTRLVIEW (SWAMIGUI_CONTROL_CTRL | SWAMIGUI_CONTROL_VIEW) typedef enum /*< flags >*/ { SWAMIGUI_CONTROL_OBJECT_NO_LABELS = 1 << 0, SWAMIGUI_CONTROL_OBJECT_NO_SORT = 1 << 1, SWAMIGUI_CONTROL_OBJECT_PROP_LABELS = 1 << 2 } SwamiguiControlObjectFlags; /** * SwamiguiControlHandler: * @widget: GUI widget to create a control for * @value_type: Control value type (useful to handler functions that can handle * multiple value types) * @pspec: Parameter spec defining the control parameters (value type, * valid range, etc), will be a GParamSpec of the specified @value_type or * %NULL for defaults * @flags: Flags indicating if control should display values or control * and display values. If the #SWAMIGUI_CONTROL_NO_CREATE flag is specified * then a control should not be created, but @widget should be configured to * conform to @pspec (or reset to defaults if %NULL). * * This is a function type to handle the creation of a control that is * bound to a GUI interface @widget. The control should be configured * according to @flags (if its display only then UI control changes should be * ignored or preferably disabled, control only flag will occur only with * handlers that don't display value changes). The UI @widget may be modified * to conform to @pspec (valid range, max string length, etc) and should be * done in a manner that allows @widget to be re-configured (i.e., set default * values if @pspec not supplied). * * Returns: Should return the new control which is controlling the * GUI interface @widget. */ typedef SwamiControl * (*SwamiguiControlHandler)(GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); /* quark used in associating a control to a controlled object */ extern GQuark swamigui_control_quark; SwamiControl *swamigui_control_new (GType type); SwamiControl *swamigui_control_new_for_widget (GObject *widget); SwamiControl *swamigui_control_new_for_widget_full (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); SwamiControl *swamigui_control_lookup (GObject *widget); void swamigui_control_prop_connect_widget (GObject *object, const char *propname, GObject *widget); GObject *swamigui_control_create_widget (GType widg_type, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); void swamigui_control_set_queue (SwamiControl *control); void swamigui_control_register (GType widg_type, GType value_type, SwamiguiControlHandler handler, guint flags); void swamigui_control_unregister (GType widg_type, GType value_type); void swamigui_control_glade_prop_connect (GtkWidget *widget, GObject *obj); GType swamigui_control_get_alias_value_type (GType type); #endif swami-2.2.0/src/swamigui/SwamiguiControlAdj.c000066400000000000000000000233321361104770400211660ustar00rootroot00000000000000/* * SwamiguiControlAdj.c - GtkAdjustment control object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiguiControlAdj.h" #include "SwamiguiRoot.h" static void swamigui_control_adj_class_init (SwamiguiControlAdjClass *klass); static void swamigui_control_adj_init (SwamiguiControlAdj *ctrladj); static void swamigui_control_adj_finalize (GObject *object); static GParamSpec *control_adj_get_spec_method (SwamiControl *control); static gboolean control_adj_set_spec_method (SwamiControl *control, GParamSpec *pspec); static void control_adj_get_value_method (SwamiControl *control, GValue *value); static void control_adj_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_control_adj_cb_value_changed (GtkAdjustment *adj, SwamiguiControlAdj *ctrladj); static void swamigui_adj_cb_destroy (GtkObject *object, gpointer user_data); static GObjectClass *parent_class = NULL; GType swamigui_control_adj_get_type (void) { static GType otype = 0; if (!otype) { static const GTypeInfo type_info = { sizeof (SwamiguiControlAdjClass), NULL, NULL, (GClassInitFunc) swamigui_control_adj_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (SwamiguiControlAdj), 0, (GInstanceInitFunc) swamigui_control_adj_init }; otype = g_type_register_static (SWAMI_TYPE_CONTROL, "SwamiguiControlAdj", &type_info, 0); } return (otype); } static void swamigui_control_adj_class_init (SwamiguiControlAdjClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); SwamiControlClass *control_class = SWAMI_CONTROL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swamigui_control_adj_finalize; control_class->get_spec = control_adj_get_spec_method; control_class->set_spec = control_adj_set_spec_method; control_class->get_value = control_adj_get_value_method; control_class->set_value = control_adj_set_value_method; } static void swamigui_control_adj_init (SwamiguiControlAdj *ctrladj) { SwamiControl *control = SWAMI_CONTROL (ctrladj); swami_control_set_queue (control, swamigui_root->ctrl_queue); swami_control_set_flags (control, SWAMI_CONTROL_SENDRECV | SWAMI_CONTROL_VALUE); swami_control_set_value_type (control, G_TYPE_DOUBLE); ctrladj->adj = NULL; } static void swamigui_control_adj_finalize (GObject *object) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (object); SWAMI_LOCK_WRITE (ctrladj); if (ctrladj->adj) { g_signal_handler_disconnect (ctrladj->adj, ctrladj->value_change_id); g_signal_handlers_disconnect_by_func (ctrladj->adj, swamigui_adj_cb_destroy, ctrladj); g_object_unref (ctrladj->adj); } if (ctrladj->pspec) g_param_spec_unref (ctrladj->pspec); SWAMI_UNLOCK_WRITE (ctrladj); if (parent_class->finalize) parent_class->finalize (object); } /* control is locked by caller */ static GParamSpec * control_adj_get_spec_method (SwamiControl *control) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (control); return (ctrladj->pspec); } /* control is locked by caller */ static gboolean control_adj_set_spec_method (SwamiControl *control, GParamSpec *pspec) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (control); GParamSpecDouble *pspec_dbl; if (ctrladj->pspec) g_param_spec_unref (ctrladj->pspec); ctrladj->pspec = g_param_spec_ref (pspec); g_param_spec_sink (pspec); /* take ownership of the parameter spec */ if (ctrladj->adj) { pspec_dbl = G_PARAM_SPEC_DOUBLE (pspec); ctrladj->adj->lower = pspec_dbl->minimum; ctrladj->adj->upper = pspec_dbl->maximum; gtk_adjustment_changed (ctrladj->adj); } return (TRUE); } /* control is NOT locked */ static void control_adj_get_value_method (SwamiControl *control, GValue *value) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (control); SWAMI_LOCK_READ (ctrladj); if (!ctrladj->adj) { SWAMI_UNLOCK_READ (ctrladj); return; } g_value_set_double (value, ctrladj->adj->value); SWAMI_UNLOCK_READ (ctrladj); } static void control_adj_set_value_method (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (control); GtkAdjustment *adj; guint value_change_id; double d; /* reference the adjustment under lock, to minimize lock time and prevent possible deadlocks with gtk_adjustment_value_changed callbacks */ SWAMI_LOCK_READ (ctrladj); adj = ctrladj->adj; if (adj) g_object_ref (adj); /* ++ ref adjustment */ value_change_id = ctrladj->value_change_id; SWAMI_UNLOCK_READ (ctrladj); if (!adj) { g_object_unref (adj); /* -- unref adjustment */ return; } d = g_value_get_double (value); if (adj->value != d) /* value is different? */ { /* block handler to avoid value set/notify loop */ g_signal_handler_block (adj, value_change_id); adj->value = g_value_get_double (value); gtk_adjustment_value_changed (adj); g_signal_handler_unblock (adj, value_change_id); } g_object_unref (adj); /* -- unref adjustment */ } /** * swamigui_control_adj_new: * @adj: GtkAdjustment to use as a control or %NULL to set later * * Create a new GtkAdjustment control object. * * Returns: New GtkAdjustment control with a refcount of 1 which the caller * owns. */ SwamiguiControlAdj * swamigui_control_adj_new (GtkAdjustment *adj) { SwamiguiControlAdj *ctrladj; ctrladj = g_object_new (SWAMIGUI_TYPE_CONTROL_ADJ, NULL); if (adj) swamigui_control_adj_set (ctrladj, adj); return (ctrladj); } /** * swamigui_control_adj_set: * @ctrladj: Swami GtkAdjustment control object * @adj: GtkAdjustment to assign to @ctrladj * * Set the GtkAdjustment of a adjustment control. */ void swamigui_control_adj_set (SwamiguiControlAdj *ctrladj, GtkAdjustment *adj) { GParamSpec *pspec; g_return_if_fail (SWAMIGUI_IS_CONTROL_ADJ (ctrladj)); g_return_if_fail (GTK_IS_ADJUSTMENT (adj)); /* ++ ref new spec */ pspec = g_param_spec_double ("value", NULL, NULL, adj->lower, adj->upper, adj->value, G_PARAM_READWRITE); SWAMI_LOCK_WRITE (ctrladj); if (ctrladj->adj) { /* disconnect value change and destroy handlers */ g_signal_handler_disconnect (ctrladj->adj, ctrladj->value_change_id); g_signal_handlers_disconnect_by_func (ctrladj->adj, swamigui_adj_cb_destroy, ctrladj); g_object_unref (ctrladj->adj); /* -- unref old adjustment */ } if (ctrladj->pspec) g_param_spec_unref (ctrladj->pspec); ctrladj->adj = g_object_ref (adj); /* ++ ref new adjustment */ ctrladj->pspec = pspec; /* we take over creator's ref */ /* connect signal to adjustment change signals */ ctrladj->value_change_id = g_signal_connect (adj, "value-changed", G_CALLBACK (swamigui_control_adj_cb_value_changed), ctrladj); /* connect to the adjustment's destroy signal */ g_signal_connect (adj, "destroy", G_CALLBACK (swamigui_adj_cb_destroy), ctrladj); SWAMI_UNLOCK_WRITE (ctrladj); } /** * swamigui_control_adj_block_changes: * @ctrladj: Swami GtkAdjustment control object * * Stop GtkAdjustment change signals from sending control events. */ void swamigui_control_adj_block_changes (SwamiguiControlAdj *ctrladj) { g_return_if_fail (SWAMIGUI_IS_CONTROL_ADJ (ctrladj)); SWAMI_LOCK_READ (ctrladj); if (ctrladj->adj) g_signal_handler_block (ctrladj->adj, ctrladj->value_change_id); SWAMI_UNLOCK_READ (ctrladj); } /** * swamigui_control_adj_block_changes: * @ctrladj: Swami GtkAdjustment control object * * Unblock a previous call to swamigui_control_adj_block_changes(). */ void swamigui_control_adj_unblock_changes (SwamiguiControlAdj *ctrladj) { g_return_if_fail (SWAMIGUI_IS_CONTROL_ADJ (ctrladj)); SWAMI_LOCK_READ (ctrladj); if (ctrladj->adj) g_signal_handler_unblock (ctrladj->adj, ctrladj->value_change_id); SWAMI_UNLOCK_READ (ctrladj); } /* adjustment value changed signal callback */ static void swamigui_control_adj_cb_value_changed (GtkAdjustment *adj, SwamiguiControlAdj *ctrladj) { GValue value = { 0 }; g_value_init (&value, G_TYPE_DOUBLE); g_value_set_double (&value, adj->value); swami_control_transmit_value ((SwamiControl *)ctrladj, &value); g_value_unset (&value); } /* GtkAdjustment destroy callback - releases reference */ static void swamigui_adj_cb_destroy (GtkObject *object, gpointer user_data) { SwamiguiControlAdj *ctrladj = SWAMIGUI_CONTROL_ADJ (user_data); SWAMI_LOCK_WRITE (ctrladj); if (ctrladj->adj) { /* disconnect value change and destroy handlers */ g_signal_handler_disconnect (ctrladj->adj, ctrladj->value_change_id); g_signal_handlers_disconnect_by_func (ctrladj->adj, swamigui_adj_cb_destroy, ctrladj); g_object_unref (ctrladj->adj); /* -- unref old adjustment */ } if (ctrladj->pspec) { g_param_spec_unref (ctrladj->pspec); ctrladj->pspec = NULL; } SWAMI_UNLOCK_WRITE (ctrladj); } swami-2.2.0/src/swamigui/SwamiguiControlAdj.h000066400000000000000000000044261361104770400211760ustar00rootroot00000000000000/* * SwamiguiControlAdj.h - GtkAdjustment SwamiControl object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_CONTROL_ADJ_H__ #define __SWAMIGUI_CONTROL_ADJ_H__ typedef struct _SwamiguiControlAdj SwamiguiControlAdj; typedef struct _SwamiguiControlAdjClass SwamiguiControlAdjClass; #include #include #define SWAMIGUI_TYPE_CONTROL_ADJ (swamigui_control_adj_get_type ()) #define SWAMIGUI_CONTROL_ADJ(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_CONTROL_ADJ, \ SwamiguiControlAdj)) #define SWAMIGUI_CONTROL_ADJ_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_CONTROL_ADJ, \ SwamiguiControlAdjClass)) #define SWAMIGUI_IS_CONTROL_ADJ(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_CONTROL_ADJ)) #define SWAMIGUI_IS_CONTROL_ADJ_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE ((obj), SWAMIGUI_TYPE_CONTROL_ADJ)) struct _SwamiguiControlAdj { SwamiControl parent_instance; GtkAdjustment *adj; /* GTK adjustment of control */ GParamSpec *pspec; /* parameter spec */ gulong value_change_id; /* GtkAdjustment value-changed handler ID */ }; struct _SwamiguiControlAdjClass { SwamiControlClass parent_class; }; GType swamigui_control_adj_get_type (void); SwamiguiControlAdj *swamigui_control_adj_new (GtkAdjustment *adj); void swamigui_control_adj_set (SwamiguiControlAdj *ctrladj, GtkAdjustment *adj); void swamigui_control_adj_block_changes (SwamiguiControlAdj *ctrladj); void swamigui_control_adj_unblock_changes (SwamiguiControlAdj *ctrladj); #endif swami-2.2.0/src/swamigui/SwamiguiControlMidiKey.c000066400000000000000000000403271361104770400220260ustar00rootroot00000000000000/* * SwamiguiControlMidiKey.c - MIDI keyboard control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /* * NOTE: Setting key arrays is NOT multi-thread safe! */ #include #include #include #include #include #include "SwamiguiControlMidiKey.h" #include "SwamiguiRoot.h" /* Some default values */ #define DEFAULT_LOWER_OCTAVE 2 #define DEFAULT_UPPER_OCTAVE DEFAULT_LOWER_OCTAVE + 1 enum { PROP_0, PROP_LOWER_OCTAVE, PROP_UPPER_OCTAVE, PROP_JOIN_OCTAVES, PROP_LOWER_VELOCITY, PROP_UPPER_VELOCITY, PROP_SAME_VELOCITY, PROP_LOWER_CHANNEL, PROP_UPPER_CHANNEL }; typedef struct { guint key; /* GDK key (see gdk/gdkkeysym.h header) */ gint8 active_note; /* active MIDI note or -1 if not active */ gint8 active_chan; /* MIDI channel of active note (if not -1) */ } MidiKey; static void swamigui_control_midi_key_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_control_midi_key_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static gint swamigui_control_midi_key_snooper (GtkWidget *grab_widget, GdkEventKey *event, gpointer func_data); static void swamigui_control_midi_key_finalize (GObject *object); /* FIXME - Dynamically determine key map */ /* default lower keys */ static guint default_lower_keys[] = { GDK_z, GDK_s, GDK_x, GDK_d, GDK_c, GDK_v, GDK_g, GDK_b, GDK_h, GDK_n, GDK_j, GDK_m, GDK_comma, GDK_l, GDK_period, GDK_semicolon, GDK_slash }; /* default upper keys */ static guint default_upper_keys[] = { GDK_q, GDK_2, GDK_w, GDK_3, GDK_e, GDK_r, GDK_5, GDK_t, GDK_6, GDK_y, GDK_7, GDK_u, GDK_i, GDK_9, GDK_o, GDK_0, GDK_p, GDK_bracketleft, GDK_equal, GDK_bracketright }; /* Widget types to ignore key presses from (text entries, etc) */ static char *ignore_widget_type_names[] = { "GtkEntry", "GtkTextView" }; /* Allocated and resolved type array, G_TYPE_INVALID terminated */ static GType *ignore_widget_types; G_DEFINE_TYPE (SwamiguiControlMidiKey, swamigui_control_midi_key, SWAMI_TYPE_CONTROL_MIDI); static void swamigui_control_midi_key_class_init (SwamiguiControlMidiKeyClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); int i; obj_class->set_property = swamigui_control_midi_key_set_property; obj_class->get_property = swamigui_control_midi_key_get_property; obj_class->finalize = swamigui_control_midi_key_finalize; g_object_class_install_property (obj_class, PROP_LOWER_OCTAVE, g_param_spec_int ("lower-octave", "Lower octave", "Lower keyboard MIDI octave", -2, 8, DEFAULT_LOWER_OCTAVE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_UPPER_OCTAVE, g_param_spec_int ("upper-octave", "Upper octave", "Upper keyboard MIDI octave", -2, 8, DEFAULT_UPPER_OCTAVE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_JOIN_OCTAVES, g_param_spec_boolean ("join-octaves", "Join octaves", "Join upper and lower octaves", TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOWER_VELOCITY, g_param_spec_int ("lower-velocity", "Lower velocity", "Lower keyboard MIDI velocity", 1, 127, 127, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_UPPER_VELOCITY, g_param_spec_int ("upper-velocity", "Upper velocity", "Upper keyboard MIDI velocity", 1, 127, 127, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SAME_VELOCITY, g_param_spec_boolean ("same-velocity", "Same velocity", "Same velocity for upper and lower", TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOWER_CHANNEL, g_param_spec_int ("lower-channel", "Lower channel", "Lower keyboard MIDI channel", 0, 15, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_UPPER_CHANNEL, g_param_spec_int ("upper-channel", "Upper channel", "Upper keyboard MIDI channel", 0, 15, 0, G_PARAM_READWRITE)); ignore_widget_types = g_new (GType, G_N_ELEMENTS (ignore_widget_type_names) + 1); for (i = 0; i < G_N_ELEMENTS (ignore_widget_type_names); i++) ignore_widget_types[i] = g_type_from_name (ignore_widget_type_names[i]); ignore_widget_types[i] = G_TYPE_INVALID; } static void swamigui_control_midi_key_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiControlMidiKey *keyctrl = SWAMIGUI_CONTROL_MIDI_KEY (object); switch (property_id) { case PROP_LOWER_OCTAVE: g_value_set_int (value, keyctrl->lower_octave); break; case PROP_UPPER_OCTAVE: g_value_set_int (value, keyctrl->upper_octave); break; case PROP_JOIN_OCTAVES: g_value_set_boolean (value, keyctrl->join_octaves); break; case PROP_LOWER_VELOCITY: g_value_set_int (value, keyctrl->lower_velocity); break; case PROP_UPPER_VELOCITY: g_value_set_int (value, keyctrl->upper_velocity); break; case PROP_SAME_VELOCITY: g_value_set_boolean (value, keyctrl->same_velocity); break; case PROP_LOWER_CHANNEL: g_value_set_int (value, keyctrl->lower_channel); break; case PROP_UPPER_CHANNEL: g_value_set_int (value, keyctrl->upper_channel); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_control_midi_key_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiControlMidiKey *keyctrl = SWAMIGUI_CONTROL_MIDI_KEY (object); switch (property_id) { case PROP_LOWER_OCTAVE: keyctrl->lower_octave = g_value_get_int (value); if (keyctrl->join_octaves) { keyctrl->upper_octave = keyctrl->lower_octave < 8 ? keyctrl->lower_octave + 1 : 8; g_object_notify (object, "upper-octave"); } break; case PROP_UPPER_OCTAVE: keyctrl->upper_octave = g_value_get_int (value); if (keyctrl->join_octaves) { keyctrl->lower_octave = keyctrl->upper_octave > -2 ? keyctrl->upper_octave - 1 : -2; g_object_notify (object, "lower-octave"); } break; case PROP_JOIN_OCTAVES: keyctrl->join_octaves = g_value_get_boolean (value); if (keyctrl->join_octaves && keyctrl->lower_octave + 1 != keyctrl->upper_octave) { keyctrl->upper_octave = keyctrl->lower_octave < 8 ? keyctrl->lower_octave + 1 : 8; g_object_notify (object, "upper-octave"); } break; case PROP_LOWER_VELOCITY: keyctrl->lower_velocity = g_value_get_int (value); if (keyctrl->same_velocity) { keyctrl->upper_velocity = keyctrl->lower_velocity; g_object_notify (object, "upper-velocity"); } break; case PROP_UPPER_VELOCITY: keyctrl->upper_velocity = g_value_get_int (value); if (keyctrl->same_velocity) { keyctrl->lower_velocity = keyctrl->upper_velocity; g_object_notify (object, "lower-velocity"); } break; case PROP_SAME_VELOCITY: keyctrl->same_velocity = g_value_get_boolean (value); if (keyctrl->same_velocity && keyctrl->lower_velocity != keyctrl->upper_velocity) { keyctrl->upper_velocity = keyctrl->lower_velocity; g_object_notify (object, "upper-velocity"); } break; case PROP_LOWER_CHANNEL: keyctrl->lower_channel = g_value_get_int (value); break; case PROP_UPPER_CHANNEL: keyctrl->upper_channel = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_control_midi_key_init (SwamiguiControlMidiKey *keyctrl) { MidiKey midikey; int i; swami_control_set_flags (SWAMI_CONTROL (keyctrl), SWAMI_CONTROL_SENDS); keyctrl->lower_keys = g_array_new (FALSE, FALSE, sizeof (MidiKey)); keyctrl->upper_keys = g_array_new (FALSE, FALSE, sizeof (MidiKey)); keyctrl->lower_octave = DEFAULT_LOWER_OCTAVE; keyctrl->upper_octave = DEFAULT_UPPER_OCTAVE; keyctrl->join_octaves = TRUE; keyctrl->lower_velocity = 127; keyctrl->upper_velocity = 127; keyctrl->same_velocity = TRUE; keyctrl->lower_channel = 0; keyctrl->upper_channel = 0; /* initialize lower and upper key array */ for (i = 0; i < G_N_ELEMENTS (default_lower_keys); i++) { midikey.key = default_lower_keys[i]; midikey.active_note = -1; g_array_append_val (keyctrl->lower_keys, midikey); } for (i = 0; i < G_N_ELEMENTS (default_upper_keys); i++) { midikey.key = default_upper_keys[i]; midikey.active_note = -1; g_array_append_val (keyctrl->upper_keys, midikey); } /* install our key snooper */ keyctrl->snooper_id = gtk_key_snooper_install (swamigui_control_midi_key_snooper, keyctrl); } static gint swamigui_control_midi_key_snooper (GtkWidget *grab_widget, GdkEventKey *event, gpointer func_data) { SwamiguiControlMidiKey *keyctrl = SWAMIGUI_CONTROL_MIDI_KEY (func_data); GtkWidget *toplevel, *focus; GType *ignore; guint key; if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) return (FALSE); key = gdk_keyval_to_lower (event->keyval); if (event->type == GDK_KEY_PRESS) { toplevel = gtk_widget_get_toplevel (grab_widget); /* Only listen to key presses on main window, not dialogs (allow releases though) */ if (toplevel != swamigui_root->main_window) return (FALSE); /* Check if focus widget type should be ignored (text entries, etc) */ focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); if (focus) { for (ignore = ignore_widget_types; *ignore != G_TYPE_INVALID; ignore++) { if (g_type_is_a (G_OBJECT_TYPE (focus), *ignore)) return (FALSE); } } swamigui_control_midi_key_press (keyctrl, key); } else if (event->type == GDK_KEY_RELEASE) swamigui_control_midi_key_release (keyctrl, key); return (FALSE); /* don't stop event propagation */ } static void swamigui_control_midi_key_finalize (GObject *object) { SwamiguiControlMidiKey *keyctrl = SWAMIGUI_CONTROL_MIDI_KEY (object); gtk_key_snooper_remove (keyctrl->snooper_id); g_array_free (keyctrl->lower_keys, TRUE); g_array_free (keyctrl->upper_keys, TRUE); if (G_OBJECT_CLASS (swamigui_control_midi_key_parent_class)->finalize) G_OBJECT_CLASS (swamigui_control_midi_key_parent_class)->finalize (object); } /** * swamigui_control_midi_key_new: * * Create a new MIDI keyboard control. * * Returns: New MIDI keyboard control with a refcount of 1 which the * caller owns. */ SwamiguiControlMidiKey * swamigui_control_midi_key_new (void) { return (SWAMIGUI_CONTROL_MIDI_KEY (g_object_new (SWAMIGUI_TYPE_CONTROL_MIDI_KEY, NULL))); } /** * swamigui_control_midi_key_press: * @keyctrl: MIDI keyboard control * @key: GDK keyboard key (see gdk/gdkkeysyms.h header) * * Send a key press to a MIDI keyboard control. */ void swamigui_control_midi_key_press (SwamiguiControlMidiKey *keyctrl, guint key) { MidiKey *midikey, *found = NULL; int newnote, newvel, newchan; int i, count; g_return_if_fail (SWAMIGUI_IS_CONTROL_MIDI_KEY (keyctrl)); /* look for key in lower octave */ count = keyctrl->lower_keys->len; for (i = 0; i < count; i++) { midikey = &g_array_index (keyctrl->lower_keys, MidiKey, i); if (midikey->key == key) /* found key? */ { newnote = (keyctrl->lower_octave + 2) * 12 + i; /* calculate MIDI note */ newvel = keyctrl->lower_velocity; newchan = keyctrl->lower_channel; found = midikey; break; } } if (!found) /* not found in lower octave? */ { /* look for key in upper octave */ count = keyctrl->upper_keys->len; for (i = 0; i < count; i++) { midikey = &g_array_index (keyctrl->upper_keys, MidiKey, i); if (midikey->key == key) /* found key? */ { newnote = (keyctrl->upper_octave + 2) * 12 + i; /* calc MIDI note */ newvel = keyctrl->upper_velocity; newchan = keyctrl->upper_channel; found = midikey; break; } } } /* return if key not found or already active and the same as new note */ if (!found || found->active_note == newnote) return; if (found->active_note != -1) /* previous active note? */ { /* send note off event */ swami_control_midi_transmit (SWAMI_CONTROL_MIDI (keyctrl), SWAMI_MIDI_NOTE_OFF, found->active_chan, found->active_note, 0x7F); found->active_note = -1; } if (newnote <= 127) { /* send note on event */ swami_control_midi_transmit (SWAMI_CONTROL_MIDI (keyctrl), SWAMI_MIDI_NOTE_ON, newchan, newnote, newvel); found->active_note = newnote; found->active_chan = newchan; } } /** * swamigui_control_midi_key_release: * @keyctrl: MIDI keyboard control * @key: GDK keyboard key (see gdk/gdkkeysyms.h header) * * Send a key release to a MIDI keyboard control. */ void swamigui_control_midi_key_release (SwamiguiControlMidiKey *keyctrl, guint key) { MidiKey *midikey, *found = NULL; int i, count; g_return_if_fail (SWAMIGUI_IS_CONTROL_MIDI_KEY (keyctrl)); /* look for key in lower octave */ count = keyctrl->lower_keys->len; for (i = 0; i < count; i++) { midikey = &g_array_index (keyctrl->lower_keys, MidiKey, i); if (midikey->key == key) /* found key? */ { found = midikey; break; } } if (!found) /* not found in lower octave? */ { /* look for key in upper octave */ count = keyctrl->upper_keys->len; for (i = 0; i < count; i++) { midikey = &g_array_index (keyctrl->upper_keys, MidiKey, i); if (midikey->key == key) /* found key? */ { found = midikey; break; } } } if (!found || found->active_note == -1) return; /* send note off event */ swami_control_midi_transmit (SWAMI_CONTROL_MIDI (keyctrl), SWAMI_MIDI_NOTE_OFF, found->active_chan, found->active_note, 0x7F); found->active_note = -1; } /** * swamigui_control_midi_key_set_lower: * @keyctrl: MIDI keyboard control * @keys: Array of GDK key values * @count: Number of values in @keys * * Set the lower keyboard key array. * * Note: Not multi-thread safe */ void swamigui_control_midi_key_set_lower (SwamiguiControlMidiKey *keyctrl, const guint *keys, guint count) { MidiKey midikey; guint i; g_return_if_fail (SWAMIGUI_IS_CONTROL_MIDI_KEY (keyctrl)); g_return_if_fail (keys != NULL || count == 0); g_array_set_size (keyctrl->lower_keys, 0); for (i = 0; i < count; i++) { midikey.key = keys[i]; midikey.active_note = -1; g_array_append_val (keyctrl->lower_keys, midikey); } } /** * swamigui_control_midi_key_set_upper: * @keyctrl: MIDI keyboard control * @keys: Array of GDK key values * @count: Number of values in @keys * * Set the upper keyboard key array. * * Note: Not multi-thread safe */ void swamigui_control_midi_key_set_upper (SwamiguiControlMidiKey *keyctrl, const guint *keys, guint count) { MidiKey midikey; guint i; g_return_if_fail (SWAMIGUI_IS_CONTROL_MIDI_KEY (keyctrl)); g_return_if_fail (keys != NULL || count == 0); g_array_set_size (keyctrl->upper_keys, 0); for (i = 0; i < count; i++) { midikey.key = keys[i]; midikey.active_note = -1; g_array_append_val (keyctrl->upper_keys, midikey); } } swami-2.2.0/src/swamigui/SwamiguiControlMidiKey.h000066400000000000000000000062611361104770400220320ustar00rootroot00000000000000/* * SwamiguiControlMidiKey.h - Header for MIDI keyboard control * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_CONTROL_MIDI_KEY_H__ #define __SWAMIGUI_CONTROL_MIDI_KEY_H__ #include #include typedef struct _SwamiguiControlMidiKey SwamiguiControlMidiKey; typedef struct _SwamiguiControlMidiKeyClass SwamiguiControlMidiKeyClass; #define SWAMIGUI_TYPE_CONTROL_MIDI_KEY (swamigui_control_midi_key_get_type ()) #define SWAMIGUI_CONTROL_MIDI_KEY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_CONTROL_MIDI_KEY, \ SwamiguiControlMidiKey)) #define SWAMIGUI_CONTROL_MIDI_KEY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_CONTROL_MIDI_KEY, \ SwamiguiControlMidiKeyClass)) #define SWAMIGUI_IS_CONTROL_MIDI_KEY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_CONTROL_MIDI_KEY)) #define SWAMIGUI_IS_CONTROL_MIDI_KEY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_CONTROL_MIDI_KEY)) /* MIDI keyboard control object */ struct _SwamiguiControlMidiKey { SwamiControlMidi parent_instance; /* derived from SwamiControlMidi */ guint snooper_id; /* key snooper handler ID */ GArray *lower_keys; /* array of lower keys (see MidiKey in .c) */ GArray *upper_keys; /* array of upper keys (see MidiKey in .c) */ gint8 lower_octave; /* lower octave (-2 - 8) */ gint8 upper_octave; /* upper octave (-2 - 8) */ gboolean join_octaves; /* if TRUE then setting lower_octave sets upper_octave + 1 */ guint8 lower_velocity; /* lower MIDI velocity */ guint8 upper_velocity; /* upper MIDI velocity */ gboolean same_velocity; /* if TRUE then setting lower_velocity sets upper_velocity */ guint8 lower_channel; /* lower MIDI channel */ guint8 upper_channel; /* upper MIDI channel */ }; /* MIDI keyboard control class */ struct _SwamiguiControlMidiKeyClass { SwamiControlMidiClass parent_class; }; GType swamigui_control_midi_key_get_type (void); SwamiguiControlMidiKey *swamigui_control_midi_key_new (void); void swamigui_control_midi_key_press (SwamiguiControlMidiKey *keyctrl, guint key); void swamigui_control_midi_key_release (SwamiguiControlMidiKey *keyctrl, guint key); void swamigui_control_midi_key_set_lower (SwamiguiControlMidiKey *keyctrl, const guint *keys, guint count); void swamigui_control_midi_key_set_upper (SwamiguiControlMidiKey *keyctrl, const guint *keys, guint count); #endif swami-2.2.0/src/swamigui/SwamiguiControl_widgets.c000066400000000000000000001242021361104770400222730ustar00rootroot00000000000000/* * SwamiguiControl_widgets.c - Builtin GtkWidget control handlers * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiguiControl.h" #include "SwamiguiControlAdj.h" #include "SwamiguiSpinScale.h" #include "SwamiguiNoteSelector.h" #include "SwamiguiComboEntry.h" #include "icons.h" #include "i18n.h" static void func_control_cb_widget_destroy (GtkObject *object, gpointer user_data); static SwamiControl *adjustment_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void adjustment_control_cb_spec_changed (SwamiControl *control, GParamSpec *pspec, gpointer user_data); static SwamiControl *entry_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void entry_control_get_func (SwamiControl *control, GValue *value); static void entry_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void entry_control_cb_changed (GtkEditable *editable, gpointer user_data); static SwamiControl *combo_box_entry_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static SwamiControl *text_view_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static SwamiControl *text_buffer_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void text_buffer_control_get_func (SwamiControl *control, GValue *value); static void text_buffer_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void text_buffer_control_cb_changed (GtkTextBuffer *txtbuf, gpointer user_data); static void text_buffer_widget_weak_notify (gpointer data, GObject *where_the_object_was); static SwamiControl *file_chooser_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void file_chooser_control_get_func (SwamiControl *control, GValue *value); static void file_chooser_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void file_chooser_control_cb_changed (GtkFileChooser *chooser, gpointer user_data); static SwamiControl *label_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void label_control_get_func (SwamiControl *control, GValue *value); static void label_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static SwamiControl *toggle_button_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void toggle_button_control_get_func (SwamiControl *control, GValue *value); static void toggle_button_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void toggle_button_control_toggled (GtkToggleButton *btn, gpointer user_data); static SwamiControl *combo_box_string_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void combo_box_string_control_get_func (SwamiControl *control, GValue *value); static void combo_box_string_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void combo_box_string_control_changed (GtkComboBox *combo, gpointer user_data); static SwamiControl *combo_box_enum_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void combo_box_enum_control_get_func (SwamiControl *control, GValue *value); static void combo_box_enum_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void combo_box_enum_control_changed (GtkComboBox *combo, gpointer user_data); static SwamiControl *combo_box_gtype_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags); static void combo_box_gtype_control_get_func (SwamiControl *control, GValue *value); static void combo_box_gtype_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void combo_box_gtype_control_changed (GtkComboBox *combo, gpointer user_data); void _swamigui_control_widgets_init (void) { /* GtkAdjustment based GUI controls */ swamigui_control_register (GTK_TYPE_SPIN_BUTTON, G_TYPE_DOUBLE, adjustment_control_handler, SWAMIGUI_CONTROL_RANK_HIGH); swamigui_control_register (SWAMIGUI_TYPE_SPIN_SCALE, G_TYPE_DOUBLE, adjustment_control_handler, 0); swamigui_control_register (GTK_TYPE_HSCALE, G_TYPE_DOUBLE, adjustment_control_handler, 0); swamigui_control_register (GTK_TYPE_VSCALE, G_TYPE_DOUBLE, adjustment_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_HSCROLLBAR, G_TYPE_DOUBLE, adjustment_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_VSCROLLBAR, G_TYPE_DOUBLE, adjustment_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (SWAMIGUI_TYPE_NOTE_SELECTOR, G_TYPE_DOUBLE, adjustment_control_handler, SWAMIGUI_CONTROL_RANK_LOW); /* string type controls */ swamigui_control_register (GTK_TYPE_ENTRY, G_TYPE_STRING, entry_control_handler, SWAMIGUI_CONTROL_RANK_HIGH); swamigui_control_register (SWAMIGUI_TYPE_COMBO_ENTRY, G_TYPE_STRING, combo_box_entry_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_TEXT_VIEW, G_TYPE_STRING, text_view_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_TEXT_BUFFER, G_TYPE_STRING, text_buffer_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_FILE_CHOOSER_BUTTON, G_TYPE_STRING, file_chooser_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_LABEL, G_TYPE_STRING, label_control_handler, SWAMIGUI_CONTROL_VIEW | SWAMIGUI_CONTROL_RANK_LOW); /* button controls */ swamigui_control_register (GTK_TYPE_CHECK_BUTTON, G_TYPE_BOOLEAN, toggle_button_control_handler, SWAMIGUI_CONTROL_RANK_HIGH); swamigui_control_register (GTK_TYPE_TOGGLE_BUTTON, G_TYPE_BOOLEAN, toggle_button_control_handler, 0); /* combo box controls */ swamigui_control_register (GTK_TYPE_COMBO_BOX, G_TYPE_STRING, combo_box_string_control_handler, SWAMIGUI_CONTROL_RANK_HIGH); swamigui_control_register (GTK_TYPE_COMBO_BOX, G_TYPE_ENUM, combo_box_enum_control_handler, SWAMIGUI_CONTROL_RANK_LOW); swamigui_control_register (GTK_TYPE_COMBO_BOX, G_TYPE_GTYPE, combo_box_gtype_control_handler, SWAMIGUI_CONTROL_RANK_LOW); /* Additional possible GUI controls: GtkProgressBar, GtkButton */ } /* function used by function control handlers to catch widget "destroy" signal and remove widget reference */ static void func_control_cb_widget_destroy (GtkObject *object, gpointer user_data) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (user_data); gpointer data; data = SWAMI_CONTROL_FUNC_DATA (control); if (data) /* destroy may be called multiple times */ { SWAMI_LOCK_WRITE (control); SWAMI_CONTROL_FUNC_DATA (control) = NULL; g_object_unref (object); SWAMI_UNLOCK_WRITE (control); } } static SwamiControl * adjustment_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { GtkAdjustment *adj; SwamiControl *control = NULL; IpatchUnitInfo *unitinfo; gdouble min, max, def; gboolean isint = FALSE; guint units; guint digits = 2; /* default digits */ g_object_get (widget, "adjustment", &adj, NULL); g_return_val_if_fail (adj != NULL, NULL); if (GTK_IS_SPIN_BUTTON (widget) && !SWAMIGUI_IS_NOTE_SELECTOR (widget)) gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE); if (pspec && swami_param_get_limits (pspec, &min, &max, &def, &isint)) { /* set the number of significant decimal digits */ if (g_object_class_find_property (G_OBJECT_GET_CLASS (widget), "digits")) { if (!isint) { /* if unit-type is set.. */ ipatch_param_get (pspec, "unit-type", &units, NULL); if (units) { unitinfo = ipatch_unit_lookup (units); digits = unitinfo->digits; } else ipatch_param_get (pspec, "float-digits", &digits, NULL); } else digits = 0; /* int type, no decimal digits */ g_object_set (widget, "digits", digits, NULL); } adj->lower = min; adj->upper = max; adj->value = def; } else { adj->lower = 0.0; adj->upper = G_MAXINT; adj->value = 0.0; } adj->step_increment = 1.0; adj->page_increment = 10.0; // FIXME - Smarter? gtk_adjustment_changed (adj); gtk_adjustment_value_changed (adj); if (!(flags & SWAMIGUI_CONTROL_NO_CREATE)) { control = SWAMI_CONTROL (swamigui_control_adj_new (adj)); /* if the pspec is not an integer type and widget has the digits property * watch for pspec changes (to update number of decimal digits) */ if (!isint && g_object_class_find_property (G_OBJECT_GET_CLASS (widget), "digits")) g_signal_connect (control, "spec-changed", G_CALLBACK (adjustment_control_cb_spec_changed), widget); } return (control); } /* updates digits if control parameter spec changes */ static void adjustment_control_cb_spec_changed (SwamiControl *control, GParamSpec *pspec, gpointer user_data) { GObject *widget = G_OBJECT (user_data); IpatchUnitInfo *unitinfo; guint digits, units; ipatch_param_get (pspec, "unit-type", &units, NULL); if (units) { unitinfo = ipatch_unit_lookup (units); digits = unitinfo->digits; } else ipatch_param_get (pspec, "float-digits", &digits, NULL); /* changing digits causes control value to change, we block events here */ swamigui_control_adj_block_changes (SWAMIGUI_CONTROL_ADJ (control)); g_object_set (widget, "digits", digits, NULL); swamigui_control_adj_unblock_changes (SWAMIGUI_CONTROL_ADJ (control)); } /* * Entry control handler */ static SwamiControl * entry_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control = NULL; if (!(flags & SWAMIGUI_CONTROL_NO_CREATE)) { g_object_ref (widget); /* ++ ref the GtkEntry for the control */ control = swami_control_func_new (); swami_control_set_value_type (SWAMI_CONTROL (control), G_TYPE_STRING); if (!pspec) pspec = g_param_spec_string ("value", "value", "value", NULL, G_PARAM_READWRITE); else g_param_spec_ref (pspec); /* ++ ref for swami_control_set_spec */ swami_control_set_spec (SWAMI_CONTROL (control), pspec); swami_control_func_assign_funcs (control, entry_control_get_func, entry_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); } if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_editable_set_editable (GTK_EDITABLE (widget), TRUE); /* set entry max length */ if (pspec) { guint maxlen; ipatch_param_get (pspec, "string-max-length", &maxlen, NULL); gtk_entry_set_max_length (GTK_ENTRY (widget), maxlen); } if (control) g_signal_connect (widget, "changed", G_CALLBACK (entry_control_cb_changed), control); } /* not controllable */ else gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE); return (SWAMI_CONTROL (control)); } /* GtkEntry handler SwamiControl get value function */ static void entry_control_get_func (SwamiControl *control, GValue *value) { gpointer data; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) g_value_set_string (value, gtk_entry_get_text (GTK_ENTRY (data))); SWAMI_UNLOCK_READ (control); } /* GtkEntry handler SwamiControl set value function */ static void entry_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkEntry *entry = NULL; gpointer data; const char *s; /* minimize lock and ref the entry */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) entry = GTK_ENTRY (g_object_ref (data)); /* ++ ref entry */ SWAMI_UNLOCK_READ (control); if (entry) { g_signal_handlers_block_by_func (entry, entry_control_cb_changed, control); s = g_value_get_string (value); gtk_entry_set_text (entry, s ? s : ""); g_signal_handlers_unblock_by_func (entry, entry_control_cb_changed, control); g_object_unref (entry); /* -- unref entry */ } } /* callback for GtkEntry control to propagate text changes */ static void entry_control_cb_changed (GtkEditable *editable, gpointer user_data) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (user_data); GValue value = { 0 }; /* transmit the entry change */ g_value_init (&value, G_TYPE_STRING); g_value_take_string (&value, gtk_editable_get_chars (editable, 0, -1)); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } static SwamiControl * combo_box_entry_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { GtkWidget *entry; entry = gtk_bin_get_child (GTK_BIN (widget)); return (entry_control_handler (G_OBJECT (entry), value_type, pspec, flags)); } /* * Text view control handler (uses text buffer control handler) */ static SwamiControl * text_view_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { GtkTextView *txtview = GTK_TEXT_VIEW (widget); GObject *txtbuf; txtbuf = G_OBJECT (gtk_text_view_get_buffer (txtview)); return (text_buffer_control_handler (txtbuf, value_type, pspec, flags)); } /* * Text buffer control handler */ static SwamiControl * text_buffer_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control = NULL; GtkTextBuffer *txtbuf = GTK_TEXT_BUFFER (widget); GtkTextIter start, end; GtkTextTag *tag; if (!(flags & SWAMIGUI_CONTROL_NO_CREATE)) { g_object_ref (widget); /* ++ ref the GtkTextBuffer for the control */ control = swami_control_func_new (); swami_control_set_spec ((SwamiControl *)control, g_param_spec_string ("value", "value", "value", NULL, G_PARAM_READWRITE)); swami_control_func_assign_funcs (control, text_buffer_control_get_func, text_buffer_control_set_func, NULL, widget); if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ g_signal_connect (widget, "changed", G_CALLBACK (text_buffer_control_cb_changed), control); /* not a GtkObject (no "destroy") so we add a weak reference instead */ g_object_weak_ref (widget, text_buffer_widget_weak_notify, control); } if (!(flags & SWAMIGUI_CONTROL_CTRL)) /* view only? */ { /* make text buffer read only */ tag = gtk_text_buffer_create_tag (txtbuf, "read_only", "editable", FALSE, NULL); gtk_text_buffer_get_bounds (txtbuf, &start, &end); gtk_text_buffer_apply_tag (txtbuf, tag, &start, &end); } return (control ? SWAMI_CONTROL (control) : NULL); } /* GtkTextBuffer handler SwamiControlFunc get value function */ static void text_buffer_control_get_func (SwamiControl *control, GValue *value) { gpointer data; GtkTextBuffer *txtbuf; GtkTextIter start, end; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) { txtbuf = GTK_TEXT_BUFFER (data); gtk_text_buffer_get_bounds (txtbuf, &start, &end); g_value_set_string (value, gtk_text_buffer_get_text (txtbuf, &start, &end, FALSE)); } SWAMI_UNLOCK_READ (control); } /* GtkTextBuffer handler SwamiControlFunc set value function */ static void text_buffer_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkTextBuffer *txtbuf = NULL; gpointer data; const char *s; /* minimize lock and ref the text buffer */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) txtbuf = GTK_TEXT_BUFFER (g_object_ref (data)); /* ++ ref */ SWAMI_UNLOCK_READ (control); if (txtbuf) { g_signal_handlers_block_by_func (txtbuf, text_buffer_control_cb_changed, control); s = g_value_get_string (value); gtk_text_buffer_set_text (txtbuf, s ? s : "", -1); g_signal_handlers_unblock_by_func (txtbuf, text_buffer_control_cb_changed, control); g_object_unref (txtbuf); /* -- unref text buffer */ } } /* GtkTextBuffer changed callback */ static void text_buffer_control_cb_changed (GtkTextBuffer *txtbuf, gpointer user_data) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (user_data); GtkTextIter start, end; GValue value = { 0 }; gtk_text_buffer_get_bounds (txtbuf, &start, &end); g_value_init (&value, G_TYPE_STRING); g_value_set_string (&value, gtk_text_buffer_get_text (txtbuf, &start, &end, FALSE)); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } /* weak notify when text buffer is destroyed */ static void text_buffer_widget_weak_notify (gpointer data, GObject *where_the_object_was) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (data); SWAMI_LOCK_WRITE (control); SWAMI_CONTROL_FUNC_DATA (control) = NULL; SWAMI_UNLOCK_WRITE (control); } /* * file chooser control handler */ static SwamiControl * file_chooser_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control = NULL; if (!(flags & SWAMIGUI_CONTROL_NO_CREATE)) { g_object_ref (widget); /* ++ ref the GtkEntry for the control */ control = swami_control_func_new (); swami_control_set_spec (SWAMI_CONTROL (control), g_param_spec_string ("value", "value", "value", NULL, G_PARAM_READWRITE)); swami_control_func_assign_funcs (control, file_chooser_control_get_func, file_chooser_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); } if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_widget_set_sensitive ((GtkWidget *)widget, TRUE); if (control) g_signal_connect (widget, "file-set", G_CALLBACK (file_chooser_control_cb_changed), control); } /* not controllable */ else gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE); return (SWAMI_CONTROL (control)); } /* file chooser handler SwamiControl get value function */ static void file_chooser_control_get_func (SwamiControl *control, GValue *value) { GtkFileChooser *chooser = NULL; gpointer data; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) chooser = GTK_FILE_CHOOSER (g_object_ref (data)); /* ++ ref file chooser */ SWAMI_UNLOCK_READ (control); if (chooser) { g_value_take_string (value, gtk_file_chooser_get_filename (chooser)); g_object_unref (chooser); /* -- unref file chooser */ } } /* file chooser handler SwamiControl set value function */ static void file_chooser_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkFileChooser *chooser = NULL; gpointer data; const char *s; /* minimize lock and ref the file chooser */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) chooser = GTK_FILE_CHOOSER (g_object_ref (data)); /* ++ ref file chooser */ SWAMI_UNLOCK_READ (control); if (chooser) { g_signal_handlers_block_by_func (chooser, file_chooser_control_cb_changed, control); s = g_value_get_string (value); gtk_file_chooser_set_filename (chooser, s ? s : ""); g_signal_handlers_unblock_by_func (chooser, file_chooser_control_cb_changed, control); g_object_unref (chooser); /* -- unref file chooser */ } } /* callback for file chooser control to propagate text changes */ static void file_chooser_control_cb_changed (GtkFileChooser *chooser, gpointer user_data) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (user_data); GValue value = { 0 }; /* transmit the file chooser change */ g_value_init (&value, G_TYPE_STRING); g_value_take_string (&value, gtk_file_chooser_get_filename (chooser)); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } /* * label control handler (display only) */ static SwamiControl * label_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control; if (flags & SWAMIGUI_CONTROL_NO_CREATE) return (NULL); g_object_ref (widget); /* ++ ref widget for control */ control = swami_control_func_new (); swami_control_set_spec ((SwamiControl *)control, g_param_spec_string ("value", "value", "value", NULL, G_PARAM_READWRITE)); swami_control_func_assign_funcs (control, label_control_get_func, label_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); return (SWAMI_CONTROL (control)); } /* GtkLabel handler SwamiControlFunc get value function */ static void label_control_get_func (SwamiControl *control, GValue *value) { gpointer data; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) g_value_set_string (value, gtk_label_get_text (GTK_LABEL (data))); SWAMI_UNLOCK_READ (control); } /* GtkLabel handler SwamiControlFunc set value function */ static void label_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkLabel *label = NULL; gpointer data; const char *s; /* minimize lock time, ref and do the work outside of lock */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) label = GTK_LABEL (g_object_ref (data)); /* -- ref label */ SWAMI_UNLOCK_READ (control); if (label) { s = g_value_get_string (value); gtk_label_set_text (label, s ? s : ""); g_object_unref (label); /* -- unref label */ } } /* * toggle button control handler */ static SwamiControl * toggle_button_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control; if (flags & SWAMIGUI_CONTROL_NO_CREATE) return (NULL); g_object_ref (widget); /* ++ ref button widget for control */ control = swami_control_func_new (); swami_control_set_spec ((SwamiControl *)control, g_param_spec_boolean ("value", "value", "value", FALSE, G_PARAM_READWRITE)); swami_control_func_assign_funcs (control, toggle_button_control_get_func, toggle_button_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_widget_set_sensitive ((GtkWidget *)widget, TRUE); g_signal_connect (widget, "toggled", G_CALLBACK (toggle_button_control_toggled), control); } else gtk_widget_set_sensitive ((GtkWidget *)widget, FALSE); return (SWAMI_CONTROL (control)); } /* toggle button handler SwamiControlFunc get value function */ static void toggle_button_control_get_func (SwamiControl *control, GValue *value) { gpointer data; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data))); SWAMI_UNLOCK_READ (control); } /* toggle button handler SwamiControlFunc set value function */ static void toggle_button_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkToggleButton *btn = NULL; gpointer data; /* minimize lock time - ref the button */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) btn = GTK_TOGGLE_BUTTON (g_object_ref (data)); /* ++ ref btn */ SWAMI_UNLOCK_READ (control); if (btn) { g_signal_handlers_block_by_func (btn, toggle_button_control_toggled, control); gtk_toggle_button_set_active (btn, g_value_get_boolean (value)); g_signal_handlers_unblock_by_func (btn, toggle_button_control_toggled, control); g_object_unref (btn); /* -- unref button */ } } /* toggle callback to transmit control change to connected controls */ static void toggle_button_control_toggled (GtkToggleButton *btn, gpointer user_data) { SwamiControlFunc *control = SWAMI_CONTROL_FUNC (user_data); GValue value = { 0 }; /* transmit the toggle button change */ g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, gtk_toggle_button_get_active (btn)); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } /* * String combo box control handler */ static SwamiControl * combo_box_string_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control; g_return_val_if_fail (g_value_type_transformable (value_type, G_TYPE_STRING), NULL); if (flags & SWAMIGUI_CONTROL_NO_CREATE) return (NULL); g_object_ref (widget); /* ++ ref widget for control */ /* Create parameter spec if none or not transformable to string */ if (!pspec || !swami_param_type_transformable (G_PARAM_SPEC_TYPE (pspec), G_TYPE_PARAM_STRING)) pspec = g_param_spec_string ("value", "value", "value", NULL, G_PARAM_READWRITE); else g_param_spec_ref (pspec); /* ++ ref for swami_control_set_spec */ control = swami_control_func_new (); swami_control_set_value_type (SWAMI_CONTROL (control), G_TYPE_STRING); swami_control_set_spec ((SwamiControl *)control, pspec); swami_control_func_assign_funcs (control, combo_box_string_control_get_func, combo_box_string_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_widget_set_sensitive ((GtkWidget *)widget, TRUE); g_signal_connect (widget, "changed", G_CALLBACK (combo_box_string_control_changed), control); } else gtk_widget_set_sensitive ((GtkWidget *)widget, FALSE); return (SWAMI_CONTROL (control)); } /* string combo box handler SwamiControlFunc get value function */ static void combo_box_string_control_get_func (SwamiControl *control, GValue *value) { GtkComboBox *combo = NULL; GtkTreeModel *model; GtkTreeIter iter; GParamSpec *pspec; gpointer data; char *text = NULL; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref for outside of lock */ pspec = swami_control_get_spec (control); /* ++ ref param spec */ SWAMI_UNLOCK_READ (control); if (combo) { if (gtk_combo_box_get_active_iter (combo, &iter)) { model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, 0, &text, /* ++ alloc */ -1); } g_value_set_string (value, text); g_free (text); /* -- free */ g_object_unref (combo); /* -- unref combo box */ } g_param_spec_unref (pspec); /* -- unref param spec */ } /* string combo box handler SwamiControlFunc set value function */ static void combo_box_string_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkComboBox *combo = NULL; GtkTreeModel *model; GtkTreeIter iter; gpointer data; int active = -1; /* defaults to de-selecting active combo item */ const char *activestr; char *str; int i; /* minimize lock time - ref the combo box */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref combo box */ SWAMI_UNLOCK_READ (control); if (combo) { activestr = g_value_get_string (value); model = gtk_combo_box_get_model (combo); if (activestr && gtk_tree_model_get_iter_first (model, &iter)) { i = 0; /* look for matching combo text row */ do { gtk_tree_model_get (model, &iter, 0, &str, /* ++ alloc */ -1); if (strcmp (activestr, str) == 0) { g_free (str); /* -- free */ active = i; break; } g_free (str); /* -- free */ i++; } while (gtk_tree_model_iter_next (model, &iter)); g_signal_handlers_block_by_func (combo, combo_box_string_control_changed, control); gtk_combo_box_set_active (combo, active); g_signal_handlers_unblock_by_func (combo, combo_box_string_control_changed, control); } g_object_unref (combo); /* -- unref combo box */ } } /* string combo box changed callback to transmit control change to connected controls */ static void combo_box_string_control_changed (GtkComboBox *combo, gpointer user_data) { SwamiControl *control = SWAMI_CONTROL (user_data); GtkTreeModel *model; GtkTreeIter iter; GValue value = { 0 }; char *str; model = gtk_combo_box_get_model (combo); if (gtk_combo_box_get_active_iter (combo, &iter)) gtk_tree_model_get (model, &iter, 0, &str, /* ++ alloc */ -1); /* transmit the combo box change */ g_value_init (&value, G_TYPE_STRING); g_value_take_string (&value, str); /* !! takes string ownership */ swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } /* * Enum combo box control handler */ static SwamiControl * combo_box_enum_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control; GEnumClass *enumklass; GtkCellRenderer *renderer; GtkListStore *store; GtkTreeIter iter; char *s; guint i; g_return_val_if_fail (G_TYPE_FUNDAMENTAL (value_type) == G_TYPE_ENUM, NULL); store = gtk_list_store_new (1, G_TYPE_STRING); gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 0, NULL); enumklass = g_type_class_ref (value_type); /* ++ ref enum class */ /* add enum items to combo box */ for (i = 0; i < enumklass->n_values; i++) { gtk_list_store_append (store, &iter); s = g_strdup (enumklass->values[i].value_nick); /* ++ alloc string dup */ s[0] = toupper (s[0]); gtk_list_store_set (store, &iter, 0, _(s), -1); g_free (s); /* -- free string */ } g_type_class_unref (enumklass); /* -- unref enum class */ if (flags & SWAMIGUI_CONTROL_NO_CREATE) return (NULL); g_object_ref (widget); /* ++ ref button widget for control */ if (!pspec) pspec = g_param_spec_enum ("value", "value", "value", value_type, enumklass->minimum, G_PARAM_READWRITE); else g_param_spec_ref (pspec); /* ++ ref for swami_control_set_spec */ control = swami_control_func_new (); swami_control_set_spec ((SwamiControl *)control, pspec); swami_control_func_assign_funcs (control, combo_box_enum_control_get_func, combo_box_enum_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_widget_set_sensitive ((GtkWidget *)widget, TRUE); g_signal_connect (widget, "changed", G_CALLBACK (combo_box_enum_control_changed), control); } else gtk_widget_set_sensitive ((GtkWidget *)widget, FALSE); return (SWAMI_CONTROL (control)); } /* enum combo box handler SwamiControlFunc get value function */ static void combo_box_enum_control_get_func (SwamiControl *control, GValue *value) { GtkComboBox *combo = NULL; GEnumClass *enumklass; GParamSpec *pspec; gpointer data; int active,n_values; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref for outside of lock */ pspec = swami_control_get_spec (control); /* ++ ref param spec */ SWAMI_UNLOCK_READ (control); if (combo) { enumklass = g_type_class_ref (G_PARAM_SPEC_TYPE (pspec)); /* ++ ref enum class */ active = gtk_combo_box_get_active (combo); n_values = enumklass->n_values; if (active != -1 && active < n_values) g_value_set_enum (value, enumklass->values[active].value); g_type_class_unref (enumklass); /* -- unref enum class */ g_object_unref (combo); /* -- unref combo box */ } g_param_spec_unref (pspec); /* -- unref param spec */ } /* enum combo box handler SwamiControlFunc set value function */ static void combo_box_enum_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkComboBox *combo = NULL; GEnumClass *enumklass; GParamSpec *pspec; gpointer data; int val; guint pos; GType type; /* minimize lock time - ref the combo box */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref combo box */ pspec = swami_control_get_spec (control); /* ++ ref param spec */ SWAMI_UNLOCK_READ (control); type = G_PARAM_SPEC_VALUE_TYPE (pspec); g_param_spec_unref (pspec); /* -- unref param spec */ g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_ENUM); if (combo) { enumklass = g_type_class_ref (type); /* ++ ref enum class */ val = g_value_get_enum (value); for (pos = 0; pos < enumklass->n_values; pos++) if (enumklass->values[pos].value == val) break; if (pos < enumklass->n_values) { g_signal_handlers_block_by_func (combo, combo_box_enum_control_changed, control); gtk_combo_box_set_active (combo, pos); g_signal_handlers_unblock_by_func (combo, combo_box_enum_control_changed, control); } g_type_class_unref (enumklass); /* -- unref enum class */ g_object_unref (combo); /* -- unref combo box */ } } /* enum combo box changed callback to transmit control change to connected controls */ static void combo_box_enum_control_changed (GtkComboBox *combo, gpointer user_data) { SwamiControl *control = SWAMI_CONTROL (user_data); GEnumClass *enumklass; GParamSpec *pspec; GValue value = { 0 }; GType type; guint active; pspec = swami_control_get_spec (control); /* ++ ref param spec */ type = G_PARAM_SPEC_VALUE_TYPE (pspec); g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_ENUM); enumklass = g_type_class_ref (type); /* ++ ref enum class */ active = gtk_combo_box_get_active (combo); /* transmit the combo box change */ if (active < enumklass->n_values) { g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_value_set_enum (&value, enumklass->values[active].value); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } g_type_class_unref (enumklass); /* -- unref enum class */ g_param_spec_unref (pspec); /* -- unref param spec */ } /* * GType combo box control handler */ static SwamiControl * combo_box_gtype_control_handler (GObject *widget, GType value_type, GParamSpec *pspec, SwamiguiControlFlags flags) { SwamiControlFunc *control; GtkCellRenderer *renderer; GtkListStore *store; GtkTreeIter iter; GType basetype; GType *types; char *name, *free_icon, *icon_name; gint category; int i; g_return_val_if_fail (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_GTYPE, NULL); store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_GTYPE); gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (renderer, "xalign", 0.0, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "stock-id", 0, NULL); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "xalign", 0.0, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 1, NULL); basetype = ((GParamSpecGType *)pspec)->is_a_type; /* ++ alloc list of child types of base GType of parameter spec */ types = swami_util_get_child_types (basetype, NULL); /* add type info to combo box */ for (i = 0; types[i]; i++) { gtk_list_store_append (store, &iter); ipatch_type_get (types[i], "name", &name, // ++ alloc "icon", &free_icon, // ++ alloc "category", &category, NULL); if (!name) name = g_strdup (g_type_name (types[i])); /* ++ alloc string */ if (!free_icon) icon_name = swamigui_icon_get_category_icon (category); else icon_name = free_icon; gtk_list_store_set (store, &iter, 0, icon_name, 1, name, 2, types[i], -1); g_free (name); // Free name g_free (free_icon); // Free the stock icon name (if any) } g_free (types); /* -- free type array */ if (flags & SWAMIGUI_CONTROL_NO_CREATE) return (NULL); g_object_ref (widget); /* ++ ref button widget for control */ if (!pspec) pspec = g_param_spec_gtype ("value", "value", "value", basetype, G_PARAM_READWRITE); else g_param_spec_ref (pspec); /* ++ ref for swami_control_set_spec */ control = swami_control_func_new (); swami_control_set_spec ((SwamiControl *)control, pspec); swami_control_func_assign_funcs (control, combo_box_gtype_control_get_func, combo_box_gtype_control_set_func, NULL, widget); g_signal_connect (widget, "destroy", G_CALLBACK (func_control_cb_widget_destroy), control); if (flags & SWAMIGUI_CONTROL_CTRL) /* controllable? */ { gtk_widget_set_sensitive ((GtkWidget *)widget, TRUE); g_signal_connect (widget, "changed", G_CALLBACK (combo_box_gtype_control_changed), control); } else gtk_widget_set_sensitive ((GtkWidget *)widget, FALSE); return (SWAMI_CONTROL (control)); } /* GType combo box handler SwamiControlFunc get value function */ static void combo_box_gtype_control_get_func (SwamiControl *control, GValue *value) { GtkComboBox *combo = NULL; GtkTreeIter iter; GParamSpec *pspec; gpointer data; GType type; SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref for outside of lock */ pspec = swami_control_get_spec (control); /* ++ ref param spec */ SWAMI_UNLOCK_READ (control); if (combo) { if (gtk_combo_box_get_active_iter (combo, &iter)) { /* get the GType value stored for the active item */ gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter, 2, &type, -1); g_value_set_gtype (value, type); } g_object_unref (combo); /* -- unref combo box */ } g_param_spec_unref (pspec); /* -- unref param spec */ } /* GType combo box handler SwamiControlFunc set value function */ static void combo_box_gtype_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { GtkComboBox *combo = NULL; GtkTreeModel *model; gpointer data; GtkTreeIter iter; GType valtype, type; gboolean type_in_list = FALSE; /* minimize lock time - ref the combo box */ SWAMI_LOCK_READ (control); data = SWAMI_CONTROL_FUNC_DATA (control); if (data) combo = GTK_COMBO_BOX (g_object_ref (data)); /* ++ ref combo box */ SWAMI_UNLOCK_READ (control); if (!combo) return; model = gtk_combo_box_get_model (combo); if (gtk_tree_model_get_iter_first (model, &iter)) { valtype = g_value_get_gtype (value); /* look for matching combo box item by GType */ do { gtk_tree_model_get (model, &iter, 2, &type, -1); if (valtype == type) { g_signal_handlers_block_by_func (combo, combo_box_gtype_control_changed, control); gtk_combo_box_set_active_iter (combo, &iter); g_signal_handlers_unblock_by_func (combo, combo_box_gtype_control_changed, control); type_in_list = TRUE; break; } } while (gtk_tree_model_iter_next (model, &iter)); } g_object_unref (combo); /* -- unref combo box */ g_return_if_fail (type_in_list); } /* GType combo box changed callback to transmit control change to connected controls */ static void combo_box_gtype_control_changed (GtkComboBox *combo, gpointer user_data) { SwamiControl *control = SWAMI_CONTROL (user_data); GtkTreeModel *model; GValue value = { 0 }; GtkTreeIter iter; GType type; model = gtk_combo_box_get_model (combo); if (!gtk_combo_box_get_active_iter (combo, &iter)) return; /* get GType of active combo box item */ gtk_tree_model_get (model, &iter, 2, &type, -1); /* transmit the combo box change */ g_value_init (&value, G_TYPE_GTYPE); g_value_set_gtype (&value, type); swami_control_transmit_value ((SwamiControl *)control, &value); g_value_unset (&value); } swami-2.2.0/src/swamigui/SwamiguiDnd.h000066400000000000000000000020721361104770400176370ustar00rootroot00000000000000/* * SwamiguiDnd.h - Swami Drag and Drop stuff * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_DND_H__ #define __SWAMIGUI_DND_H__ enum { SWAMIGUI_DND_OBJECT_INFO, SWAMIGUI_DND_URI_INFO }; #define SWAMIGUI_DND_OBJECT_NAME "GObject-type" #define SWAMIGUI_DND_URI_NAME "text/uri-list" #endif swami-2.2.0/src/swamigui/SwamiguiItemMenu.c000066400000000000000000000637721361104770400206660ustar00rootroot00000000000000/* * SwamiguiTreeMenu.c - Swami Tree right-click menu object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiItemMenu.h" #include "SwamiguiRoot.h" #include "i18n.h" enum { PROP_0, PROP_SELECTION, /* the item selection list */ PROP_RIGHT_CLICK, /* the right click item */ PROP_CREATOR /* the widget creating the menu */ }; typedef struct { char *action_id; /* store the hash key, for convenience */ SwamiguiItemMenuInfo *info; SwamiguiItemMenuHandler handler; } ActionBag; typedef struct { GType type; /* type to match */ gboolean derived; /* TRUE if derived types should match also */ } TypeMatch; static void swamigui_item_menu_class_init (SwamiguiItemMenuClass *klass); static void type_match_list_free (gpointer data); static void swamigui_item_menu_set_property (GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_item_menu_get_property (GObject *obj, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_item_menu_init (SwamiguiItemMenu *menu); static void swamigui_item_menu_finalize (GObject *object); static void swamigui_item_menu_cb_deactivate (SwamiguiItemMenu *menu, gpointer user_data); static ActionBag *lookup_item_action_bag (const char *action_id); static void container_foreach_remove (GtkWidget *widg, gpointer data); static void make_action_list_GHFunc (gpointer key, gpointer value, gpointer user_data); static void swamigui_item_menu_callback_activate (GtkMenuItem *mitem, gpointer user_data); static void swamigui_item_menu_accel_activate_callback (gpointer user_data); /* keyboard accelerator group for item menu actions */ GtkAccelGroup *swamigui_item_menu_accel_group; /* hash of action ID string -> ActionBag */ G_LOCK_DEFINE_STATIC (menu_action_hash); static GHashTable *menu_action_hash = NULL; /* hash of action ID string -> GSList of TypeMatch (for including types) */ G_LOCK_DEFINE_STATIC (item_type_include_hash); static GHashTable *item_type_include_hash = NULL; /* hash of action ID string -> GSList of TypeMatch (for excluding types) */ G_LOCK_DEFINE_STATIC (item_type_exclude_hash); static GHashTable *item_type_exclude_hash = NULL; static GObjectClass *parent_class = NULL; void _swamigui_item_menu_init (void) { /* create key accelerator group */ swamigui_item_menu_accel_group = gtk_accel_group_new (); /* create menu action hash */ menu_action_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_free); /* create menu item type inclusion hash */ item_type_include_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, type_match_list_free); /* create menu item type exclusion hash */ item_type_exclude_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, type_match_list_free); } GType swamigui_item_menu_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiItemMenuClass), NULL, NULL, (GClassInitFunc) swamigui_item_menu_class_init, NULL, NULL, sizeof (SwamiguiItemMenu), 0, (GInstanceInitFunc) swamigui_item_menu_init, }; obj_type = g_type_register_static (GTK_TYPE_MENU, "SwamiguiItemMenu", &obj_info, 0); } return (obj_type); } static void swamigui_item_menu_class_init (SwamiguiItemMenuClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swamigui_item_menu_finalize; obj_class->get_property = swamigui_item_menu_get_property; obj_class->set_property = swamigui_item_menu_set_property; g_object_class_install_property (obj_class, PROP_SELECTION, g_param_spec_object ("selection", "selection", "selection", IPATCH_TYPE_LIST, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_RIGHT_CLICK, g_param_spec_object ("right-click", "right-click", "right-click", G_TYPE_OBJECT, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_CREATOR, g_param_spec_object ("creator", "creator", "creator", G_TYPE_OBJECT, G_PARAM_READWRITE)); } /* free a GSList of TypeMatch structures */ static void type_match_list_free (gpointer data) { GSList *p = (GSList *)data; for (; p; p = g_slist_delete_link (p, p)) g_free (p->data); } static void swamigui_item_menu_set_property (GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiItemMenu *menu = SWAMIGUI_ITEM_MENU (obj); GObject *object; switch (property_id) { case PROP_SELECTION: if (menu->selection) g_object_unref (menu->selection); object = g_value_get_object (value); g_return_if_fail (!object || IPATCH_IS_LIST (object)); menu->selection = (IpatchList *)g_object_ref (object); break; case PROP_RIGHT_CLICK: if (menu->rclick) g_object_unref (menu->rclick); menu->rclick = g_value_dup_object (value); break; case PROP_CREATOR: if (menu->creator) g_object_unref (menu->creator); menu->creator = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } static void swamigui_item_menu_get_property (GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiItemMenu *menu = SWAMIGUI_ITEM_MENU (obj); switch (property_id) { case PROP_SELECTION: g_value_set_object (value, (GObject *)(menu->selection)); break; case PROP_RIGHT_CLICK: g_value_set_object (value, (GObject *)(menu->rclick)); break; case PROP_CREATOR: g_value_set_object (value, (GObject *)(menu->creator)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } static void swamigui_item_menu_init (SwamiguiItemMenu *menu) { gtk_menu_set_accel_group (GTK_MENU (menu), swamigui_item_menu_accel_group); g_signal_connect (menu, "deactivate", G_CALLBACK (swamigui_item_menu_cb_deactivate), NULL); g_object_ref_sink (menu); // HACK: This is used as a popup menu which gets destroyed in deactivate signal handler } static void swamigui_item_menu_finalize (GObject *object) { SwamiguiItemMenu *menu = SWAMIGUI_ITEM_MENU (object); if (menu->selection) g_object_unref (menu->selection); if (menu->rclick) g_object_unref (menu->rclick); if (menu->creator) g_object_unref (menu->creator); menu->selection = NULL; menu->rclick = NULL; menu->creator = NULL; if (parent_class->finalize) parent_class->finalize (object); } // HACK: Popup menus appear to leak with gtk_menu_popup, so we destroy it here static void swamigui_item_menu_cb_deactivate (SwamiguiItemMenu *menu, gpointer user_data) { g_object_unref (menu); } /** * swamigui_item_menu_new: * * Create a new Swami item menu. * * Returns: New Swami item menu object with a ref count of 1. */ SwamiguiItemMenu * swamigui_item_menu_new (void) { return (SWAMIGUI_ITEM_MENU (g_object_new (SWAMIGUI_TYPE_ITEM_MENU, NULL))); } /** * swamigui_item_menu_add: * @menu: GUI menu to add item to * @info: Info describing new menu item to add * @action_id: The action ID string that is adding the menu item * * Add a menu item to a GUI menu. * * Returns: The new GtkMenuItem that was added to the menu. */ GtkWidget * swamigui_item_menu_add (SwamiguiItemMenu *menu, const SwamiguiItemMenuInfo *info, const char *action_id) { GtkWidget *mitem; guint key; GdkModifierType mods; char *accel_path; GList *list, *p; guint order; int index; g_return_val_if_fail (SWAMIGUI_IS_ITEM_MENU (menu), NULL); g_return_val_if_fail (info != NULL, NULL); if (info->icon) { GtkWidget *image; mitem = gtk_image_menu_item_new_with_mnemonic (_(info->label)); image = gtk_image_new_from_stock (info->icon, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), image); } else mitem = gtk_menu_item_new_with_mnemonic (_(info->label)); gtk_widget_show (mitem); g_object_set_data (G_OBJECT (mitem), "_order", GUINT_TO_POINTER (info->order)); g_object_set_data (G_OBJECT (mitem), "_func", (gpointer)(info->func)); /* connect menu item to callback function */ if (info->func) g_signal_connect (mitem, "activate", G_CALLBACK (swamigui_item_menu_callback_activate), info->data); /* parse key accelerator and add it to menu item */ if (info->accel) { gtk_accelerator_parse (info->accel, &key, &mods); accel_path = g_strdup_printf ("/%s", action_id); gtk_accel_map_add_entry (accel_path, key, mods); gtk_menu_item_set_accel_path (GTK_MENU_ITEM (mitem), accel_path); g_free (accel_path); } if (info->flags & SWAMIGUI_ITEM_MENU_INACTIVE) gtk_widget_set_sensitive (GTK_WIDGET (mitem), FALSE); list = gtk_container_get_children (GTK_CONTAINER (menu)); /* find insert position from order info parameter */ for (p = list, index = 0; p; p = p->next, index++) { order = GPOINTER_TO_INT (g_object_get_data (p->data, "_order")); if (info->order < order) break; } g_list_free (list); gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mitem, index); return (mitem); } /** * swamigui_item_menu_add_registered_info: * @menu: GUI menu to add item to * @action_id: The action ID string that is adding the menu item * * Add a menu item to a GUI menu using the default info added when the * @action_id was registered. * * Returns: The new GtkMenuItem that was added to the menu. */ GtkWidget * swamigui_item_menu_add_registered_info (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *found_action; g_return_val_if_fail (SWAMIGUI_IS_ITEM_MENU (menu), NULL); found_action = lookup_item_action_bag (action_id); g_return_val_if_fail (found_action != NULL, NULL); g_return_val_if_fail (found_action->info != NULL, NULL); return (swamigui_item_menu_add (menu, found_action->info, action_id)); } /** * swamigui_item_menu_add_registered_info_inactive: * @menu: GUI menu to add item to * @action_id: The action ID string that is adding the menu item * * Add an inactive menu item to a GUI menu using the default info added when the * @action_id was registered. * * Returns: The new GtkMenuItem that was added to the menu. */ GtkWidget * swamigui_item_menu_add_registered_info_inactive (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *found_action; SwamiguiItemMenuInfo info; g_return_val_if_fail (SWAMIGUI_IS_ITEM_MENU (menu), NULL); found_action = lookup_item_action_bag (action_id); g_return_val_if_fail (found_action != NULL, NULL); g_return_val_if_fail (found_action->info != NULL, NULL); memcpy (&info, found_action->info, sizeof (info)); info.flags |= SWAMIGUI_ITEM_MENU_INACTIVE; return (swamigui_item_menu_add (menu, &info, action_id)); } /* lookup a registered action by its ID */ static ActionBag * lookup_item_action_bag (const char *action_id) { ActionBag *bag; G_LOCK (menu_action_hash); bag = g_hash_table_lookup (menu_action_hash, (gpointer)action_id); G_UNLOCK (menu_action_hash); return (bag); } /** * swamigui_item_menu_generate: * @menu: GUI menu * * Generate a GUI menu by executing all registered item action handlers which * add items to the menu. Any existing items are removed before generating the * new menu. */ void swamigui_item_menu_generate (SwamiguiItemMenu *menu) { GSList *actions = NULL, *p; g_return_if_fail (SWAMIGUI_IS_ITEM_MENU (menu)); /* remove any existing items from the menu */ gtk_container_foreach (GTK_CONTAINER (menu), container_foreach_remove, menu); /* make a list so we can unlock the hash (prevent recursive locks) */ G_LOCK (menu_action_hash); g_hash_table_foreach (menu_action_hash, make_action_list_GHFunc, &actions); G_UNLOCK (menu_action_hash); for (p = actions; p; p = p->next) { ActionBag *bag = (ActionBag *)(p->data); /* if handler was supplied then execute it, otherwise create item using the item info set when action was registered (one or the other must be set). */ if (bag->handler) bag->handler (menu, bag->action_id); else swamigui_item_menu_add (menu, bag->info, bag->action_id); } g_slist_free (actions); } /* turn hash into a list */ static void make_action_list_GHFunc (gpointer key, gpointer value, gpointer user_data) { GSList **listp = (GSList **)user_data; *listp = g_slist_prepend (*listp, value); } static void container_foreach_remove (GtkWidget *widg, gpointer data) { gtk_container_remove (GTK_CONTAINER (data), widg); } /* callback when a menu item is activated */ static void swamigui_item_menu_callback_activate (GtkMenuItem *mitem, gpointer user_data) { SwamiguiItemMenuCallback callback; IpatchList *selection; callback = g_object_get_data (G_OBJECT (mitem), "_func"); g_return_if_fail (callback != NULL); /* ++ ref selection */ g_object_get (swamigui_root, "selection", &selection, NULL); if (!selection) return; (*callback)(selection, user_data); g_object_unref (selection); /* -- unref selection */ } /** * swamigui_register_item_menu_action: * @action_id: Menu item action ID (example: "paste", "new", "copy", etc), * should be a static string since it is used directly. * @info: Menu item info (optional if @handler specified). Structure is * used directly and so it should be static as well as the strings inside. * @handler: Function to call when generating a menu (may be %NULL in which * case the default handler is used). Handler function should determine * whether the registered action is appropriate for the current item * selection and right click item and add menu items as appropriate. * * Registers a menu action. */ void swamigui_register_item_menu_action (char *action_id, SwamiguiItemMenuInfo *info, SwamiguiItemMenuHandler handler) { ActionBag *bag; guint key; GdkModifierType mods; GClosure *closure; g_return_if_fail (action_id != NULL && strlen(action_id) > 0); g_return_if_fail (info != NULL || handler != NULL); bag = g_new (ActionBag, 1); bag->action_id = action_id; bag->info = info; bag->handler = handler; G_LOCK (menu_action_hash); g_hash_table_insert (menu_action_hash, (gpointer)action_id, bag); G_UNLOCK (menu_action_hash); if (info->accel) { /* parse the accelerator */ gtk_accelerator_parse (info->accel, &key, &mods); if (key != 0) /* valid accelerator? */ { /* create closure for callback and add accelerator to accel group */ closure = g_cclosure_new_swap ((GCallback)swamigui_item_menu_accel_activate_callback, info, NULL); gtk_accel_group_connect (swamigui_item_menu_accel_group, key, mods, GTK_ACCEL_VISIBLE, closure); } } } static void swamigui_item_menu_accel_activate_callback (gpointer user_data) { SwamiguiItemMenuInfo *info = (SwamiguiItemMenuInfo *)user_data; IpatchList *selection; g_return_if_fail (info->func != NULL); g_object_get (swamigui_root, "selection", &selection, NULL); info->func (selection, info->data); g_object_unref (selection); } /** * swamigui_lookup_item_menu_action: * @action_id: ID string of item menu action to lookup. * @info: Location to store pointer to registered info (or %NULL to ignore). * @handler: Location to store pointer to registered handler (or %NULL to * ignore). * * Lookup item action information registered by @action_id. * * Returns: %TRUE if action found by @action_id, %FALSE if not found. */ gboolean swamigui_lookup_item_menu_action (const char *action_id, SwamiguiItemMenuInfo **info, SwamiguiItemMenuHandler *handler) { ActionBag *bag; if (info) *info = NULL; if (handler) *handler = NULL; g_return_val_if_fail (action_id != NULL, FALSE); bag = lookup_item_action_bag (action_id); if (!bag) return (FALSE); if (info) *info = bag->info; if (handler) *handler = bag->handler; return (TRUE); } /** * swamigui_register_item_menu_include_type: * @action_id: The registered action ID string * @type: The type to add * @derived: Set to %TRUE if derived types should match also * * Adds a selection item type for inclusion for the given registered item * action. */ void swamigui_register_item_menu_include_type (const char *action_id, GType type, gboolean derived) { TypeMatch *typematch; GSList *list; g_return_if_fail (action_id != NULL); g_return_if_fail (type != 0); typematch = g_new (TypeMatch, 1); typematch->type = type; typematch->derived = derived; G_LOCK (item_type_include_hash); list = g_hash_table_lookup (item_type_include_hash, action_id); if (!list) { list = g_slist_append (list, typematch); g_hash_table_insert (item_type_include_hash, (gpointer)action_id, list); } // Assignment to keep warn_unused_result happy (append to non-empty list) else list = g_slist_append (list, typematch); G_UNLOCK (item_type_include_hash); } /** * swamigui_register_item_menu_exclude_type: * @action_id: The registered action ID string * @type: The type to add * @derived: Set to %TRUE if derived types should match also * * Adds a selection item type for exclusion for the given registered item * action. */ void swamigui_register_item_menu_exclude_type (const char *action_id, GType type, gboolean derived) { TypeMatch *typematch; GSList *list; g_return_if_fail (action_id != NULL); g_return_if_fail (type != 0); typematch = g_new (TypeMatch, 1); typematch->type = type; typematch->derived = derived; G_LOCK (item_type_exclude_hash); list = g_hash_table_lookup (item_type_exclude_hash, action_id); if (!list) { list = g_slist_append (list, typematch); g_hash_table_insert (item_type_exclude_hash, (gpointer)action_id, list); } // Assignment to keep warn_unused_result happy (append to non-empty list) else list = g_slist_append (list, typematch); G_UNLOCK (item_type_exclude_hash); } /** * swamigui_test_item_menu_type: * @action_id: Menu item action ID string * @type: Type to test * * Tests if a given item selection @type is in the include list and not in * the exclude list for @action_id. * * Returns: %TRUE if item is in include list and not in exclude list * for @action_id, %FALSE otherwise. */ gboolean swamigui_test_item_menu_type (const char *action_id, GType type) { return (swamigui_test_item_menu_include_type (action_id, type) && swamigui_test_item_menu_exclude_type (action_id, type)); } /** * swamigui_test_item_menu_include_type: * @action_id: Menu item action ID string * @type: Type to test * * Tests if a given item selection @type is in the include list for @action_id. * * Returns: %TRUE if item is in include list for @action_id, %FALSE otherwise. */ gboolean swamigui_test_item_menu_include_type (const char *action_id, GType type) { TypeMatch *match; GSList *p; g_return_val_if_fail (action_id != NULL, FALSE); g_return_val_if_fail (type != 0, FALSE); G_LOCK (item_type_include_hash); p = g_hash_table_lookup (item_type_include_hash, action_id); for (; p; p = p->next) { match = (TypeMatch *)(p->data); if ((match->derived && g_type_is_a (type, match->type)) || (!match->derived && type == match->type)) break; } G_UNLOCK (item_type_include_hash); return (p != NULL); } /** * swamigui_test_item_menu_exclude_type: * @action_id: Menu item action ID string * @type: Type to test * * Tests if a given item selection @type is not in the exclude list for * @action_id. * * Returns: %TRUE if item is not in exclude list for @action_id (should be * included), %FALSE otherwise. */ gboolean swamigui_test_item_menu_exclude_type (const char *action_id, GType type) { TypeMatch *match; GSList *p; g_return_val_if_fail (action_id != NULL, FALSE); g_return_val_if_fail (type != 0, FALSE); G_LOCK (item_type_exclude_hash); p = g_hash_table_lookup (item_type_exclude_hash, action_id); for (; p; p = p->next) { match = (TypeMatch *)(p->data); if ((match->derived && g_type_is_a (type, match->type)) || (!match->derived && type == match->type)) break; } G_UNLOCK (item_type_exclude_hash); return (p == NULL); } /** * swamigui_item_menu_get_selection_single: * @menu: GUI menu object * * Test if a menu object has a single selected item and return it if so. * * Returns: The single selected item object or %NULL if not a single selection. * Returned object does NOT have a reference added and should be referenced * if used outside of calling function. */ GObject * swamigui_item_menu_get_selection_single (SwamiguiItemMenu *menu) { g_return_val_if_fail (SWAMIGUI_IS_ITEM_MENU (menu), FALSE); if (menu->selection && menu->selection->items && !menu->selection->items->next) return (menu->selection->items->data); else return (NULL); } /** * swamigui_item_menu_get_selection: * @menu: GUI menu object * * Test if a menu object has any selected items and return the selection list * if so. * * Returns: Selected items or %NULL if no selection object. No reference is * added for caller, so caller must reference if used outside of calling * context. */ IpatchList * swamigui_item_menu_get_selection (SwamiguiItemMenu *menu) { g_return_val_if_fail (SWAMIGUI_IS_ITEM_MENU (menu), NULL); if (menu->selection) return (menu->selection); return (NULL); } /** * swamigui_item_menu_handler_single: * @menu: The menu object * @action_id: The action ID * * A #SwamiguiItemMenuHandler type that can be used when registering a menu * action. It will add a single menu item, using the info provided during * action registration, if a single item is selected and is of a type found * in the include type list and not found in exclude list. */ void swamigui_item_menu_handler_single (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *bag; GObject *item; g_return_if_fail (SWAMIGUI_IS_ITEM_MENU (menu)); g_return_if_fail (action_id != NULL); /* make sure there is only 1 item selected */ item = swamigui_item_menu_get_selection_single (menu); if (!item) return; /* item type is not in include list or in exclude list? - then return */ if (!swamigui_test_item_menu_type (action_id, G_OBJECT_TYPE (item))) return; bag = lookup_item_action_bag (action_id); /* lookup the registered action */ /* add a menu item from the registered info */ swamigui_item_menu_add (menu, bag->info, action_id); } /** * swamigui_item_menu_handler_multi: * @menu: The menu object * @action_id: The action ID * * A #SwamiguiItemMenuHandler type that can be used when registering a menu * action. It will add a single menu item, using the info provided during * action registration, if a single item is selected and is of a type found * in the include type list and not in exclude list or multiple items are * selected. */ void swamigui_item_menu_handler_multi (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *bag; IpatchList *list; GList *items; g_return_if_fail (SWAMIGUI_IS_ITEM_MENU (menu)); g_return_if_fail (action_id != NULL); /* make sure there is at least 1 item selected */ list = swamigui_item_menu_get_selection (menu); if (!list || !list->items) return; items = list->items; /* if only 1 item is selected.. */ if (!items->next) { GObject *item = G_OBJECT (items->data); /* item type is not in include list or in exclude list? - then return */ if (!swamigui_test_item_menu_type (action_id, G_OBJECT_TYPE (item))) return; } bag = lookup_item_action_bag (action_id); /* lookup the registered action */ /* add a menu item from the registered info */ swamigui_item_menu_add (menu, bag->info, action_id); } /** * swamigui_item_menu_handler_single_all: * @menu: The menu object * @action_id: The action ID * * A #SwamiguiItemMenuHandler type that can be used when registering a menu * action. It will add a single menu item, using the info provided during * action registration, if there is a single item selected of any type. */ void swamigui_item_menu_handler_single_all (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *bag; g_return_if_fail (SWAMIGUI_IS_ITEM_MENU (menu)); g_return_if_fail (action_id != NULL); /* make sure there is only 1 item selected */ if (!swamigui_item_menu_get_selection_single (menu)) return; bag = lookup_item_action_bag (action_id); /* lookup the registered action */ /* add a menu item from the registered info */ swamigui_item_menu_add (menu, bag->info, action_id); } /** * swamigui_item_menu_handler_multi_all: * @menu: The menu object * @action_id: The action ID * * A #SwamiguiItemMenuHandler type that can be used when registering a menu * action. It will add a single menu item, using the info provided during * action registration, if there is at least one item selected of any type. */ void swamigui_item_menu_handler_multi_all (SwamiguiItemMenu *menu, const char *action_id) { ActionBag *bag; IpatchList *list; g_return_if_fail (SWAMIGUI_IS_ITEM_MENU (menu)); g_return_if_fail (action_id != NULL); /* make sure there is at least 1 item selected */ list = swamigui_item_menu_get_selection (menu); if (!list || !list->items) return; bag = lookup_item_action_bag (action_id); /* lookup the registered action */ /* add a menu item from the registered info */ swamigui_item_menu_add (menu, bag->info, action_id); } swami-2.2.0/src/swamigui/SwamiguiItemMenu.h000066400000000000000000000124421361104770400206570ustar00rootroot00000000000000/* * SwamiguiItemMenu.h - Swami item action (right click) menu routines * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_ITEM_MENU_H__ #define __SWAMIGUI_ITEM_MENU_H__ #include typedef struct _SwamiguiItemMenu SwamiguiItemMenu; typedef struct _SwamiguiItemMenuClass SwamiguiItemMenuClass; typedef struct _SwamiguiItemMenuInfo SwamiguiItemMenuInfo; typedef struct _SwamiguiItemMenuEntry SwamiguiItemMenuEntry; #define SWAMIGUI_TYPE_ITEM_MENU (swamigui_item_menu_get_type ()) #define SWAMIGUI_ITEM_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_ITEM_MENU, \ SwamiguiItemMenu)) #define SWAMIGUI_ITEM_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_ITEM_MENU, \ SwamiguiItemMenuClass)) #define SWAMIGUI_IS_ITEM_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_ITEM_MENU)) #define SWAMIGUI_IS_ITEM_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_ITEM_MENU)) /** * SwamiguiItemMenuCallback: * @selection: Item selection * @data: Data defined by the menu item * * A callback function type that is used when a menu item is activated. */ typedef void (*SwamiguiItemMenuCallback)(IpatchList *selection, gpointer data); /** * SwamiguiItemMenuHandler: * @menu: GUI menu object * @action_id: Menu action ID (set when action was registered) * * A handler for a menu item type. Called when generating a menu for an * item selection and right click item. This function should determine if * its action type (example: paste, delete, copy, new, etc) is valid for the * given selection and add one or more menu items if so. */ typedef void (*SwamiguiItemMenuHandler)(SwamiguiItemMenu *menu, const char *action_id); typedef enum /*< flags >*/ { SWAMIGUI_ITEM_MENU_INACTIVE = 1 << 0, /* set if menu item should be inactive */ SWAMIGUI_ITEM_MENU_PLUGIN = 1 << 1 /* set if menu item is for a plugin */ } SwamiguiItemMenuFlags; /* menu item info */ struct _SwamiguiItemMenuInfo { guint order; /* an integer used to sort items (lower values first) */ char *label; /* menu label text */ char *accel; /* key accelerator */ char *icon; /* stock ID of icon */ guint flags; /* SwamiguiItemMenuFlags */ SwamiguiItemMenuCallback func; /* function to call when item is activated */ gpointer data; /* data to pass to callback function */ }; struct _SwamiguiItemMenu { GtkMenu parent_instance; IpatchList *selection; /* current item selection or NULL */ GObject *rclick; /* current right click item or NULL */ GObject *creator; /* object that created menu (SwamiguiTree for example) */ }; struct _SwamiguiItemMenuClass { GtkMenuClass parent_class; }; extern GtkAccelGroup *swamigui_item_menu_accel_group; GType swamigui_item_menu_get_type (); SwamiguiItemMenu *swamigui_item_menu_new (); GtkWidget *swamigui_item_menu_add (SwamiguiItemMenu *menu, const SwamiguiItemMenuInfo *info, const char *action_id); GtkWidget *swamigui_item_menu_add_registered_info (SwamiguiItemMenu *menu, const char *action_id); GtkWidget *swamigui_item_menu_add_registered_info_inactive (SwamiguiItemMenu *menu, const char *action_id); void swamigui_item_menu_generate (SwamiguiItemMenu *menu); void swamigui_register_item_menu_action (char *action_id, SwamiguiItemMenuInfo *info, SwamiguiItemMenuHandler handler); gboolean swamigui_lookup_item_menu_action (const char *action_id, SwamiguiItemMenuInfo **info, SwamiguiItemMenuHandler *handler); void swamigui_register_item_menu_include_type (const char *action_id, GType type, gboolean derived); void swamigui_register_item_menu_exclude_type (const char *action_id, GType type, gboolean derived); gboolean swamigui_test_item_menu_type (const char *action_id, GType type); gboolean swamigui_test_item_menu_include_type (const char *action_id, GType type); gboolean swamigui_test_item_menu_exclude_type (const char *action_id, GType type); GObject *swamigui_item_menu_get_selection_single (SwamiguiItemMenu *menu); IpatchList *swamigui_item_menu_get_selection (SwamiguiItemMenu *menu); void swamigui_item_menu_handler_single (SwamiguiItemMenu *menu, const char *action_id); void swamigui_item_menu_handler_multi (SwamiguiItemMenu *menu, const char *action_id); void swamigui_item_menu_handler_single_all (SwamiguiItemMenu *menu, const char *action_id); void swamigui_item_menu_handler_multi_all (SwamiguiItemMenu *menu, const char *action_id); #endif swami-2.2.0/src/swamigui/SwamiguiItemMenu_actions.c000066400000000000000000000347611361104770400224020ustar00rootroot00000000000000/* * SwamiguiTreeMenu_items.c - Built in menu items * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiItemMenu.h" #include "icons.h" #include "patch_funcs.h" #include "i18n.h" static void item_action_paste_handler (SwamiguiItemMenu *menu, const char *action_id); static void item_action_goto_link_handler (SwamiguiItemMenu *menu, const char *action_id); static void item_action_load_samples_handler (SwamiguiItemMenu *menu, const char *action_id); static void item_action_new_handler (SwamiguiItemMenu *menu, const char *action_id); static void item_cb_paste_items (IpatchList *selection, gpointer data); static void item_cb_find_item (IpatchList *selection, gpointer data); static void item_cb_find_next_item (IpatchList *selection, gpointer data); static void item_cb_goto_link_item (IpatchList *selection, gpointer data); static void item_cb_load_samples (IpatchList *selection, gpointer data); static void item_cb_new (IpatchList *selection, gpointer data); static void item_cb_solo_toggle (IpatchList *selection, gpointer data); typedef struct { char *action_id; SwamiguiItemMenuHandler handler; SwamiguiItemMenuInfo info; } MultiActionInfo; MultiActionInfo item_action_info[] = { { "copy", swamigui_item_menu_handler_multi, { 100, N_("_Copy"), "C", GTK_STOCK_COPY, 0, (SwamiguiItemMenuCallback)swamigui_copy_items, NULL } }, { "paste", item_action_paste_handler, { 200, N_("_Paste"), "V", GTK_STOCK_PASTE, 0, (SwamiguiItemMenuCallback)item_cb_paste_items, NULL } }, { "delete", swamigui_item_menu_handler_multi, { 300, N_("_Delete"), "D", GTK_STOCK_DELETE, 0, (SwamiguiItemMenuCallback)swamigui_delete_items, NULL } }, { "find", swamigui_item_menu_handler_single_all, { 400, N_("_Find"), "F", GTK_STOCK_FIND, 0, item_cb_find_item, NULL } }, { "find-next", swamigui_item_menu_handler_single_all, { 410, N_("Find _Next"), "G", GTK_STOCK_FIND, 0, item_cb_find_next_item, NULL } }, { "goto-link", item_action_goto_link_handler, { 500, N_("_Goto Link"), "L", GTK_STOCK_JUMP_TO, 0, item_cb_goto_link_item, NULL } }, { "save", swamigui_item_menu_handler_multi, { 600, N_("_Save"), "S", GTK_STOCK_SAVE, 0, (SwamiguiItemMenuCallback)swamigui_save_files, GINT_TO_POINTER (FALSE) } }, { "save-as", swamigui_item_menu_handler_multi, { 700, N_("Save _As"), NULL, GTK_STOCK_SAVE_AS, 0, (SwamiguiItemMenuCallback)swamigui_save_files, GINT_TO_POINTER(TRUE) } }, { "close", swamigui_item_menu_handler_multi, { 800, N_("Close"), "W", GTK_STOCK_CLOSE, 0, (SwamiguiItemMenuCallback)swamigui_close_files, NULL } }, { "load-samples", item_action_load_samples_handler, { 900, N_("Load Samples"), NULL, GTK_STOCK_OPEN, 0, item_cb_load_samples, NULL } }, { "export-samples", swamigui_item_menu_handler_multi, { 1000, N_("Export Samples"), NULL, GTK_STOCK_SAVE, 0, (SwamiguiItemMenuCallback)swamigui_export_samples, NULL } }, { "solo-item", swamigui_item_menu_handler_single, { 1100, N_("Solo Toggle"), "T", GTK_STOCK_MEDIA_PLAY, 0, (SwamiguiItemMenuCallback)item_cb_solo_toggle, NULL } }, /* some of these fields are dynamically generated by the handler */ { "new", item_action_new_handler, { 1100, "", NULL, GTK_STOCK_NEW, 0, item_cb_new, NULL } } }; void _swamigui_item_menu_actions_init (void) { GValue value = { 0 }; GType *types; guint count, i; /* register the item actions */ for (i = 0; i < G_N_ELEMENTS (item_action_info); i++) { swamigui_register_item_menu_action (item_action_info[i].action_id, &item_action_info[i].info, item_action_info[i].handler); } swamigui_register_item_menu_include_type ("copy", IPATCH_TYPE_ITEM, TRUE); swamigui_register_item_menu_exclude_type ("copy", IPATCH_TYPE_VIRTUAL_CONTAINER, TRUE); swamigui_register_item_menu_include_type ("paste", IPATCH_TYPE_CONTAINER, TRUE); swamigui_register_item_menu_include_type ("paste", IPATCH_TYPE_VIRTUAL_CONTAINER, TRUE); swamigui_register_item_menu_include_type ("delete", IPATCH_TYPE_ITEM, TRUE); swamigui_register_item_menu_exclude_type ("delete", IPATCH_TYPE_BASE, TRUE); swamigui_register_item_menu_exclude_type ("delete", IPATCH_TYPE_VIRTUAL_CONTAINER, TRUE); swamigui_register_item_menu_include_type ("export-samples", IPATCH_TYPE_SAMPLE, TRUE); swamigui_register_item_menu_include_type ("save", IPATCH_TYPE_ITEM, TRUE); swamigui_register_item_menu_include_type ("save-as", IPATCH_TYPE_ITEM, TRUE); swamigui_register_item_menu_include_type ("close", IPATCH_TYPE_BASE, TRUE); /* Add types for solo-item action to instrument ref and sample ref category types */ g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, IPATCH_CATEGORY_INSTRUMENT_REF); types = ipatch_type_find_types_with_property ("category", &value, &count); /* ++ alloc types */ if (types) { for (i = 0; i < count; i++) swamigui_register_item_menu_include_type ("solo-item", types[i], TRUE); g_free (types); /* -- unref types */ } g_value_set_int (&value, IPATCH_CATEGORY_SAMPLE_REF); types = ipatch_type_find_types_with_property ("category", &value, &count); /* ++ alloc types */ if (types) { for (i = 0; i < count; i++) swamigui_register_item_menu_include_type ("solo-item", types[i], TRUE); g_free (types); /* -- unref types */ } #if 0 { "wavetbl-load", N_("Wavetable Load"), "L", 0, swamigui_ifaces_cb_wtbl_load_patch, NULL } #endif } static void item_action_paste_handler (SwamiguiItemMenu *menu, const char *action_id) { GObject *item; item = swamigui_item_menu_get_selection_single (menu); if (!item) return; /* if item type matches an exclude type - return */ if (!swamigui_test_item_menu_exclude_type (action_id, G_OBJECT_TYPE (item))) return; /* if item does not match an include type.. */ if (!swamigui_test_item_menu_include_type (action_id, G_OBJECT_TYPE (item))) { GType link_type; /* IpatchItem's with a link type are valid */ ipatch_type_get (G_OBJECT_TYPE (item), "link-type", &link_type, NULL); if (!IPATCH_IS_ITEM (item) || link_type == G_TYPE_NONE) return; } swamigui_item_menu_add_registered_info (menu, action_id); } static void item_action_goto_link_handler (SwamiguiItemMenu *menu, const char *action_id) { IpatchList *list; GObject *item, *origin; list = swamigui_item_menu_get_selection (menu); /* make sure exactly one item is selected */ if (!list || !list->items || list->items->next) return; /* only makes sense to goto link if a tree is the origin of selection */ origin = swami_object_get_origin (G_OBJECT (list)); // ++ ref origin object if (!origin || !SWAMIGUI_IS_TREE (origin)) { if (origin) g_object_unref (origin); // -- unref origin object return; } g_object_unref (origin); // -- unref origin object item = (GObject *)(list->items->data); /* if item type matches an exclude type - return */ if (!swamigui_test_item_menu_exclude_type (action_id, G_OBJECT_TYPE (item))) return; /* if item does not match an include type.. */ if (!swamigui_test_item_menu_include_type (action_id, G_OBJECT_TYPE (item))) { GType link_type; GObject *link; /* if object has link-type type property and link-item property is set, create menu item */ ipatch_type_get (G_OBJECT_TYPE (item), "link-type", &link_type, NULL); if (!IPATCH_IS_ITEM (item) || link_type == G_TYPE_NONE) return; g_object_get (item, "link-item", &link, NULL); if (link) g_object_unref (link); if (!link) return; } swamigui_item_menu_add_registered_info (menu, action_id); } static void item_action_load_samples_handler (SwamiguiItemMenu *menu, const char *action_id) { GObject *item; const GType *child_types, child; int category; item = swamigui_item_menu_get_selection_single (menu); if (!item) return; /* if item type matches an exclude type - return */ if (!swamigui_test_item_menu_exclude_type (action_id, G_OBJECT_TYPE (item))) return; /* if item does not match an include type.. */ if (!swamigui_test_item_menu_include_type (action_id, G_OBJECT_TYPE (item))) { /* virtual container which contains sample category items? - Add */ if (IPATCH_IS_VIRTUAL_CONTAINER (item)) { ipatch_type_get (G_OBJECT_TYPE (item), "virtual-child-type", &child, NULL); if (child == G_TYPE_NONE) return; ipatch_type_get (child, "category", &category, NULL); if (category != IPATCH_CATEGORY_SAMPLE) return; } else /* IpatchBase derived and has child item with sample category? - add */ { if (!IPATCH_IS_BASE (item)) return; child_types = ipatch_container_get_child_types (IPATCH_CONTAINER (item)); for (; *child_types; child_types++) { ipatch_type_get (*child_types, "category", &category, NULL); if (category == IPATCH_CATEGORY_SAMPLE) break; } if (!*child_types) return; } } swamigui_item_menu_add_registered_info (menu, action_id); } static void item_action_new_handler (SwamiguiItemMenu *menu, const char *action_id) { GObject *item; const GType *child_types; GType type; SwamiguiItemMenuInfo newinfo, *infop; char *type_name; int category; item = swamigui_item_menu_get_selection_single (menu); if (!item) return; /* if item type matches an exclude type - return */ if (!swamigui_test_item_menu_exclude_type (action_id, G_OBJECT_TYPE (item))) return; if (IPATCH_IS_VIRTUAL_CONTAINER (item)) { swamigui_lookup_item_menu_action (action_id, &infop, NULL); newinfo = *infop; ipatch_type_get (G_OBJECT_TYPE (item), "virtual-child-type", &type, NULL); if (type == G_TYPE_NONE) return; /* only New for Program and Instrument types */ ipatch_type_get (type, "category", &category, NULL); if (category != IPATCH_CATEGORY_PROGRAM && category != IPATCH_CATEGORY_INSTRUMENT) return; ipatch_type_get (type, "name", &type_name, NULL); newinfo.label = g_strdup_printf (_("New %s"), type_name ? type_name : g_type_name (type)); g_free (type_name); newinfo.data = GSIZE_TO_POINTER (type); swamigui_item_menu_add (menu, &newinfo, action_id); g_free (newinfo.label); return; } if (!IPATCH_IS_CONTAINER (item)) return; swamigui_lookup_item_menu_action (action_id, &infop, NULL); newinfo = *infop; child_types = ipatch_container_get_child_types (IPATCH_CONTAINER (item)); for (; *child_types; child_types++) { /* skip any types in exclude list */ if (!swamigui_test_item_menu_exclude_type (action_id, *child_types)) continue; /* only New for Program and Instrument types */ ipatch_type_get (*child_types, "category", &category, NULL); if (category != IPATCH_CATEGORY_PROGRAM && category != IPATCH_CATEGORY_INSTRUMENT) continue; newinfo.order++; ipatch_type_get (*child_types, "name", &type_name, NULL); newinfo.label = g_strdup_printf (_("New %s"), type_name ? type_name : g_type_name (*child_types)); g_free (type_name); newinfo.data = GSIZE_TO_POINTER (*child_types); swamigui_item_menu_add (menu, &newinfo, action_id); g_free (newinfo.label); } } /* SwamiguiItemMenu action callbacks */ static void item_cb_paste_items (IpatchList *selection, gpointer data) { if (!selection->items || selection->items->next) return; swamigui_paste_items (IPATCH_ITEM (selection->items->data), NULL); } static void item_cb_find_item (IpatchList *selection, gpointer data) { GObject *origin; origin = swami_object_get_origin (G_OBJECT (selection)); // ++ ref origin object if (!origin || !SWAMIGUI_IS_TREE (origin)) { if (origin) g_object_unref (origin); // -- unref origin object return; } swamigui_tree_search_set_visible (SWAMIGUI_TREE (origin), TRUE); g_object_unref (origin); /* -- unref */ } static void item_cb_find_next_item (IpatchList *selection, gpointer data) { GObject *origin; origin = swami_object_get_origin (G_OBJECT (selection)); // ++ ref origin object if (!origin || !SWAMIGUI_IS_TREE (origin)) { if (origin) g_object_unref (origin); // -- unref origin object return; } swamigui_tree_search_next (SWAMIGUI_TREE (origin)); g_object_unref (origin); /* -- unref */ } static void item_cb_goto_link_item (IpatchList *selection, gpointer data) { GObject *origin; if (!selection->items || selection->items->next) return; origin = swami_object_get_origin (G_OBJECT (selection)); // ++ ref origin object if (!origin || !SWAMIGUI_IS_TREE (origin)) { if (origin) g_object_unref (origin); // -- unref origin object return; } swamigui_goto_link_item (IPATCH_ITEM (selection->items->data), SWAMIGUI_TREE (origin)); g_object_unref (origin); /* -- unref */ } static void item_cb_load_samples (IpatchList *selection, gpointer data) { if (!selection->items || selection->items->next) return; swamigui_load_files (G_OBJECT (selection->items->data), TRUE); } static void item_cb_new (IpatchList *selection, gpointer data) { if (!selection->items || selection->items->next) return; swamigui_new_item (IPATCH_ITEM (selection->items->data), (GType)GPOINTER_TO_SIZE (data)); } static void item_cb_solo_toggle (IpatchList *selection, gpointer data) { gboolean enabled; if (!selection->items || selection->items->next) return; /* Toggle solo-item-enable property of swamiguiroot */ g_object_get (swamigui_root, "solo-item-enable", &enabled, NULL); g_object_set (swamigui_root, "solo-item-enable", !enabled, NULL); } swami-2.2.0/src/swamigui/SwamiguiKnob.c000066400000000000000000000217641361104770400200270ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "config.h" #include "SwamiguiKnob.h" #include "util.h" /* PNG image dimensions */ #define KNOB_WIDTH 40 #define KNOB_HEIGHT 40 #define KNOB_SIZE_REQ 40 /* Default knob request size (width and height) */ /* Indicator scale to knob width */ #define RADIUS_WIDTH_SCALE (1.0/3.0) /* default rotation rates in pixels/radians */ #define DEFAULT_ROTATION_RATE (140.0 / (2 * M_PI)) #define DEFAULT_ROTATION_RATE_FINE (1000.0 / (2 * M_PI)) G_DEFINE_TYPE (SwamiguiKnob, swamigui_knob, GTK_TYPE_DRAWING_AREA); static gboolean swamigui_knob_expose (GtkWidget *widget, GdkEventExpose *event); static void swamigui_knob_update (SwamiguiKnob *knob); static gboolean swamigui_knob_button_press_event (GtkWidget *widget, GdkEventButton *event); static gboolean swamigui_knob_button_release_event (GtkWidget *widget, GdkEventButton *event); static gboolean swamigui_knob_motion_notify_event (GtkWidget *widget, GdkEventMotion *event); static void swamigui_knob_adj_value_changed (GtkAdjustment *adj, gpointer user_data); GdkPixbuf *knob_pixbuf = NULL; static void swamigui_knob_class_init (SwamiguiKnobClass *class) { GtkWidgetClass *widget_class; widget_class = GTK_WIDGET_CLASS (class); widget_class->expose_event = swamigui_knob_expose; widget_class->button_press_event = swamigui_knob_button_press_event; widget_class->button_release_event = swamigui_knob_button_release_event; widget_class->motion_notify_event = swamigui_knob_motion_notify_event; } static void swamigui_knob_init (SwamiguiKnob *knob) { if (!knob_pixbuf) { GError *err = NULL; gchar *resdir, *filename; /* ++ alloc resdir */ resdir = swamigui_util_get_resource_path (SWAMIGUI_RESOURCE_PATH_IMAGES); /* ++ alloc filename */ filename = g_build_filename (resdir, "knob.png", NULL); g_free (resdir); /* -- free resdir */ knob_pixbuf = gdk_pixbuf_new_from_file (filename, &err); if (!knob_pixbuf) { g_critical ("Failed to open SVG knob file '%s': %s", filename, err ? err->message : "No error details"); g_clear_error (&err); } g_free (filename); /* -- free allocated filename */ } gtk_widget_set_size_request (GTK_WIDGET (knob), KNOB_SIZE_REQ, KNOB_SIZE_REQ); knob->adj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.10, 0.0)); knob->start_pos = -150.0 * M_PI / 180.0; knob->end_pos = -knob->start_pos; knob->rotation = knob->start_pos; knob->rotation_rate = DEFAULT_ROTATION_RATE; knob->rotation_rate_fine = DEFAULT_ROTATION_RATE_FINE; g_signal_connect (knob->adj, "value-changed", G_CALLBACK (swamigui_knob_adj_value_changed), knob); gtk_widget_set_events (GTK_WIDGET (knob), GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); } static gboolean swamigui_knob_expose (GtkWidget *widget, GdkEventExpose *event) { SwamiguiKnob *knob = SWAMIGUI_KNOB (widget); static guint cache_width = 0, cache_height = 0; static cairo_surface_t *knob_background = NULL; static double radius = 0.0; double halfwidth, halfheight; cairo_t *cr; if (!knob_background || cache_width != widget->allocation.width || cache_height != widget->allocation.height) { if (knob_background) cairo_surface_destroy (knob_background); cache_width = widget->allocation.width; cache_height = widget->allocation.height; radius = (double)cache_width * RADIUS_WIDTH_SCALE; knob_background = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cache_width, cache_height); cr = cairo_create (knob_background); cairo_scale (cr, cache_width / (double)KNOB_WIDTH, cache_height / (double)KNOB_HEIGHT); gdk_cairo_set_source_pixbuf (cr, knob_pixbuf, 0.0, 0.0); cairo_paint (cr); cairo_destroy (cr); } halfwidth = cache_width / 2.0; halfheight = cache_height / 2.0; cr = gdk_cairo_create (widget->window); /* ++ create a cairo_t */ /* clip to the exposed area */ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height); cairo_clip (cr); /* draw knob background */ cairo_set_source_surface (cr, knob_background, 0.0, 0.0); cairo_paint (cr); /* set line properties */ cairo_set_line_width (cr, 2.0); cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); /* draw the line */ cairo_line_to (cr, halfwidth, halfheight); cairo_line_to (cr, halfwidth + radius * sin (knob->rotation), halfheight - radius * cos (knob->rotation)); cairo_stroke (cr); cairo_destroy (cr); /* -- free cairo_t */ return (FALSE); } /* update the knob by forcing an expose */ static void swamigui_knob_update (SwamiguiKnob *knob) { GdkRegion *region; GtkWidget *widget = GTK_WIDGET (knob); if (!widget->window) return; region = gdk_drawable_get_clip_region (widget->window); /* redraw the cairo canvas completely by exposing it */ gdk_window_invalidate_region (widget->window, region, TRUE); gdk_window_process_updates (widget->window, TRUE); gdk_region_destroy (region); } static gboolean swamigui_knob_button_press_event (GtkWidget *widget, GdkEventButton *event) { SwamiguiKnob *knob = SWAMIGUI_KNOB (widget); if (event->type != GDK_BUTTON_PRESS || event->button != 1) return (FALSE); if (gdk_pointer_grab (widget->window, FALSE, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, NULL, NULL, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) return (TRUE); knob->rotation_active = TRUE; knob->xclick = event->x; knob->yclick = event->y; knob->click_rotation = knob->rotation; return (TRUE); } static gboolean swamigui_knob_button_release_event (GtkWidget *widget, GdkEventButton *event) { SwamiguiKnob *knob = SWAMIGUI_KNOB (widget); if (event->type != GDK_BUTTON_RELEASE || event->button != 1) return (FALSE); knob->rotation_active = FALSE; gdk_pointer_ungrab (GDK_CURRENT_TIME); return (TRUE); } static gboolean swamigui_knob_motion_notify_event (GtkWidget *widget, GdkEventMotion *event) { SwamiguiKnob *knob = SWAMIGUI_KNOB (widget); double rotation, normval; if (!widget->window || !knob->rotation_active) return (TRUE); /* calculate rotation amount (SHIFT key causes a finer rotation) */ rotation = knob->click_rotation + (knob->yclick - event->y) / ((event->state & GDK_SHIFT_MASK) ? knob->rotation_rate_fine : knob->rotation_rate); rotation = CLAMP (rotation, knob->start_pos, knob->end_pos); if (rotation == knob->rotation) return (TRUE); knob->rotation = rotation; /* normalize rotation to a 0.0 to 1.0 position */ normval = (rotation - knob->start_pos) / (knob->end_pos - knob->start_pos); /* update the adjustment value */ knob->adj->value = normval * (knob->adj->upper - knob->adj->lower) + knob->adj->lower; /* emit value-changed signal (block our own handler) */ g_signal_handlers_block_by_func (knob->adj, swamigui_knob_adj_value_changed, knob); gtk_adjustment_value_changed (knob->adj); g_signal_handlers_unblock_by_func (knob->adj, swamigui_knob_adj_value_changed, knob); /* update knob visual */ swamigui_knob_update (knob); return (TRUE); } /* callback which gets called when the knob adjustment value changes externally */ static void swamigui_knob_adj_value_changed (GtkAdjustment *adj, gpointer user_data) { SwamiguiKnob *knob = SWAMIGUI_KNOB (user_data); gdouble normval; /* normalize the adjustment value to 0.0 through 1.0 */ normval = (adj->value - adj->lower) / (adj->upper - adj->lower); normval = CLAMP (normval, 0.0, 1.0); /* convert to a rotation amount */ knob->rotation = normval * (knob->end_pos - knob->start_pos) + knob->start_pos; swamigui_knob_update (knob); /* update knob visual */ } /** * swamigui_knob_new: * * Create a new knob widget. * * Returns: Knob widget */ GtkWidget * swamigui_knob_new (void) { return (g_object_new (SWAMIGUI_TYPE_KNOB, NULL)); } /** * swamigui_knob_get_adjustment: * @knob: Swamigui knob widget * * Get the #GtkAdjustment associated with a knob. * * Returns: The #GtkAdjustment for the knob */ GtkAdjustment * swamigui_knob_get_adjustment (SwamiguiKnob *knob) { g_return_val_if_fail (SWAMIGUI_IS_KNOB (knob), NULL); return (GTK_ADJUSTMENT (knob->adj)); } swami-2.2.0/src/swamigui/SwamiguiKnob.h000066400000000000000000000041231361104770400200220ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_KNOB_H__ #define __SWAMIGUI_KNOB_H__ #include G_BEGIN_DECLS #define SWAMIGUI_TYPE_KNOB (swamigui_knob_get_type ()) #define SWAMIGUI_KNOB(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_KNOB, SwamiguiKnob)) #define SWAMIGUI_KNOB_CLASS(obj) \ (G_TYPE_CHECK_CLASS_CAST ((obj), SWAMIGUI_TYPE_KNOB, SwamiguiKnobClass)) #define SWAMIGUI_IS_KNOB(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_KNOB)) typedef struct _SwamiguiKnob SwamiguiKnob; typedef struct _SwamiguiKnobClass SwamiguiKnobClass; struct _SwamiguiKnob { GtkDrawingArea parent; GtkAdjustment *adj; /* < private > */ gboolean rotation_active; /* mouse rotation is active? */ double start_pos, end_pos; /* start and end rotation in radians */ double rotation; /* current rotation in radians */ double rotation_rate; /* rotation rate in pixels/radians */ double rotation_rate_fine; /* rotation rate (fine) in pixels/rads */ double xclick, yclick; /* position of mouse click */ double click_rotation; /* rotation of knob upon click */ }; struct _SwamiguiKnobClass { GtkDrawingAreaClass parent_class; }; GType swamigui_knob_get_type (void); GtkWidget *swamigui_knob_new (void); GtkAdjustment *swamigui_knob_get_adjustment (SwamiguiKnob *knob); G_END_DECLS #endif swami-2.2.0/src/swamigui/SwamiguiLoopFinder.c000066400000000000000000000413661361104770400211770ustar00rootroot00000000000000/* * SwamiguiLoopFinder.c - Sample loop finder widget * * Swami * Copyright (C) 1999-2014 Element Green * * Thanks to Luis Garrido for the loop finder algorithm code and his * interest in creating this feature for Swami. * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiguiLoopFinder.h" #include "SwamiguiControl.h" #include "SwamiguiRoot.h" #include "util.h" #include "i18n.h" enum { PROP_0, PROP_FINDER }; /* columns for result list store */ enum { SIZE_COLUMN, START_COLUMN, END_COLUMN, QUALITY_COLUMN }; /* GUI worker thread monitor callback interval in milliseconds */ #define PROGRESS_UPDATE_INTERVAL 100 static void swamigui_loop_finder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_loop_finder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_loop_finder_init (SwamiguiLoopFinder *editor); static void swamigui_loop_finder_finalize (GObject *object); static void swamigui_loop_finder_destroy (GtkObject *object); static void swamigui_loop_finder_cb_selection_changed (GtkTreeSelection *selection, gpointer user_data); static void swamigui_loop_finder_cb_revert (GtkButton *button, gpointer user_data); static void swamigui_loop_finder_cb_find (GtkButton *button, gpointer user_data); static void swamigui_loop_finder_update_find_button (SwamiguiLoopFinder *finder, gboolean find); static gboolean swamigui_loop_finder_thread_monitor (gpointer user_data); static gpointer find_loop_thread (gpointer data); G_DEFINE_TYPE (SwamiguiLoopFinder, swamigui_loop_finder, GTK_TYPE_VBOX); static void swamigui_loop_finder_class_init (SwamiguiLoopFinderClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GtkObjectClass *gtkobj_class = GTK_OBJECT_CLASS (klass); obj_class->set_property = swamigui_loop_finder_set_property; obj_class->get_property = swamigui_loop_finder_get_property; obj_class->finalize = swamigui_loop_finder_finalize; gtkobj_class->destroy = swamigui_loop_finder_destroy; g_object_class_install_property (obj_class, PROP_FINDER, g_param_spec_object ("finder", _("Finder"), _("Loop finder object"), SWAMI_TYPE_LOOP_FINDER, G_PARAM_READABLE)); } static void swamigui_loop_finder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_loop_finder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (object); switch (property_id) { case PROP_FINDER: g_value_set_object (value, finder->loop_finder); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_loop_finder_init (SwamiguiLoopFinder *finder) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkWidget *treeview; GtkWidget *widg; /* create the loop finder object (!! takes over ref) */ finder->loop_finder = swami_loop_finder_new (); /* create result list store */ finder->store = gtk_list_store_new (4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT); /* create Glade GTK loop finder interface */ finder->glade_widg = swamigui_util_glade_create ("LoopFinder"); gtk_container_add (GTK_CONTAINER (finder), finder->glade_widg); /* fetch the tree view widget (result list) */ treeview = swamigui_util_glade_lookup (finder->glade_widg, "ListMatches"); /* Disable tree view search since it breaks piano key playback */ g_object_set (treeview, "enable-search", FALSE, NULL); /* add size column to tree view widget */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Loop size"), renderer, "text", SIZE_COLUMN, NULL); gtk_tree_view_column_set_sort_column_id (column, SIZE_COLUMN); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* add start column to tree view widget */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Loop start"), renderer, "text", START_COLUMN, NULL); gtk_tree_view_column_set_sort_column_id (column, START_COLUMN); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* add end column to tree view widget */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Loop end"), renderer, "text", END_COLUMN, NULL); gtk_tree_view_column_set_sort_column_id (column, END_COLUMN); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* add quality column to tree view widget */ renderer = gtk_cell_renderer_progress_new (); column = gtk_tree_view_column_new_with_attributes (_("Rating"), renderer, "value", QUALITY_COLUMN, NULL); gtk_tree_view_column_set_sort_column_id (column, QUALITY_COLUMN); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* assign the tree store */ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (finder->store)); /* connect to list view selection changed signal */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); g_signal_connect (selection, "changed", G_CALLBACK (swamigui_loop_finder_cb_selection_changed), finder); /* connect analysis window spin button to finder's property */ widg = swamigui_util_glade_lookup (finder->glade_widg, "SpinAnalysisWindow"); swamigui_control_prop_connect_widget (G_OBJECT (finder->loop_finder), "analysis-window", G_OBJECT (widg)); /* connect min loop spin button to finder's property */ widg = swamigui_util_glade_lookup (finder->glade_widg, "SpinMinLoop"); swamigui_control_prop_connect_widget (G_OBJECT (finder->loop_finder), "min-loop-size", G_OBJECT (widg)); /* connect max results spin button to finder's property */ widg = swamigui_util_glade_lookup (finder->glade_widg, "SpinMaxResults"); swamigui_control_prop_connect_widget (G_OBJECT (finder->loop_finder), "max-results", G_OBJECT (widg)); /* connect group pos diff spin button to finder's property */ widg = swamigui_util_glade_lookup (finder->glade_widg, "SpinGroupPosDiff"); swamigui_control_prop_connect_widget (G_OBJECT (finder->loop_finder), "group-pos-diff", G_OBJECT (widg)); /* connect group size diff spin button to finder's property */ widg = swamigui_util_glade_lookup (finder->glade_widg, "SpinGroupSizeDiff"); swamigui_control_prop_connect_widget (G_OBJECT (finder->loop_finder), "group-size-diff", G_OBJECT (widg)); widg = swamigui_util_glade_lookup (finder->glade_widg, "BtnRevert"); g_signal_connect (widg, "clicked", G_CALLBACK (swamigui_loop_finder_cb_revert), finder); /* find button has nothing packed in it, initialize it */ swamigui_loop_finder_update_find_button (finder, TRUE); widg = swamigui_util_glade_lookup (finder->glade_widg, "BtnFind"); g_signal_connect (widg, "clicked", G_CALLBACK (swamigui_loop_finder_cb_find), finder); } static void swamigui_loop_finder_finalize (GObject *object) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (object); g_object_unref (finder->loop_finder); if (G_OBJECT_CLASS (swamigui_loop_finder_parent_class)->finalize) G_OBJECT_CLASS (swamigui_loop_finder_parent_class)->finalize (object); } /* loop finder widget destroy */ static void swamigui_loop_finder_destroy (GtkObject *object) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (object); /* signal loop finder object to stop (does nothing if not active) */ g_object_set (finder->loop_finder, "cancel", TRUE, NULL); } static void swamigui_loop_finder_cb_selection_changed (GtkTreeSelection *selection, gpointer user_data) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (user_data); IpatchSample *sample; GtkTreeModel *model; GtkTreeIter iter; guint loop_start, loop_end; g_object_get (finder->loop_finder, "sample", &sample, NULL); /* ++ ref */ if (!sample) return; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { g_object_unref (sample); /* -- unref */ return; } gtk_tree_model_get (model, &iter, START_COLUMN, &loop_start, END_COLUMN, &loop_end, -1); g_object_set (sample, "loop-start", loop_start, "loop-end", loop_end, NULL); g_object_unref (sample); /* -- unref */ } /* button to revert to original loop settings */ static void swamigui_loop_finder_cb_revert (GtkButton *button, gpointer user_data) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (user_data); IpatchSample *sample; g_object_get (finder->loop_finder, "sample", &sample, NULL); /* ++ ref */ if (!sample) return; /* restore original loop */ g_object_set (sample, "loop-start", finder->orig_loop_start, "loop-end", finder->orig_loop_end, NULL); g_object_unref (sample); /* -- unref */ } static void swamigui_loop_finder_cb_find (GtkButton *button, gpointer user_data) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (user_data); GtkWidget *main_window; GtkWidget *dialog; IpatchSample *sample; GtkWidget *label; GError *err = NULL; gboolean active; g_object_get (finder->loop_finder, "sample", &sample, /* ++ ref */ "active", &active, NULL); if (!sample) return; /* store original loop start/end before any changes (for revert button) */ g_object_get (sample, "loop-start", &finder->orig_loop_start, "loop-end", &finder->orig_loop_end, NULL); g_object_unref (sample); /* -- unref */ if (active) /* if already active, tell it to stop */ { g_object_set (finder->loop_finder, "cancel", TRUE, NULL); return; } /* clear time ellapsed label */ label = swamigui_util_glade_lookup (finder->glade_widg, "LabelTime"); gtk_label_set_text (GTK_LABEL (label), ""); #if 0 { /* For debugging */ SwamiLoopFinder *f = finder->loop_finder; printf ("analysis=%d minloop=%d swinpos=%d swinsize=%d ewinpos=%d ewinsize=%d\n", f->analysis_window, f->min_loop_size, f->start_window_pos, f->start_window_size, f->end_window_pos, f->end_window_size); } #endif /* verify loop finder parameters and nudge them if needed */ if (!swami_loop_finder_verify_params (finder->loop_finder, TRUE, &err)) { g_object_get (swamigui_root, "main-window", &main_window, NULL); dialog = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Loop find failed: %s"), ipatch_gerror_message (err)); if (err) g_error_free (err); /* Destroy the dialog when the user responds to it */ g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); return; } #if 0 { /* For debugging */ SwamiLoopFinder *f = finder->loop_finder; printf ("analysis=%d minloop=%d swinpos=%d swinsize=%d ewinpos=%d ewinsize=%d\n", f->analysis_window, f->min_loop_size, f->start_window_pos, f->start_window_size, f->end_window_pos, f->end_window_size); } #endif /* create a thread to do the work */ if (!g_thread_create (find_loop_thread, finder, FALSE, &err)) { g_critical (_("Failed to start loop finder thread: %s"), ipatch_gerror_message (err)); if (err) g_error_free (err); return; } /* update the find button to "Stop" state */ swamigui_loop_finder_update_find_button (finder, FALSE); /* ++ ref finder for worker thread (unref'd in thread monitor timeout) */ g_object_ref (finder); g_timeout_add (PROGRESS_UPDATE_INTERVAL, swamigui_loop_finder_thread_monitor, finder); } /* modifies the find button to reflect the current state (Execute or Stop) */ static void swamigui_loop_finder_update_find_button (SwamiguiLoopFinder *finder, gboolean find) { GtkWidget *btn; GtkWidget *hbox; GtkWidget *image; GtkWidget *label; GList *children, *p; btn = swamigui_util_glade_lookup (finder->glade_widg, "BtnFind"); /* remove current widgets in button */ children = gtk_container_get_children (GTK_CONTAINER (btn)); for (p = children; p; p = p->next) gtk_container_remove (GTK_CONTAINER (btn), GTK_WIDGET (p->data)); g_list_free (children); hbox = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (btn), hbox); image = gtk_image_new_from_stock (find ? GTK_STOCK_EXECUTE : GTK_STOCK_STOP, GTK_ICON_SIZE_BUTTON); label = gtk_label_new (find ? _("Find Loops") : _("Stop")); gtk_box_pack_start (GTK_BOX (hbox), image, 0, 0, 0); gtk_box_pack_start (GTK_BOX (hbox), label, 0, 0, 0); gtk_widget_show_all (hbox); } /* monitors worker thread activity and updates GUI */ static gboolean swamigui_loop_finder_thread_monitor (gpointer user_data) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (user_data); SwamiLoopResults *results; SwamiLoopMatch *matches; GtkTreeIter iter; GtkWidget *label; GtkWidget *prog_widg; float cur_progress; float maxq, diffq, quality; gboolean active; guint match_count; guint exectime; char *s; guint i; /* read progress float */ g_object_get (finder->loop_finder, "progress", &cur_progress, "active", &active, NULL); /* progress changed from previous value? */ if (cur_progress != finder->prev_progress) { prog_widg = swamigui_util_glade_lookup (finder->glade_widg, "Progress"); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (prog_widg), cur_progress); finder->prev_progress = cur_progress; } /* stop the monitor timeout callback if thread is done */ if (!active) { /* update the time ellapsed label */ g_object_get (finder->loop_finder, "exec-time", &exectime, NULL); s = g_strdup_printf (_("%0.2f secs"), exectime / 1000.0); label = swamigui_util_glade_lookup (finder->glade_widg, "LabelTime"); gtk_label_set_text (GTK_LABEL (label), s); g_free (s); swamigui_loop_finder_update_find_button (finder, TRUE); results = swami_loop_finder_get_results (finder->loop_finder); /* ++ ref */ if (!results) return (FALSE); /* no results? */ matches = swami_loop_results_get_values (results, &match_count); gtk_list_store_clear (finder->store); /* clear all items in store */ if (match_count > 0) { maxq = matches[0].quality; diffq = matches[match_count - 1].quality - maxq; } for (i = 0; i < match_count; i++) /* loop over result matches */ { gtk_list_store_append (finder->store, &iter); quality = (float)(100.0 - (matches[i].quality - maxq) / diffq * 100.0); gtk_list_store_set (finder->store, &iter, SIZE_COLUMN, matches[i].end - matches[i].start, START_COLUMN, (guint)matches[i].start, END_COLUMN, (guint)matches[i].end, QUALITY_COLUMN, quality, -1); } g_object_unref (results); /* -- unref */ /* -- remove reference added for worker thread (in cb_find) */ g_object_unref (finder); return (FALSE); /* monitor thread not needed anymore */ } else return (TRUE); } /** * swamigui_loop_finder_new: * * Create a new sample loop finder widget. * * Returns: New widget of type SwamiguiLoopFinder */ GtkWidget * swamigui_loop_finder_new (void) { return (GTK_WIDGET (gtk_type_new (SWAMIGUI_TYPE_LOOP_FINDER))); } static gpointer find_loop_thread (gpointer data) { SwamiguiLoopFinder *finder = SWAMIGUI_LOOP_FINDER (data); GError *err = NULL; if (!swami_loop_finder_find (finder->loop_finder, &err)) { g_critical (_("Find thread failed: %s"), ipatch_gerror_message (err)); if (err) g_error_free (err); } return (NULL); } /** * swamigui_loop_finder_clear_results: * @finder: Loop finder GUI widget * * Clear results of a loop finder widget (if any). */ void swamigui_loop_finder_clear_results (SwamiguiLoopFinder *finder) { g_return_if_fail (SWAMIGUI_IS_LOOP_FINDER (finder)); gtk_list_store_clear (finder->store); } swami-2.2.0/src/swamigui/SwamiguiLoopFinder.h000066400000000000000000000042111361104770400211700ustar00rootroot00000000000000/* * SwamiguiLoopFinder.h - Sample loop finder widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_LOOP_FINDER_H__ #define __SWAMIGUI_LOOP_FINDER_H__ #include #include typedef struct _SwamiguiLoopFinder SwamiguiLoopFinder; typedef struct _SwamiguiLoopFinderClass SwamiguiLoopFinderClass; #define SWAMIGUI_TYPE_LOOP_FINDER (swamigui_loop_finder_get_type ()) #define SWAMIGUI_LOOP_FINDER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_LOOP_FINDER, \ SwamiguiLoopFinder)) #define SWAMIGUI_IS_LOOP_FINDER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_LOOP_FINDER)) /* Loop finder object */ struct _SwamiguiLoopFinder { GtkVBox parent_instance; /* derived from GtkVBox */ GtkListStore *store; /* list store for results */ GtkWidget *glade_widg; /* the embedded glade widget */ guint orig_loop_start; /* original loop start of current sample */ guint orig_loop_end; /* original loop end of current sample */ float prev_progress; /* progress value caching */ /*< public >*/ SwamiLoopFinder *loop_finder; /* loop finder object instance */ }; /* Loop finder widget class */ struct _SwamiguiLoopFinderClass { GtkVBoxClass parent_class; }; GType swamigui_loop_finder_get_type (void); GtkWidget *swamigui_loop_finder_new (void); void swamigui_loop_finder_clear_results (SwamiguiLoopFinder *finder); #endif swami-2.2.0/src/swamigui/SwamiguiMenu.c000066400000000000000000000412101361104770400200260ustar00rootroot00000000000000/* * SwamiguiMenu.c - Swami GUI Menu object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiMenu.h" #include "SwamiguiRoot.h" #include "SwamiguiPref.h" #include "SwamiguiPythonView.h" #include "help.h" #include "patch_funcs.h" #include "i18n.h" #include "icons.h" #include "splash.h" #include "util.h" static void swamigui_menu_class_init (SwamiguiMenuClass *klass); static void swamigui_menu_init (SwamiguiMenu *menubar); static gboolean swamigui_menu_recent_filter_custom_func (const GtkRecentFilterInfo *filter_info, gpointer user_data); static void swamigui_menu_recent_chooser_item_activated (GtkRecentChooser *chooser, gpointer user_data); static void swamigui_menu_realize (GtkWidget *widget); static void swamigui_menu_update_new_type_item (void); static GtkWidget *create_patch_type_menu (SwamiguiMenu *menubar); static int sort_by_type_name (const void *a, const void *b); /* menu callbacks */ static void swamigui_menu_cb_new_patch (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_load_files (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_save_all (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_quit (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_preferences (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_swamitips (GtkWidget *mitem, gpointer data); static void swamigui_menu_cb_splash_image (GtkWidget *mitem, gpointer data); #ifdef PYTHON_SUPPORT static void swamigui_menu_cb_python (GtkWidget *mitem, gpointer data); #endif static void swamigui_menu_cb_restart_fluid (GtkWidget *mitem, gpointer data); static GtkWidgetClass *parent_class = NULL; /* the last patch type selected from the NewType menu item */ static GType last_new_type = 0; static GtkWidget *last_new_mitem = NULL; static GtkActionEntry entries[] = { { "FileMenu", NULL, "_File" }, /* name, stock id, label */ { "EditMenu", NULL, "_Edit" }, /* name, stock id, label */ { "PluginsMenu", NULL, "_Plugins" }, /* name, stock id, label */ { "ToolsMenu", NULL, "_Tools" }, /* name, stock id, label */ { "HelpMenu", NULL, "_Help" }, /* name, stock id, label */ /* File Menu */ /* New is not actually on the menu, just used for key accelerator */ { "New", GTK_STOCK_NEW, "_New", /* name, stock id, label */ "N", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (swamigui_menu_cb_new_patch) }, { "NewType", GTK_STOCK_NEW, "N_ew...", "", N_("Create a new patch file of type..") }, { "Open", GTK_STOCK_OPEN, /* name, stock id */ "_Open", "O", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (swamigui_menu_cb_load_files) }, { "OpenRecent", GTK_STOCK_OPEN, /* name, stock id */ "Open _Recent", "", /* label, accelerator */ NULL, /* tooltip */ NULL }, /* callback */ { "SaveAll", GTK_STOCK_SAVE, /* name, stock id */ "_Save All", "", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (swamigui_menu_cb_save_all) }, { "Quit", GTK_STOCK_QUIT, /* name, stock id */ "_Quit", "Q", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (swamigui_menu_cb_quit) }, /* Edit Menu */ { "Preferences", GTK_STOCK_PREFERENCES, /* name, stock id */ "_Preferences", "", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (swamigui_menu_cb_preferences) }, /* Plugins Menu */ { "RestartFluid", GTK_STOCK_REFRESH, N_("_Restart FluidSynth"), "", N_("Restart FluidSynth plugin"), G_CALLBACK (swamigui_menu_cb_restart_fluid) }, /* Tools Menu */ #ifdef PYTHON_SUPPORT { "Python", SWAMIGUI_STOCK_PYTHON, /* name, stock id */ "_Python", "", /* label, accelerator */ N_("Python script editor and console"), /* tooltip */ G_CALLBACK (swamigui_menu_cb_python) }, #endif /* Help Menu */ { "SwamiTips", GTK_STOCK_HELP, /* name, stock id */ "Swami _Tips", "", /* label, accelerator */ N_("Get helpful tips on using Swami"), /* tooltip */ G_CALLBACK (swamigui_menu_cb_swamitips) }, { "SplashImage", GTK_STOCK_INFO, /* name, stock id */ "_Splash Image", "", /* label, accelerator */ N_("Show splash image"), /* tooltip */ G_CALLBACK (swamigui_menu_cb_splash_image) }, { "About", GTK_STOCK_ABOUT, /* name, stock id */ "_About", "", /* label, accelerator */ N_("About Swami"), /* tooltip */ G_CALLBACK (swamigui_help_about) }, }; static guint n_entries = G_N_ELEMENTS (entries); static const gchar *ui_info = "" " " "

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " /* FIXME - Python disabled until crashing is fixed and binding is updated */ #if 0 " " #ifdef PYTHON_SUPPORT " " #endif " " #endif " " " " " " " " " " " " ""; GType swamigui_menu_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiMenuClass), NULL, NULL, (GClassInitFunc) swamigui_menu_class_init, NULL, NULL, sizeof (SwamiguiMenu), 0, (GInstanceInitFunc) swamigui_menu_init, }; obj_type = g_type_register_static (GTK_TYPE_VBOX, "SwamiguiMenu", &obj_info, 0); } return (obj_type); } static void swamigui_menu_class_init (SwamiguiMenuClass *klass) { GtkWidgetClass *widg_class = GTK_WIDGET_CLASS (klass); parent_class = g_type_class_peek_parent (klass); widg_class->realize = swamigui_menu_realize; } static void swamigui_menu_init (SwamiguiMenu *guimenu) { GtkActionGroup *actions; GtkWidget *new_type_menu; GtkWidget *mitem; GError *error = NULL; GtkWidget *recent_menu; GtkRecentManager *manager; GtkRecentFilter *filter; actions = gtk_action_group_new ("Actions"); gtk_action_group_add_actions (actions, entries, n_entries, guimenu); guimenu->ui = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (guimenu->ui, actions, 0); if (!gtk_ui_manager_add_ui_from_string (guimenu->ui, ui_info, -1, &error)) { g_critical ("Building SwamiGuiMenu failed: %s", error->message); g_error_free (error); return; } gtk_box_pack_start (GTK_BOX (guimenu), gtk_ui_manager_get_widget (guimenu->ui, "/MenuBar"), FALSE, FALSE, 0); /* if last_new_type not set assign it from SwamiguiRoot "default-patch-type" property */ if (!last_new_type) { g_object_get (swamigui_root, "default-patch-type", &last_new_type, NULL); /* also not set? Just set it to SoundFont type */ if (last_new_type == G_TYPE_NONE) last_new_type = IPATCH_TYPE_SF2; } /* set correct label of "New " menu item */ last_new_mitem = gtk_ui_manager_get_widget (guimenu->ui, "/MenuBar/FileMenu/New"); swamigui_menu_update_new_type_item (); /* create patch type menu and add to File->"New .." menu item */ new_type_menu = create_patch_type_menu (guimenu); mitem = gtk_ui_manager_get_widget (guimenu->ui, "/MenuBar/FileMenu/NewType"); gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), new_type_menu); /* Recent chooser menu */ manager = gtk_recent_manager_get_default (); recent_menu = gtk_recent_chooser_menu_new_for_manager (manager); /* set the limit to unlimited (FIXME - issues?) */ gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (recent_menu), -1); /* filter recent items to only include those stored by Swami and in the instrument files group */ filter = gtk_recent_filter_new (); gtk_recent_filter_add_custom (filter, GTK_RECENT_FILTER_APPLICATION | GTK_RECENT_FILTER_GROUP, swamigui_menu_recent_filter_custom_func, NULL, NULL); gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter); /* Set the sort type to most recent first */ gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU); mitem = gtk_ui_manager_get_widget (guimenu->ui, "/MenuBar/FileMenu/OpenRecent"); gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), recent_menu); g_signal_connect (recent_menu, "item-activated", G_CALLBACK (swamigui_menu_recent_chooser_item_activated), NULL); } /* Custom filter function to match application name and instrument files group (exclude samples, etc) */ static gboolean swamigui_menu_recent_filter_custom_func (const GtkRecentFilterInfo *filter_info, gpointer user_data) { const char **sp; const char *app_name; if (!filter_info->applications || !filter_info->groups) return (FALSE); app_name = g_get_application_name (); for (sp = filter_info->applications; *sp; sp++) if (strcmp (*sp, app_name) == 0) break; if (!*sp) return (FALSE); for (sp = filter_info->groups; *sp; sp++) if (strcmp (*sp, SWAMIGUI_ROOT_INSTRUMENT_FILES_GROUP) == 0) break; return (*sp != NULL); } /* callback for when the user selects a recent file in the recent files menu */ static void swamigui_menu_recent_chooser_item_activated (GtkRecentChooser *chooser, gpointer user_data) { char *file_uri, *fname; GError *err = NULL; GtkWidget *msgdialog; file_uri = gtk_recent_chooser_get_current_uri (chooser); if (!file_uri) return; fname = g_filename_from_uri (file_uri, NULL, NULL); g_free (file_uri); if (!fname) { g_critical (_("Failed to parse recent file URI '%s'"), file_uri); return; } if (!swami_root_patch_load (SWAMI_ROOT (swamigui_root), fname, NULL, &err)) { msgdialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to load '%s': %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); gtk_dialog_run (GTK_DIALOG (msgdialog)); gtk_widget_destroy (msgdialog); } g_free (fname); } static void swamigui_menu_realize (GtkWidget *widget) { SwamiguiMenu *guimenu = SWAMIGUI_MENU (widget); GtkWidget *toplevel; parent_class->realize (widget); toplevel = gtk_widget_get_toplevel (widget); if (toplevel) gtk_window_add_accel_group (GTK_WINDOW (toplevel), gtk_ui_manager_get_accel_group (guimenu->ui)); } static void swamigui_menu_update_new_type_item (void) { char *name, *s; char *free_icon, *icon_name; GtkWidget *icon; gint category; ipatch_type_get (last_new_type, "name", &name, NULL); s = g_strdup_printf (_("_New %s"), name); g_free (name); gtk_label_set_text_with_mnemonic (GTK_LABEL (gtk_bin_get_child (GTK_BIN (last_new_mitem))), s); g_free (s); /* get icon stock name */ ipatch_type_get (last_new_type, "icon", &free_icon, "category", &category, NULL); if (!free_icon) icon_name = swamigui_icon_get_category_icon (category); else icon_name = free_icon; icon = gtk_image_new_from_stock (icon_name, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (last_new_mitem), icon); if (free_icon) g_free (free_icon); } static GtkWidget * create_patch_type_menu (SwamiguiMenu *guimenu) { GtkWidget *menu; GtkWidget *item; GtkWidget *icon; GType *types, *ptype; char *name; char *free_icon = NULL, *icon_name; guint n_types; gint category; menu = gtk_menu_new (); types = swami_util_get_child_types (IPATCH_TYPE_BASE, &n_types); qsort (types, n_types, sizeof (GType), sort_by_type_name); for (ptype = types; *ptype; ptype++) { ipatch_type_get (*ptype, "name", &name, "icon", &free_icon, "category", &category, NULL); if (!name) { g_free (free_icon); continue; } item = gtk_image_menu_item_new_with_label (name); g_free (name); if (!free_icon) icon_name = swamigui_icon_get_category_icon (category); else icon_name = free_icon; icon = gtk_image_new_from_stock (icon_name, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon); g_free (free_icon); g_object_set_data (G_OBJECT (item), "patch-type", GSIZE_TO_POINTER (*ptype)); gtk_widget_show_all (item); gtk_container_add (GTK_CONTAINER (menu), item); g_signal_connect (item, "activate", G_CALLBACK (swamigui_menu_cb_new_patch), guimenu); } g_free (types); return (menu); } static int sort_by_type_name (const void *a, const void *b) { GType *atype = (GType *)a, *btype = (GType *)b; char *aname, *bname; ipatch_type_get (*atype, "name", &aname, NULL); ipatch_type_get (*btype, "name", &bname, NULL); if (!aname && !bname) return (0); else if (!aname) return (1); else if (!bname) return (-1); else return (strcmp (aname, bname)); } /** * swamigui_menu_new: * * Create a Swami main menu object. * * Returns: New Swami menu object. */ GtkWidget * swamigui_menu_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_MENU, NULL))); } /* main menu callback to create a new patch objects */ static void swamigui_menu_cb_new_patch (GtkWidget *mitem, gpointer data) { GType patch_type; patch_type = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (mitem), "patch-type")); if (patch_type) { last_new_type = patch_type; swamigui_menu_update_new_type_item (); } else patch_type = last_new_type; swamigui_new_item (NULL, patch_type); } static void swamigui_menu_cb_load_files (GtkWidget *mitem, gpointer data) { SwamiguiRoot *root = swamigui_get_root (data); IpatchList *selection; if (root) { g_object_get (root, "selection", &selection, NULL); // ++ ref if (selection->items && !selection->items->next) swamigui_load_files (G_OBJECT (selection->items->data), FALSE); else swamigui_load_files (G_OBJECT (root), FALSE); g_object_unref (selection); // -- unref } } /* main menu callback to save files */ static void swamigui_menu_cb_save_all (GtkWidget *mitem, gpointer data) { IpatchList *patches; /* save them all */ patches = ipatch_container_get_children (IPATCH_CONTAINER (swami_root->patch_root), IPATCH_TYPE_BASE); if (patches) { if (patches->items) swamigui_save_files (patches, FALSE); g_object_unref (patches); } } static void swamigui_menu_cb_quit (GtkWidget *mitem, gpointer data) { SwamiguiRoot *root = swamigui_get_root (data); if (root) swamigui_root_quit (root); } static void swamigui_menu_cb_preferences (GtkWidget *mitem, gpointer data) { GtkWidget *pref; pref = swamigui_util_lookup_unique_dialog ("preferences", 0); if (!pref) { pref = swamigui_pref_new (); gtk_widget_show (pref); swamigui_util_register_unique_dialog (pref, "preferences", 0); } } static void swamigui_menu_cb_swamitips (GtkWidget *mitem, gpointer data) { SwamiguiRoot *root = swamigui_get_root (data); if (root) swamigui_help_swamitips_create (root); } static void swamigui_menu_cb_splash_image (GtkWidget *mitem, gpointer data) { swamigui_splash_display (0); } #ifdef PYTHON_SUPPORT static void swamigui_menu_cb_python (GtkWidget *mitem, gpointer data) { GtkWidget *window; GtkWidget *pythonview; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); pythonview = swamigui_python_view_new (); gtk_container_add (GTK_CONTAINER (window), pythonview); gtk_widget_show_all (window); } #endif static void swamigui_menu_cb_restart_fluid (GtkWidget *mitem, gpointer data) { /* FIXME - Should be handled by FluidSynth plugin */ swami_wavetbl_close (swamigui_root->wavetbl); swami_wavetbl_open (swamigui_root->wavetbl, NULL); } swami-2.2.0/src/swamigui/SwamiguiMenu.h000066400000000000000000000033501361104770400200360ustar00rootroot00000000000000/* * SwamiguiMenu.h - Swami main menu object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_MENU_H__ #define __SWAMIGUI_MENU_H__ typedef struct _SwamiguiMenu SwamiguiMenu; typedef struct _SwamiguiMenuClass SwamiguiMenuClass; #include #include "SwamiguiRoot.h" #define SWAMIGUI_TYPE_MENU (swamigui_menu_get_type ()) #define SWAMIGUI_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_MENU, SwamiguiMenu)) #define SWAMIGUI_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_MENU, SwamiguiMenuClass)) #define SWAMIGUI_IS_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_MENU)) #define SWAMIGUI_IS_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_MENU)) /* Swami Menu object */ struct _SwamiguiMenu { GtkVBox parent_instance; GtkUIManager *ui; }; struct _SwamiguiMenuClass { GtkVBoxClass parent_class; }; GType swamigui_menu_get_type (void); GtkWidget *swamigui_menu_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiModEdit.c000066400000000000000000001351531361104770400204610ustar00rootroot00000000000000/* * SwamiguiModEdit.c - User interface modulator editor widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiguiModEdit.h" #include "SwamiguiControl.h" #include "SwamiguiPanel.h" #include "widgets/icon-combo.h" #include "icons.h" #include "util.h" #include "i18n.h" enum { PROP_0, PROP_ITEM_SELECTION, PROP_MODULATORS }; /* tree view list stuff */ enum { DEST_LABEL, SRC_PIXBUF, SRC_LABEL, AMT_PIXBUF, AMT_LABEL, AMT_VALUE, MOD_PTR, /* modulator pointer */ NUM_FIELDS }; /* Source control GtkListStore fields */ enum { SRC_STORE_LABEL, /* The text displayed in the combo box */ SRC_STORE_CTRLNUM, /* The modulator source control value */ SRC_STORE_NUM_FIELDS }; /* destination combo box tree store fields */ enum { DEST_COLUMN_TEXT, /* Text to display for this group/generator */ DEST_COLUMN_ID, /* Index of group if group ID, generator ID otherwise */ DEST_COLUMN_COUNT }; /* flag set in DEST_COLUMN_ID for group items (unset for generators) */ #define DEST_COLUMN_ID_IS_GROUP 0x100 /* Modulator General Controller palette descriptions */ struct { int ctrlnum; char *descr; } modctrl_descr[] = { { IPATCH_SF2_MOD_CONTROL_NONE, N_("No Controller") }, { IPATCH_SF2_MOD_CONTROL_NOTE_ON_VELOCITY, N_("Note-On Velocity") }, { IPATCH_SF2_MOD_CONTROL_NOTE_NUMBER, N_("Note-On Key Number") }, { IPATCH_SF2_MOD_CONTROL_POLY_PRESSURE, N_("Poly Pressure") }, { IPATCH_SF2_MOD_CONTROL_CHAN_PRESSURE, N_("Channel Pressure") }, { IPATCH_SF2_MOD_CONTROL_PITCH_WHEEL, N_("Pitch Wheel") }, { IPATCH_SF2_MOD_CONTROL_BEND_RANGE, N_("Bend Range") } }; #define MODCTRL_DESCR_COUNT \ (sizeof (modctrl_descr) / sizeof (modctrl_descr[0])) /* MIDI Continuous Controller descriptions */ struct { int ctrlnum; char *descr; } midicc_descr[] = { { 1, N_("Modulation") }, { 2, N_("Breath Controller") }, { 3, N_("Undefined") }, { 4, N_("Foot Controller") }, { 5, N_("Portamento Time") }, { 7, N_("Main Volume") }, { 8, N_("Balance") }, { 9, N_("Undefined") }, { 10, N_("Panpot") }, { 11, N_("Expression Pedal") }, { 12, N_("Effect Control 1") }, { 13, N_("Effect Control 2") }, { 14, N_("Undefined") }, { 15, N_("Undefined") }, { 16, N_("General Purpose 1") }, { 17, N_("General Purpose 2") }, { 18, N_("General Purpose 3") }, { 19, N_("General Purpose 4") }, /* 20-31 Undefined, 33-63 LSB for controllers 1-31 */ { 64, N_("Hold 1 (Damper)") }, { 65, N_("Portamento") }, { 66, N_("Sostenuto") }, { 67, N_("Soft Pedal") }, { 68, N_("Undefined") }, { 69, N_("Hold 2 (Freeze)") }, /* 70-79 Undefined */ { 80, N_("General Purpose 5") }, { 81, N_("General Purpose 6") }, { 82, N_("General Purpose 7") }, { 83, N_("General Purpose 8") }, /* 84-90 Undefined */ { 91, N_("Effect 1 (Reverb)") }, { 92, N_("Effect 2 (Tremolo)") }, { 93, N_("Effect 3 (Chorus)") }, { 94, N_("Effect 4 (Celeste)") }, { 95, N_("Effect 5 (Phaser)") }, { 96, N_("Data Increment") }, { 97, N_("Data Decrement") } /* 102-119 Undefined */ }; #define MIDICC_DESCR_COUNT (sizeof (midicc_descr) / sizeof (midicc_descr[0])) char *modgroup_names[] = { N_("Sample"), N_("Pitch/Effects"), N_("Volume Envelope"), N_("Modulation Envelope"), N_("Modulation LFO"), N_("Vibrato LFO") }; #define MODGROUP_COUNT (sizeof (modgroup_names) / sizeof (modgroup_names[0])) #define MODGROUP_SEPARATOR (-1) int modgroup_gens[] = { /* Sample group */ IPATCH_SF2_GEN_SAMPLE_START, IPATCH_SF2_GEN_SAMPLE_COARSE_START, IPATCH_SF2_GEN_SAMPLE_END, IPATCH_SF2_GEN_SAMPLE_COARSE_END, IPATCH_SF2_GEN_SAMPLE_LOOP_START, IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, IPATCH_SF2_GEN_SAMPLE_LOOP_END, IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, MODGROUP_SEPARATOR, /* Pitch/Effects group */ IPATCH_SF2_GEN_COARSE_TUNE, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, IPATCH_SF2_GEN_FILTER_Q, IPATCH_SF2_GEN_FILTER_CUTOFF, IPATCH_SF2_GEN_REVERB, IPATCH_SF2_GEN_CHORUS, IPATCH_SF2_GEN_PAN, MODGROUP_SEPARATOR, /* Volumen Envelope group */ IPATCH_SF2_GEN_VOL_ENV_DELAY, IPATCH_SF2_GEN_VOL_ENV_ATTACK, IPATCH_SF2_GEN_VOL_ENV_HOLD, IPATCH_SF2_GEN_VOL_ENV_DECAY, IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, IPATCH_SF2_GEN_VOL_ENV_RELEASE, IPATCH_SF2_GEN_ATTENUATION, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, MODGROUP_SEPARATOR, /* Modulation Envelope group */ IPATCH_SF2_GEN_MOD_ENV_DELAY, IPATCH_SF2_GEN_MOD_ENV_ATTACK, IPATCH_SF2_GEN_MOD_ENV_HOLD, IPATCH_SF2_GEN_MOD_ENV_DECAY, IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, IPATCH_SF2_GEN_MOD_ENV_RELEASE, IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, MODGROUP_SEPARATOR, /* Modulation LFO group */ IPATCH_SF2_GEN_MOD_LFO_DELAY, IPATCH_SF2_GEN_MOD_LFO_FREQ, IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, MODGROUP_SEPARATOR, /* Vibrato LFO group */ IPATCH_SF2_GEN_VIB_LFO_DELAY, IPATCH_SF2_GEN_VIB_LFO_FREQ, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, MODGROUP_SEPARATOR }; #define MODGROUP_GENS_SIZE (sizeof (modgroup_gens) / sizeof (modgroup_gens[0])) /* elements for source modulator transform icon combo widget */ static IconComboElement modtransform_elements[] = { { N_("Linear Positive Unipolar"), SWAMIGUI_STOCK_LINEAR_POS_UNI, IPATCH_SF2_MOD_TYPE_LINEAR | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Linear Negative Unipolar"), SWAMIGUI_STOCK_LINEAR_NEG_UNI, IPATCH_SF2_MOD_TYPE_LINEAR | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Linear Positive Bipolar"), SWAMIGUI_STOCK_LINEAR_POS_BI, IPATCH_SF2_MOD_TYPE_LINEAR | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Linear Negative Bipolar"), SWAMIGUI_STOCK_LINEAR_NEG_BI, IPATCH_SF2_MOD_TYPE_LINEAR | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Concave Positive Unipolar"), SWAMIGUI_STOCK_CONCAVE_POS_UNI, IPATCH_SF2_MOD_TYPE_CONCAVE | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Concave Negative Unipolar"), SWAMIGUI_STOCK_CONCAVE_NEG_UNI, IPATCH_SF2_MOD_TYPE_CONCAVE | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Concave Positive Bipolar"), SWAMIGUI_STOCK_CONCAVE_POS_BI, IPATCH_SF2_MOD_TYPE_CONCAVE | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Concave Negative Bipolar"), SWAMIGUI_STOCK_CONCAVE_NEG_BI, IPATCH_SF2_MOD_TYPE_CONCAVE | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Convex Positive Unipolar"), SWAMIGUI_STOCK_CONVEX_POS_UNI, IPATCH_SF2_MOD_TYPE_CONVEX | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Convex Negative Unipolar"), SWAMIGUI_STOCK_CONVEX_NEG_UNI, IPATCH_SF2_MOD_TYPE_CONVEX | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Convex Positive Bipolar"), SWAMIGUI_STOCK_CONVEX_POS_BI, IPATCH_SF2_MOD_TYPE_CONVEX | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Convex Negative Bipolar"), SWAMIGUI_STOCK_CONVEX_NEG_BI, IPATCH_SF2_MOD_TYPE_CONVEX | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Switch Positive Unipolar"), SWAMIGUI_STOCK_SWITCH_POS_UNI, IPATCH_SF2_MOD_TYPE_SWITCH | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Switch Negative Unipolar"), SWAMIGUI_STOCK_SWITCH_NEG_UNI, IPATCH_SF2_MOD_TYPE_SWITCH | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_UNIPOLAR }, { N_("Switch Positive Bipolar"), SWAMIGUI_STOCK_SWITCH_POS_BI, IPATCH_SF2_MOD_TYPE_SWITCH | IPATCH_SF2_MOD_DIRECTION_POSITIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR }, { N_("Switch Negative Bipolar"), SWAMIGUI_STOCK_SWITCH_NEG_BI, IPATCH_SF2_MOD_TYPE_SWITCH | IPATCH_SF2_MOD_DIRECTION_NEGATIVE | IPATCH_SF2_MOD_POLARITY_BIPOLAR } }; static void swamigui_mod_edit_class_init (SwamiguiModEditClass *klass); static void swamigui_mod_edit_panel_iface_init (SwamiguiPanelIface *panel_iface); static gboolean swamigui_mod_edit_panel_iface_check_selection (IpatchList *selection, GType *selection_types); static void swamigui_mod_edit_init (SwamiguiModEdit *modedit); static void swamigui_mod_edit_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_mod_edit_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_mod_edit_finalize (GObject *object); static GtkWidget *swamigui_mod_edit_create_list_view(SwamiguiModEdit *modedit); static GtkTreeStore *swamigui_mod_edit_init_dest_combo_box (GtkWidget *combo_dest); static void swamigui_mod_edit_cb_destination_changed (GtkComboBox *combo, gpointer user_data); static gboolean swamigui_mod_edit_real_set_selection (SwamiguiModEdit *modedit, IpatchList *selection); static gboolean swamigui_mod_edit_real_set_mods (SwamiguiModEdit *modedit, IpatchSF2ModList *mods); static void swamigui_mod_edit_cb_mod_select_changed (GtkTreeSelection *selection, SwamiguiModEdit *modedit); static void swamigui_mod_edit_tree_selection_count (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static void swamigui_mod_edit_cb_new_clicked (GtkButton *btn, SwamiguiModEdit *modedit); static void swamigui_mod_edit_cb_delete_clicked (GtkButton *btn, SwamiguiModEdit *modedit); static void swamigui_mod_edit_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static void swamigui_mod_edit_cb_pixcombo_changed (IconCombo *pixcombo, int id, SwamiguiModEdit *modedit); static void swamigui_mod_edit_cb_combo_src_ctrl_changed (GtkComboBox *combo, gpointer user_data); static void swamigui_mod_edit_cb_amtsrc_changed (GtkAdjustment *adj, SwamiguiModEdit *modedit); static void swamigui_mod_edit_update (SwamiguiModEdit *modedit); static void swamigui_mod_edit_update_store_row (SwamiguiModEdit *modedit, GtkTreeIter *iter); static void swamigui_mod_edit_set_active_mod (SwamiguiModEdit *modedit, GtkTreeIter *iter, gboolean force); static gboolean swamigui_mod_edit_select_src_ctrl (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static char *swamigui_mod_edit_get_control_name (guint16 modsrc); static char *swamigui_mod_edit_find_transform_icon (guint16 modsrc); static int swamigui_mod_edit_find_gen_group (int genid, int *index); static GObjectClass *parent_class = NULL; GType swamigui_mod_edit_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiModEditClass), NULL, NULL, (GClassInitFunc) swamigui_mod_edit_class_init, NULL, NULL, sizeof (SwamiguiModEdit), 0, (GInstanceInitFunc) swamigui_mod_edit_init, }; static const GInterfaceInfo panel_info = { (GInterfaceInitFunc)swamigui_mod_edit_panel_iface_init, NULL, NULL }; obj_type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "SwamiguiModEdit", &obj_info, 0); g_type_add_interface_static (obj_type, SWAMIGUI_TYPE_PANEL, &panel_info); } return (obj_type); } static void swamigui_mod_edit_class_init (SwamiguiModEditClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_mod_edit_set_property; obj_class->get_property = swamigui_mod_edit_get_property; obj_class->finalize = swamigui_mod_edit_finalize; g_object_class_override_property (obj_class, PROP_ITEM_SELECTION, "item-selection"); g_object_class_install_property (obj_class, PROP_MODULATORS, g_param_spec_boxed ("modulators", _("Modulators"), _("Modulators"), IPATCH_TYPE_SF2_MOD_LIST, G_PARAM_READWRITE)); } static void swamigui_mod_edit_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("Modulators"); panel_iface->blurb = _("Edit real time effect controls"); panel_iface->stockid = SWAMIGUI_STOCK_MODULATOR_EDITOR; panel_iface->check_selection = swamigui_mod_edit_panel_iface_check_selection; } static gboolean swamigui_mod_edit_panel_iface_check_selection (IpatchList *selection, GType *selection_types) { /* one item only and with mod item interface */ return (!selection->items->next && g_object_class_find_property (G_OBJECT_GET_CLASS (selection->items->data), "modulators")); } static void swamigui_mod_edit_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiModEdit *modedit = SWAMIGUI_MOD_EDIT (object); switch (property_id) { case PROP_ITEM_SELECTION: swamigui_mod_edit_real_set_selection (modedit, g_value_get_object (value)); break; case PROP_MODULATORS: swamigui_mod_edit_real_set_mods (modedit, (IpatchSF2ModList *)g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_mod_edit_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiModEdit *modedit = SWAMIGUI_MOD_EDIT (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, modedit->selection); break; case PROP_MODULATORS: g_value_set_boxed (value, modedit->mods); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_mod_edit_finalize (GObject *object) { SwamiguiModEdit *modedit = SWAMIGUI_MOD_EDIT (object); g_object_unref (modedit->modctrl); if (modedit->selection) g_object_unref (modedit->selection); ipatch_sf2_mod_list_free (modedit->mods, TRUE); if (parent_class->finalize) parent_class->finalize (object); } static void swamigui_mod_edit_init (SwamiguiModEdit *modedit) { GtkWidget *glade_widg; GtkWidget *icon; GtkWidget *pixcombo; GtkWidget *widg; GtkCellRenderer *renderer; char *descr; int i; int ctrlnum; GtkTreeIter iter; gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (modedit), NULL); gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (modedit), NULL); gtk_container_border_width (GTK_CONTAINER (modedit), 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (modedit), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); modedit->mod_selected = FALSE; modedit->block_callbacks = FALSE; /* create control for the "modulators" property and add to GUI queue */ modedit->modctrl = SWAMI_CONTROL (swami_get_control_prop_by_name (G_OBJECT (modedit), "modulators")); swamigui_control_set_queue (modedit->modctrl); glade_widg = swamigui_util_glade_create ("ModEdit"); modedit->glade_widg = glade_widg; /* set up modulator tree view list widget */ swamigui_mod_edit_create_list_view (modedit); /* configure callbacks on action buttons */ widg = swamigui_util_glade_lookup (glade_widg, "BTNNew"); g_signal_connect (widg, "clicked", G_CALLBACK (swamigui_mod_edit_cb_new_clicked), modedit); widg = swamigui_util_glade_lookup (glade_widg, "BTNDel"); g_signal_connect (widg, "clicked", G_CALLBACK (swamigui_mod_edit_cb_delete_clicked), modedit); /* nice modulator junction icon */ icon = gtk_image_new_from_stock (SWAMIGUI_STOCK_MODULATOR_JUNCT, SWAMIGUI_ICON_SIZE_CUSTOM_LARGE1); gtk_widget_show (icon); widg = swamigui_util_glade_lookup (glade_widg, "HBXIcon"); gtk_box_pack_start (GTK_BOX (widg), icon, FALSE, 0, 0); gtk_box_reorder_child (GTK_BOX (widg), icon, 0); /* create source modulator icon combo */ pixcombo = icon_combo_new (modtransform_elements, 4, 4); gtk_widget_show (pixcombo); g_object_set_data (G_OBJECT (glade_widg), "PIXSrc", pixcombo); widg = swamigui_util_glade_lookup (glade_widg, "HBXSrc"); gtk_box_pack_start (GTK_BOX (widg), pixcombo, FALSE, 0, 0); gtk_box_reorder_child (GTK_BOX (widg), pixcombo, 0); g_signal_connect (pixcombo, "changed", G_CALLBACK (swamigui_mod_edit_cb_pixcombo_changed), modedit); /* create amount source modulator icon combo */ pixcombo = icon_combo_new (modtransform_elements, 4, 4); gtk_widget_show (pixcombo); g_object_set_data (G_OBJECT (glade_widg), "PIXAmtSrc", pixcombo); widg = swamigui_util_glade_lookup (glade_widg, "HBXAmtSrc"); gtk_box_pack_start (GTK_BOX (widg), pixcombo, FALSE, 0, 0); gtk_box_reorder_child (GTK_BOX (widg), pixcombo, 0); g_signal_connect (pixcombo, "changed", G_CALLBACK (swamigui_mod_edit_cb_pixcombo_changed), modedit); modedit->src_store = gtk_list_store_new (SRC_STORE_NUM_FIELDS, G_TYPE_STRING, G_TYPE_INT); /* add controls to the source control list store */ for (i = 0; i < MODCTRL_DESCR_COUNT + 120; i++) { ctrlnum = i < MODCTRL_DESCR_COUNT ? modctrl_descr[i].ctrlnum : (i - MODCTRL_DESCR_COUNT) | IPATCH_SF2_MOD_CC_MIDI; descr = swamigui_mod_edit_get_control_name (ctrlnum); if (!descr) continue; gtk_list_store_append (modedit->src_store, &iter); gtk_list_store_set (modedit->src_store, &iter, SRC_STORE_LABEL, descr, SRC_STORE_CTRLNUM, ctrlnum, -1); g_free (descr); } /* add modulator source controller description strings to combos */ widg = swamigui_util_glade_lookup (glade_widg, "COMSrcCtrl"); gtk_combo_box_set_model (GTK_COMBO_BOX (widg), GTK_TREE_MODEL (modedit->src_store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widg), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widg), renderer, "text", SRC_STORE_LABEL, NULL); g_signal_connect (widg, "changed", G_CALLBACK (swamigui_mod_edit_cb_combo_src_ctrl_changed), modedit); widg = swamigui_util_glade_lookup (glade_widg, "COMAmtCtrl"); gtk_combo_box_set_model (GTK_COMBO_BOX (widg), GTK_TREE_MODEL (modedit->src_store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widg), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widg), renderer, "text", SRC_STORE_LABEL, NULL); g_signal_connect (widg, "changed", G_CALLBACK (swamigui_mod_edit_cb_combo_src_ctrl_changed), modedit); /* add value changed signal to amount spin button */ widg = swamigui_util_glade_lookup (glade_widg, "SPBAmount"); g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widg)), "value-changed", G_CALLBACK (swamigui_mod_edit_cb_amtsrc_changed), modedit); /* add generator groups to option menu */ widg = swamigui_util_glade_lookup (glade_widg, "ComboDestination"); modedit->dest_store = swamigui_mod_edit_init_dest_combo_box (widg); gtk_combo_box_set_model (GTK_COMBO_BOX (widg), GTK_TREE_MODEL (modedit->dest_store)); g_signal_connect (widg, "changed", G_CALLBACK (swamigui_mod_edit_cb_destination_changed), modedit); swamigui_mod_edit_set_active_mod (modedit, NULL, TRUE); /* disable editor */ gtk_widget_show (glade_widg); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (modedit), glade_widg); } static GtkWidget * swamigui_mod_edit_create_list_view (SwamiguiModEdit *modedit) { GtkWidget *tree; GtkTreeSelection *sel; GtkListStore *store; GtkCellRenderer *renderer; GtkTreeViewColumn *column; tree = swamigui_util_glade_lookup (modedit->glade_widg, "ModList"); store = gtk_list_store_new (NUM_FIELDS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT, IPATCH_TYPE_SF2_MOD); gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (store)); modedit->tree_view = tree; modedit->list_store = store; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE); g_signal_connect (sel, "changed", G_CALLBACK (swamigui_mod_edit_cb_mod_select_changed), modedit); /* Disable tree view search, since it breaks piano key playback */ g_object_set (tree, "enable-search", FALSE, NULL); /* destination label column */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Destination"), renderer, "text", DEST_LABEL, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); /* source pixbuf and label column */ renderer = gtk_cell_renderer_pixbuf_new (); column = gtk_tree_view_column_new_with_attributes (_("Source"), renderer, "pixbuf", SRC_PIXBUF, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "text", SRC_LABEL, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); /* amount source pixbuf and label column */ renderer = gtk_cell_renderer_pixbuf_new (); column = gtk_tree_view_column_new_with_attributes (_("Amount Source"), renderer, "pixbuf", AMT_PIXBUF, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "text", AMT_LABEL, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); /* amount value column */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Amount"), renderer, "text", AMT_VALUE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); return (tree); } static GtkTreeStore * swamigui_mod_edit_init_dest_combo_box (GtkWidget *combo_dest) { GtkCellRenderer *renderer; GtkTreeStore *store; GtkTreeIter group_iter, gen_iter; int group, gen; char *name; /* get ipatch_sf2_gen_info from libinstpatch library */ const IpatchSF2GenInfo * ipatch_sf2_gen_info = ipatch_sf2_get_gen_info(); store = gtk_tree_store_new (DEST_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_INT); for (group = 0, gen = 0; group < MODGROUP_COUNT; group++) { /* append group name */ gtk_tree_store_append (store, &group_iter, NULL); gtk_tree_store_set (store, &group_iter, DEST_COLUMN_TEXT, modgroup_names[group], DEST_COLUMN_ID, DEST_COLUMN_ID_IS_GROUP | group, -1); while (modgroup_gens[gen] != MODGROUP_SEPARATOR) { gtk_tree_store_append (store, &gen_iter, &group_iter); name = ipatch_sf2_gen_info[modgroup_gens[gen]].label; gtk_tree_store_set (store, &gen_iter, DEST_COLUMN_TEXT, name, DEST_COLUMN_ID, modgroup_gens[gen], -1); gen++; } gen++; } renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_dest), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_dest), renderer, "text", DEST_COLUMN_TEXT, NULL); return (store); } /** * swamigui_mod_edit_new: * * Create a new modulator editor object * * Returns: New widget of type SwamiguiModEdit */ GtkWidget * swamigui_mod_edit_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_mod_edit_get_type ()))); } /** * swamigui_mod_edit_set_selection: * @modedit: Modulation editor object * @selection: Item selection to assign to modulator editor or %NULL (should * currently contain one item only) * * Set item to edit modulator list of. If @item does not implement the * #IpatchSF2ModItem interface then it is deactivated. */ void swamigui_mod_edit_set_selection (SwamiguiModEdit *modedit, IpatchList *selection) { if (swamigui_mod_edit_real_set_selection (modedit, selection)) g_object_notify (G_OBJECT (modedit), "item-selection"); } static gboolean swamigui_mod_edit_real_set_selection (SwamiguiModEdit *modedit, IpatchList *selection) { GObject *item = NULL; g_return_val_if_fail (modedit != NULL, FALSE); g_return_val_if_fail (SWAMIGUI_IS_MOD_EDIT (modedit), FALSE); g_return_val_if_fail (!selection || IPATCH_IS_LIST (selection), FALSE); /* valid if single item and it has a "modulators" property */ if (selection && selection->items && !selection->items->next && g_object_class_find_property (G_OBJECT_GET_CLASS (selection->items->data), "modulators")) item = G_OBJECT (selection->items->data); if (!item) selection = NULL; /* same item already selected? */ if ((!selection && !modedit->selection) || (selection && modedit->selection && selection->items->data == modedit->selection->items->data)) return (FALSE); if (modedit->selection) g_object_unref (modedit->selection); if (selection) modedit->selection = g_object_ref (selection); else modedit->selection = NULL; /* disconnect any current connections to modulator editor "modulators" */ swami_control_disconnect_all (modedit->modctrl); if (modedit->mods) { ipatch_sf2_mod_list_free (modedit->mods, TRUE); modedit->mods = NULL; } /* connect modulator editor to item "modulators" property */ if (item) { swami_control_prop_connect_objects (G_OBJECT (item), "modulators", G_OBJECT (modedit), NULL, SWAMI_CONTROL_CONN_BIDIR); g_object_get (item, "modulators", &modedit->mods, NULL); } swamigui_mod_edit_update (modedit); return (TRUE); } /** * swamigui_mod_edit_set_mods: * @modedit: Modulation editor object * @mods: List of modulators to assign to modulator editor object, and thus its * active item's modulators as well. List is duplicated. * * Assign modulators to a modulator editor object and the item it is editing. */ void swamigui_mod_edit_set_mods (SwamiguiModEdit *modedit, IpatchSF2ModList *mods) { if (swamigui_mod_edit_real_set_mods (modedit, mods)) g_object_notify ((GObject *)modedit, "modulators"); } static gboolean swamigui_mod_edit_real_set_mods (SwamiguiModEdit *modedit, IpatchSF2ModList *mods) { g_return_val_if_fail (SWAMIGUI_IS_MOD_EDIT (modedit), FALSE); if (modedit->mods) ipatch_sf2_mod_list_free (modedit->mods, TRUE); modedit->mods = ipatch_sf2_mod_list_duplicate (mods); swamigui_mod_edit_update (modedit); return (TRUE); } typedef struct { int count; GtkTreeIter iter; } ForeachSelBag; /* callback for when a modulator gets selected in the list */ static void swamigui_mod_edit_cb_mod_select_changed (GtkTreeSelection *selection, SwamiguiModEdit *modedit) { ForeachSelBag selbag; selbag.count = 0; /* count selection and get first selected iter */ gtk_tree_selection_selected_foreach (selection, swamigui_mod_edit_tree_selection_count, &selbag); if (selbag.count == 1) swamigui_mod_edit_set_active_mod (modedit, &selbag.iter, FALSE); else /* disable editor */ swamigui_mod_edit_set_active_mod (modedit, NULL, TRUE); } static void swamigui_mod_edit_tree_selection_count (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { ForeachSelBag *selbag = (ForeachSelBag *)data; selbag->count += 1; if (selbag->count == 1) selbag->iter = *iter; } /* callback for new modulator button click */ static void swamigui_mod_edit_cb_new_clicked (GtkButton *btn, SwamiguiModEdit *modedit) { GtkTreeIter iter; GtkTreeSelection *selection; IpatchSF2Mod *mod; if (!modedit->selection) return; mod = ipatch_sf2_mod_new (); modedit->mods = ipatch_sf2_mod_list_insert (modedit->mods, mod, -1); g_object_notify ((GObject *)modedit, "modulators"); gtk_list_store_append (modedit->list_store, &iter); gtk_list_store_set (modedit->list_store, &iter, MOD_PTR, mod, -1); ipatch_sf2_mod_free (mod); /* select the new item */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (modedit->tree_view)); gtk_tree_selection_unselect_all (selection); gtk_tree_selection_select_iter (selection, &iter); swamigui_mod_edit_update_store_row (modedit, &iter); } /* callback for delete modulator button click */ static void swamigui_mod_edit_cb_delete_clicked (GtkButton *btn, SwamiguiModEdit *modedit) { GtkTreeSelection *sel; GtkTreeIter *iter; GList *sel_iters = NULL, *p; IpatchSF2Mod *mod; gboolean changed; gboolean anychanged = FALSE; if (!modedit->selection) return; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (modedit->tree_view)); gtk_tree_selection_selected_foreach (sel, swamigui_mod_edit_selection_foreach, &sel_iters); p = sel_iters; while (p) { iter = (GtkTreeIter *)(p->data); gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), iter, MOD_PTR, &mod, -1); if (mod) { modedit->mods = ipatch_sf2_mod_list_remove (modedit->mods, mod, &changed); if (changed) anychanged = TRUE; } gtk_list_store_remove (modedit->list_store, iter); ipatch_sf2_mod_free (mod); /* ## FREE mod from gtk_tree_model_get */ gtk_tree_iter_free (iter); p = g_list_next (p); } g_list_free (sel_iters); if (anychanged) g_object_notify ((GObject *)modedit, "modulators"); } /* a tree selection foreach callback to remove selected modulators */ static void swamigui_mod_edit_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { GList **sel_iters = (GList **)data; GtkTreeIter *copy; copy = gtk_tree_iter_copy (iter); *sel_iters = g_list_append (*sel_iters, copy); } /* callback when destination combo box is changed */ static void swamigui_mod_edit_cb_destination_changed (GtkComboBox *combo, gpointer user_data) { SwamiguiModEdit *modedit = SWAMIGUI_MOD_EDIT (user_data); IpatchSF2Mod *oldmod, newmod; GtkTreeIter iter, parent; GtkWidget *label; int groupid, genid; char *s; if (modedit->block_callbacks || !modedit->mod_selected) return; label = swamigui_util_glade_lookup (modedit->glade_widg, "LabelDestination"); /* get active combo box item iterator and its parent group, return if none */ if (!gtk_combo_box_get_active_iter (combo, &iter) || !gtk_tree_model_iter_parent (GTK_TREE_MODEL (modedit->dest_store), &parent, &iter)) { gtk_label_set_text (GTK_LABEL (label), ""); return; } /* get the group ID value */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->dest_store), &parent, DEST_COLUMN_ID, &groupid, -1); groupid &= ~DEST_COLUMN_ID_IS_GROUP; /* mask off the IS group flag */ s = g_strdup_printf ("%s", modgroup_names[groupid]); gtk_label_set_markup (GTK_LABEL (label), s); g_free (s); /* get the generator ID of the selected item */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->dest_store), &iter, DEST_COLUMN_ID, &genid, -1); /* get current modulator values (gtk_tree_model_get duplicates mod) */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), &modedit->mod_iter, MOD_PTR, &oldmod, -1); newmod = *oldmod; newmod.dest = genid; /* set the modulator values in the modulator list and notify property */ if (ipatch_sf2_mod_list_change (modedit->mods, oldmod, &newmod)) g_object_notify ((GObject *)modedit, "modulators"); ipatch_sf2_mod_free (oldmod); /* free oldmod from gtk_tree_model_get */ /* update the modulator in the list store */ gtk_list_store_set (modedit->list_store, &modedit->mod_iter, MOD_PTR, &newmod, -1); swamigui_mod_edit_update_store_row (modedit, &modedit->mod_iter); } /* callback for modulator source controller transform icon combo change */ static void swamigui_mod_edit_cb_pixcombo_changed (IconCombo *pixcombo, int id, SwamiguiModEdit *modedit) { IpatchSF2Mod *oldmod, newmod; guint16 *src; if (modedit->block_callbacks || !modedit->mod_selected) return; /* get current modulator values (gtk_tree_model_get duplicates mod) */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), &modedit->mod_iter, MOD_PTR, &oldmod, -1); newmod = *oldmod; /* duplicate modulator values */ if (pixcombo == g_object_get_data (G_OBJECT (modedit->glade_widg), "PIXSrc")) src = &newmod.src; else src = &newmod.amtsrc; *src &= ~(IPATCH_SF2_MOD_MASK_TYPE | IPATCH_SF2_MOD_MASK_DIRECTION | IPATCH_SF2_MOD_MASK_POLARITY); *src |= id; /* set the modulator values in the modulator list and notify property */ if (ipatch_sf2_mod_list_change (modedit->mods, oldmod, &newmod)) g_object_notify ((GObject *)modedit, "modulators"); ipatch_sf2_mod_free (oldmod); /* free oldmod from gtk_tree_model_get */ /* update the modulator in the list store */ gtk_list_store_set (modedit->list_store, &modedit->mod_iter, MOD_PTR, &newmod, -1); swamigui_mod_edit_update_store_row (modedit, &modedit->mod_iter); } /* callback for modulator source controller combo changes */ static void swamigui_mod_edit_cb_combo_src_ctrl_changed (GtkComboBox *combo, gpointer user_data) { SwamiguiModEdit *modedit = SWAMIGUI_MOD_EDIT (user_data); IpatchSF2Mod *oldmod, newmod; GtkTreeIter active_iter; GtkWidget *widg; guint16 *src; int ctrl; if (!gtk_combo_box_get_active_iter (combo, &active_iter)) return; if (modedit->block_callbacks || !modedit->mod_selected) return; /* get current modulator values (gtk_tree_model_get duplicates mod) */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), &modedit->mod_iter, MOD_PTR, &oldmod, -1); newmod = *oldmod; /* duplicate modulator values */ /* which source controller combo list? */ widg = swamigui_util_glade_lookup (modedit->glade_widg, "COMSrcCtrl"); if ((void *)widg == (void *)combo) src = &newmod.src; else src = &newmod.amtsrc; gtk_tree_model_get (GTK_TREE_MODEL (modedit->src_store), &active_iter, SRC_STORE_CTRLNUM, &ctrl, -1); *src &= ~(IPATCH_SF2_MOD_MASK_CONTROL | IPATCH_SF2_MOD_MASK_CC); *src |= ctrl; /* set the modulator values in the modulator list and notify property */ if (ipatch_sf2_mod_list_change (modedit->mods, oldmod, &newmod)) g_object_notify ((GObject *)modedit, "modulators"); ipatch_sf2_mod_free (oldmod); /* free oldmod from gtk_tree_model_get */ /* update the modulator in the list store */ gtk_list_store_set (modedit->list_store, &modedit->mod_iter, MOD_PTR, &newmod, -1); swamigui_mod_edit_update_store_row (modedit, &modedit->mod_iter); } /* callback for modulator amount source spin button value change */ static void swamigui_mod_edit_cb_amtsrc_changed (GtkAdjustment *adj, SwamiguiModEdit *modedit) { IpatchSF2Mod *oldmod, newmod; if (modedit->block_callbacks || !modedit->mod_selected) return; /* get current modulator values (gtk_tree_model_get duplicates mod) */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), &modedit->mod_iter, MOD_PTR, &oldmod, -1); newmod = *oldmod; /* duplicate modulator values */ newmod.amount = (gint16)(adj->value); /* set the modulator values in the modulator list and notify property */ if (ipatch_sf2_mod_list_change (modedit->mods, oldmod, &newmod)) g_object_notify ((GObject *)modedit, "modulators"); ipatch_sf2_mod_free (oldmod); /* free oldmod from gtk_tree_model_get */ /* update the modulator in the list store */ gtk_list_store_set (modedit->list_store, &modedit->mod_iter, MOD_PTR, &newmod, -1); swamigui_mod_edit_update_store_row (modedit, &modedit->mod_iter); } /* synchronizes modulator editor to current modulator list */ static void swamigui_mod_edit_update (SwamiguiModEdit *modedit) { GtkTreeIter iter; GSList *p; gtk_list_store_clear (modedit->list_store); swamigui_mod_edit_set_active_mod (modedit, NULL, FALSE); /* disable editor */ if (!modedit->mods) return; for (p = modedit->mods; p; p = p->next) { gtk_list_store_append (modedit->list_store, &iter); gtk_list_store_set (modedit->list_store, &iter, MOD_PTR, (IpatchSF2Mod *)(p->data), -1); swamigui_mod_edit_update_store_row (modedit, &iter); } } /* update a modulator in the list view */ static void swamigui_mod_edit_update_store_row (SwamiguiModEdit *modedit, GtkTreeIter *iter) { GdkPixbuf *pixbuf; char *stock_id; int group; IpatchSF2Mod *mod; char *s; /* get ipatch_sf2_gen_info from libinstpatch library */ const IpatchSF2GenInfo * ipatch_sf2_gen_info = ipatch_sf2_get_gen_info(); /* `mod' will be newly allocated */ gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), iter, MOD_PTR, &mod, -1); /* set mod destination label */ group = swamigui_mod_edit_find_gen_group (mod->dest, NULL); if (group >= 0) s = g_strdup_printf ("%s: %s", _(modgroup_names[group]), _(ipatch_sf2_gen_info[mod->dest].label)); else s = g_strdup_printf (_("Invalid (genid = %d)"), mod->dest); gtk_list_store_set (modedit->list_store, iter, DEST_LABEL, s, -1); g_free (s); /* set source pixbuf */ stock_id = swamigui_mod_edit_find_transform_icon (mod->src); if (stock_id) { pixbuf = gtk_widget_render_icon (modedit->tree_view, stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); gtk_list_store_set (modedit->list_store, iter, SRC_PIXBUF, pixbuf, -1); } /* set source label */ s = swamigui_mod_edit_get_control_name (mod->src); if (!s) s = g_strdup_printf (_("Invalid (cc = %d, index = %d)"), ((mod->src & IPATCH_SF2_MOD_MASK_CC) != 0), mod->src & ~IPATCH_SF2_MOD_MASK_CC); gtk_list_store_set (modedit->list_store, iter, SRC_LABEL, s, -1); g_free (s); stock_id = swamigui_mod_edit_find_transform_icon (mod->amtsrc); if (stock_id) { pixbuf = gtk_widget_render_icon (modedit->tree_view, stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); gtk_list_store_set (modedit->list_store, iter, AMT_PIXBUF, pixbuf, -1); } /* set amount source label */ s = swamigui_mod_edit_get_control_name (mod->amtsrc); if (!s) s = g_strdup_printf (_("Invalid (cc = %d, index = %d)"), ((mod->amtsrc & IPATCH_SF2_MOD_MASK_CC) != 0), mod->amtsrc & ~IPATCH_SF2_MOD_MASK_CC); gtk_list_store_set (modedit->list_store, iter, AMT_LABEL, s, -1); g_free (s); /* set amount value */ gtk_list_store_set (modedit->list_store, iter, AMT_VALUE, mod->amount, -1); ipatch_sf2_mod_free (mod); /* ## FREE modulator from gtk_tree_model_get */ } /* Bag used for finding and selecting source control combo box items by ctrlnum */ typedef struct { GtkComboBox *combo; int ctrlnum; } SelectSrcBag; /* set the modulator that is being edited, or NULL to disable. */ static void swamigui_mod_edit_set_active_mod (SwamiguiModEdit *modedit, GtkTreeIter *iter, gboolean force) { GtkWidget *pixsrc, *comsrc, *comdst, *lbldst, *spbamt, *pixamt, *comamt; GtkTreeIter destiter; IpatchSF2Mod *mod = NULL; GtkWidget *gw; int transform, ctrlnum, group, index; SelectSrcBag selectbag; int pathcmp = 1; char *pathstr; char *s; /* get paths for new and current GtkTreeIter and compare them to see if request to set already set modulator */ if (iter && modedit->mod_selected) { GtkTreePath *newpath, *curpath; newpath = gtk_tree_model_get_path (GTK_TREE_MODEL (modedit->list_store), iter); curpath = gtk_tree_model_get_path (GTK_TREE_MODEL (modedit->list_store), &modedit->mod_iter); pathcmp = gtk_tree_path_compare (newpath, curpath); gtk_tree_path_free (newpath); gtk_tree_path_free (curpath); } else if (!iter && !modedit->mod_selected) pathcmp = 0; /* already disabled */ if (!force && pathcmp == 0) return; if (iter) { modedit->mod_selected = TRUE; modedit->mod_iter = *iter; gtk_tree_model_get (GTK_TREE_MODEL (modedit->list_store), iter, MOD_PTR, &mod, -1); } else modedit->mod_selected = FALSE; gw = modedit->glade_widg; pixsrc = g_object_get_data (G_OBJECT (gw), "PIXSrc"); comsrc = swamigui_util_glade_lookup (gw, "COMSrcCtrl"); comdst = swamigui_util_glade_lookup (gw, "ComboDestination"); lbldst = swamigui_util_glade_lookup (gw, "LabelDestination"); spbamt = swamigui_util_glade_lookup (gw, "SPBAmount"); pixamt = g_object_get_data (G_OBJECT (gw), "PIXAmtSrc"); comamt = swamigui_util_glade_lookup (gw, "COMAmtCtrl"); gtk_widget_set_sensitive (pixsrc, mod != NULL); gtk_widget_set_sensitive (comsrc, mod != NULL); gtk_widget_set_sensitive (comdst, mod != NULL); gtk_widget_set_sensitive (spbamt, mod != NULL); gtk_widget_set_sensitive (pixamt, mod != NULL); gtk_widget_set_sensitive (comamt, mod != NULL); modedit->block_callbacks = TRUE; /* block signal callbacks */ /* set transform icon for source control */ transform = mod ? mod->src & (IPATCH_SF2_MOD_MASK_TYPE | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_DIRECTION) : 0; icon_combo_select_icon (ICON_COMBO (pixsrc), transform); /* set control combo for source control */ ctrlnum = mod ? mod->src & (IPATCH_SF2_MOD_MASK_CONTROL | IPATCH_SF2_MOD_MASK_CC) : 0; selectbag.combo = GTK_COMBO_BOX (comsrc); selectbag.ctrlnum = ctrlnum; gtk_tree_model_foreach (GTK_TREE_MODEL (modedit->src_store), swamigui_mod_edit_select_src_ctrl, &selectbag); if (selectbag.combo) // Set to NULL if control was found and selected gtk_combo_box_set_active (GTK_COMBO_BOX (comsrc), -1); /* set destination generator group option menu */ group = mod ? swamigui_mod_edit_find_gen_group (mod->dest, &index) : -1; if (group >= 0) { /* create group:index path string to select active combo box destination generator */ pathstr = g_strdup_printf ("%d:%d", group, index); /* ++ alloc */ if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (modedit->dest_store), &destiter, pathstr)) gtk_combo_box_set_active_iter (GTK_COMBO_BOX (comdst), &destiter); g_free (pathstr); /* -- free */ s = g_strdup_printf ("%s", modgroup_names[group]); gtk_label_set_markup (GTK_LABEL (lbldst), s); g_free (s); } else { gtk_combo_box_set_active (GTK_COMBO_BOX (comdst), -1); gtk_label_set_text (GTK_LABEL (lbldst), ""); } /* set amount spin button */ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spbamt), mod ? mod->amount : 0); /* set transform icon for amount source control */ transform = mod ? mod->amtsrc & (IPATCH_SF2_MOD_MASK_TYPE | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_DIRECTION) : 0; icon_combo_select_icon (ICON_COMBO (pixamt), transform); /* set control combo for amount source control */ ctrlnum = mod ? mod->amtsrc & (IPATCH_SF2_MOD_MASK_CONTROL | IPATCH_SF2_MOD_MASK_CC) : 0; selectbag.combo = GTK_COMBO_BOX (comamt); selectbag.ctrlnum = ctrlnum; gtk_tree_model_foreach (GTK_TREE_MODEL (modedit->src_store), swamigui_mod_edit_select_src_ctrl, &selectbag); if (selectbag.combo) // Set to NULL if control was found and selected gtk_combo_box_set_active (GTK_COMBO_BOX (comsrc), -1); modedit->block_callbacks = FALSE; /* unblock callbacks */ if (mod) ipatch_sf2_mod_free (mod); } /* Function for gtk_tree_model_foreach() which will select the source control * by modulator control number */ static gboolean swamigui_mod_edit_select_src_ctrl (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { SelectSrcBag *bag = data; int ctrlnum; gtk_tree_model_get (model, iter, SRC_STORE_CTRLNUM, &ctrlnum, -1); if (ctrlnum == bag->ctrlnum) { gtk_combo_box_set_active_iter (bag->combo, iter); bag->combo = NULL; return (TRUE); // Stop iterating } else return (FALSE); } /* returns a description for the control of a modulator source enumeration, string should be freed when finished with. Returns NULL if modsrc is invalid. */ static char * swamigui_mod_edit_get_control_name (guint16 modsrc) { const char *descr = NULL; int ctrlnum, i; ctrlnum = modsrc & IPATCH_SF2_MOD_MASK_CONTROL; if (modsrc & IPATCH_SF2_MOD_MASK_CC) { /* MIDI CC controller */ if ((ctrlnum >= 20 && ctrlnum <= 31) || (ctrlnum >= 70 && ctrlnum <= 79) || (ctrlnum >= 84 && ctrlnum <= 90) || (ctrlnum >= 102 && ctrlnum <= 119)) descr = "Undefined"; /* loop over control descriptions */ for (i = 0; i < MIDICC_DESCR_COUNT; i++) { if (midicc_descr[i].ctrlnum == ctrlnum) { descr = _(midicc_descr[i].descr); break; } } if (descr) return (g_strdup_printf (_("CC %d %s"), ctrlnum, descr)); } else { /* general modulator source controller */ for (i = 0; i < MODCTRL_DESCR_COUNT; i++) { if (modctrl_descr[i].ctrlnum == ctrlnum) return (g_strdup (_(modctrl_descr[i].descr))); } } return (NULL); } /* returns the icon stock ID for the transform type of the given modulator source enumeration or NULL if invalid */ static char * swamigui_mod_edit_find_transform_icon (guint16 modsrc) { int transform; int i; transform = modsrc & (IPATCH_SF2_MOD_MASK_TYPE | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_DIRECTION); for (i = 0; i < 16; i++) { if (modtransform_elements[i].id == transform) return (modtransform_elements[i].stock_id); } return (NULL); } /* determines the group a generator is part of and returns the group index or -1 if generator is not a valid modulator source, if index != NULL then the index within the group is stored in it */ static int swamigui_mod_edit_find_gen_group (int genid, int *index) { int group = 0; int i, groupndx = 0; for (i = 0; i < MODGROUP_GENS_SIZE; i++) { if (modgroup_gens[i] != MODGROUP_SEPARATOR) { if (modgroup_gens[i] == genid) break; groupndx++; } else /* group separator */ { group++; groupndx = 0; } } if (index) *index = groupndx; if (i < MODGROUP_GENS_SIZE) return (group); else return (-1); } swami-2.2.0/src/swamigui/SwamiguiModEdit.h000066400000000000000000000052461361104770400204650ustar00rootroot00000000000000/* * SwamiguiModEdit.h - User interface modulator editor object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_MOD_EDIT_H__ #define __SWAMIGUI_MOD_EDIT_H__ #include #include #include typedef struct _SwamiguiModEdit SwamiguiModEdit; typedef struct _SwamiguiModEditClass SwamiguiModEditClass; #define SWAMIGUI_TYPE_MOD_EDIT (swamigui_mod_edit_get_type ()) #define SWAMIGUI_MOD_EDIT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_MOD_EDIT, SwamiguiModEdit)) #define SWAMIGUI_MOD_EDIT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_MOD_EDIT, \ SwamiguiModEditClass)) #define SWAMIGUI_IS_MOD_EDIT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_MOD_EDIT)) #define SWAMIGUI_IS_MOD_EDIT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_MOD_EDIT)) struct _SwamiguiModEdit { GtkScrolledWindow parent; IpatchList *selection; /* item selection or NULL (single item only) */ IpatchSF2ModList *mods; /* modulator list being edited (copy) */ SwamiControl *modctrl; /* "modulatos" property control */ GtkWidget *tree_view; /* tree view widget for modulator list */ GtkListStore *list_store; /* GtkTreeModel list store of modulator list */ gboolean mod_selected; /* modulator selected? (mod_iter is valid) */ GtkTreeIter mod_iter; /* modulator list node being edited */ GtkWidget *glade_widg; /* glade generated editor widget */ gboolean block_callbacks; /* blocks modulator editor callbacks */ GtkTreeStore *dest_store; /* destination combo box tree store */ GtkListStore *src_store; /* Source control list store */ }; struct _SwamiguiModEditClass { GtkScrolledWindowClass parent_class; }; GType swamigui_mod_edit_get_type (void); GtkWidget *swamigui_mod_edit_new (void); void swamigui_mod_edit_set_selection (SwamiguiModEdit *modedit, IpatchList *selection); #endif swami-2.2.0/src/swamigui/SwamiguiMultiSave.c000066400000000000000000000450141361104770400210410ustar00rootroot00000000000000/* * SwamiguiMultiSave.h - Multiple file save dialog * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiguiRoot.h" #include "SwamiguiMultiSave.h" #include "i18n.h" enum { SAVE_COLUMN, /* save check box */ CHANGED_COLUMN, /* changed text */ TITLE_COLUMN, /* title of item */ PATH_COLUMN, /* file path */ ITEM_COLUMN, /* associated IpatchItem */ N_COLUMNS }; static void swamigui_multi_save_finalize (GObject *object); static void browse_clicked (GtkButton *button, gpointer user_data); static void browse_file_chooser_response (GtkDialog *dialog, int response, gpointer user_data); static void save_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data); static void save_column_clicked (GtkTreeViewColumn *column, gpointer data); static void path_edited (GtkCellRendererText *cell, const char *path_string, const char *new_text, gpointer data); static gboolean swamigui_multi_save_treeview_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data); static void multi_save_response (GtkDialog *dialog, int response, gpointer user_data); G_DEFINE_TYPE (SwamiguiMultiSave, swamigui_multi_save, GTK_TYPE_DIALOG); static void swamigui_multi_save_class_init (SwamiguiMultiSaveClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = swamigui_multi_save_finalize; } static void swamigui_multi_save_init (SwamiguiMultiSave *multi) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *hbox; GtkWidget *frame; GtkWidget *btn; GtkWidget *image; GtkTooltips *tooltips; /* tool tips for dialog widgets */ tooltips = gtk_tooltips_new (); gtk_window_set_default_size (GTK_WINDOW (multi), 600, 300); hbox = gtk_hbox_new (FALSE, 8); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multi)->vbox), hbox, FALSE, FALSE, 8); /* icon image */ multi->icon = gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), multi->icon, FALSE, FALSE, 0); /* message label */ multi->message = gtk_label_new (""); gtk_label_set_line_wrap (GTK_LABEL (multi->message), TRUE); gtk_box_pack_start (GTK_BOX (hbox), multi->message, FALSE, FALSE, 0); /* browse button */ btn = gtk_button_new_with_label (_("Browse")); image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (btn), image); gtk_box_pack_end (GTK_BOX (hbox), btn, FALSE, FALSE, 0); g_signal_connect (btn, "clicked", G_CALLBACK (browse_clicked), multi); gtk_widget_show_all (hbox); /* frame for list */ frame = gtk_frame_new (NULL); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multi)->vbox), frame, TRUE, TRUE, 0); /* scroll window for list */ multi->scroll_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (multi->scroll_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show (multi->scroll_win); gtk_container_add (GTK_CONTAINER (frame), multi->scroll_win); /* ++ ref list store */ multi->store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, IPATCH_TYPE_ITEM); /* tree view */ multi->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (multi->store)); gtk_widget_set_has_tooltip (multi->treeview, TRUE); g_signal_connect (multi->treeview, "query-tooltip", G_CALLBACK (swamigui_multi_save_treeview_query_tooltip), multi); gtk_container_add (GTK_CONTAINER (multi->scroll_win), multi->treeview); renderer = gtk_cell_renderer_toggle_new (); g_signal_connect (renderer, "toggled", G_CALLBACK (save_toggled), multi->store); column = gtk_tree_view_column_new_with_attributes (_("Save"), renderer, "active", SAVE_COLUMN, NULL); gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); g_signal_connect (column, "clicked", G_CALLBACK (save_column_clicked), multi); gtk_tree_view_append_column (GTK_TREE_VIEW (multi->treeview), column); gtk_tooltips_set_tip (tooltips, GTK_TREE_VIEW_COLUMN (column)->button, _("Select which files to save."), NULL); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Changed"), renderer, "text", CHANGED_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (multi->treeview), column); gtk_tooltips_set_tip (tooltips, GTK_TREE_VIEW_COLUMN (column)->button, _("File changed since last save?"), NULL); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ellipsize-set", TRUE, NULL); column = gtk_tree_view_column_new_with_attributes ("Title", renderer, "text", TITLE_COLUMN, NULL); g_object_set (column, "resizable", TRUE, "expand", TRUE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (multi->treeview), column); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "editable", TRUE, "ellipsize", PANGO_ELLIPSIZE_START, "ellipsize-set", TRUE, NULL); g_signal_connect (renderer, "edited", G_CALLBACK (path_edited), multi->store); column = gtk_tree_view_column_new_with_attributes ("Path", renderer, "text", PATH_COLUMN, NULL); g_object_set (column, "resizable", TRUE, "expand", TRUE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (multi->treeview), column); gtk_widget_show_all (frame); gtk_dialog_add_buttons (GTK_DIALOG (multi), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); multi->accept_btn = gtk_dialog_add_button (GTK_DIALOG (multi), GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT); g_signal_connect (multi, "response", G_CALLBACK (multi_save_response), NULL); } static void swamigui_multi_save_finalize (GObject *object) { SwamiguiMultiSave *multi = SWAMIGUI_MULTI_SAVE (object); g_object_unref (multi->store); // -- unref list store if (G_OBJECT_CLASS (swamigui_multi_save_parent_class)->finalize) G_OBJECT_CLASS (swamigui_multi_save_parent_class)->finalize (object); } /* browse button clicked callback */ static void browse_clicked (GtkButton *button, gpointer user_data) { SwamiguiMultiSave *multi = SWAMIGUI_MULTI_SAVE (user_data); GtkWidget *filesel; GtkTreeModel *model; GtkTreePath *path; GtkTreeSelection *selection; GtkTreeIter iter; char *fname; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (multi->treeview)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, PATH_COLUMN, &fname, /* ++ alloc */ -1); filesel = gtk_file_chooser_dialog_new (_("Save file as"), GTK_WINDOW (multi), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); if (!fname) { g_object_get (swami_root, "patch-path", &fname, NULL); // ++ alloc patch path if (fname) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filesel), fname); } else gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filesel), fname); g_signal_connect (filesel, "response", G_CALLBACK (browse_file_chooser_response), multi); path = gtk_tree_model_get_path (model, &iter); /* ++ alloc new path */ g_object_set_data_full (G_OBJECT (filesel), "path", path, /* !! takes over alloc */ (GDestroyNotify)gtk_tree_path_free); gtk_widget_show (filesel); g_free (fname); /* -- free file name string */ } /* dialog response callback for file chooser dialog */ static void browse_file_chooser_response (GtkDialog *dialog, int response, gpointer user_data) { SwamiguiMultiSave *multi = SWAMIGUI_MULTI_SAVE (user_data); GtkTreePath *path; GtkTreeIter iter; char *fname; if (response != GTK_RESPONSE_ACCEPT) { gtk_object_destroy (GTK_OBJECT (dialog)); return; } /* ++ alloc file name from file chooser */ fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); path = g_object_get_data (G_OBJECT (dialog), "path"); gtk_tree_model_get_iter (GTK_TREE_MODEL (multi->store), &iter, path); gtk_list_store_set (multi->store, &iter, PATH_COLUMN, fname, -1); g_free (fname); /* -- free file name string */ gtk_object_destroy (GTK_OBJECT (dialog)); } static void save_toggled (GtkCellRendererToggle *cell, char *path_str, gpointer data) { GtkTreeModel *model = (GtkTreeModel *)data; GtkTreeIter iter; GtkTreePath *path; gboolean save; /* ++ alloc path from string */ path = gtk_tree_path_new_from_string (path_str); /* get toggled iter */ gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, SAVE_COLUMN, &save, -1); save ^= 1; /* toggle the value */ /* set new value */ gtk_list_store_set (GTK_LIST_STORE (model), &iter, SAVE_COLUMN, save, -1); gtk_tree_path_free (path); /* -- free path */ } // Callback when "save" column button gets clicked (toggle all save checkboxes) static void save_column_clicked (GtkTreeViewColumn *column, gpointer data) { GtkTreeView *treeview = GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (column)); GtkTreeModel *model = gtk_tree_view_get_model (treeview); gboolean all_checked = TRUE; GtkTreeIter iter; gboolean save; if (!gtk_tree_model_get_iter_first (model, &iter)) return; do { gtk_tree_model_get (model, &iter, SAVE_COLUMN, &save, -1); if (!save) { all_checked = FALSE; break; } } while (gtk_tree_model_iter_next (model, &iter)); gtk_tree_model_get_iter_first (model, &iter); do { gtk_list_store_set (GTK_LIST_STORE (model), &iter, SAVE_COLUMN, !all_checked, -1); } while (gtk_tree_model_iter_next (model, &iter)); } static void path_edited (GtkCellRendererText *cell, const char *path_string, const char *new_text, gpointer data) { GtkTreeModel *model = (GtkTreeModel *)data; GtkTreePath *path; GtkTreeIter iter; path = gtk_tree_path_new_from_string (path_string); gtk_tree_model_get_iter (model, &iter, path); gtk_list_store_set (GTK_LIST_STORE (model), &iter, PATH_COLUMN, new_text, -1); gtk_tree_path_free (path); } // Signal handler for query-tooltip on tree view static gboolean swamigui_multi_save_treeview_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data) { GtkTreeViewColumn *column; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GList *list; int colindex; char *s; if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y, keyboard_mode, &model, &path, &iter)) return (FALSE); if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y, NULL, &column, NULL, NULL)) return (FALSE); list = gtk_tree_view_get_columns (GTK_TREE_VIEW (widget)); // ++ alloc list colindex = g_list_index (list, column); g_list_free (list); // -- free list if (colindex == 2) gtk_tree_model_get (model, &iter, TITLE_COLUMN, &s, // ++ alloc -1); else if (colindex == 3) gtk_tree_model_get (model, &iter, PATH_COLUMN, &s, // ++ alloc -1); else return (FALSE); gtk_tooltip_set_text (tooltip, s); g_free (s); // -- free gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (widget), tooltip, path, column, NULL); return (TRUE); } /* called when dialog response received (button clicked by user) */ static void multi_save_response (GtkDialog *dialog, int response, gpointer user_data) { SwamiguiMultiSave *multi = SWAMIGUI_MULTI_SAVE (dialog); GtkTreeModel *model = GTK_TREE_MODEL (multi->store); GtkWidget *msgdialog; GtkTreeIter iter; gboolean save; char *path; IpatchItem *item; GError *err = NULL; int result; gboolean close_ok; IpatchList *close_list; if (response != GTK_RESPONSE_ACCEPT) { gtk_object_destroy (GTK_OBJECT (dialog)); return; } /* get first item in list (destroy dialog if no items) */ if (!gtk_tree_model_get_iter_first (model, &iter)) { gtk_object_destroy (GTK_OBJECT (dialog)); return; } close_list = ipatch_list_new (); // ++ ref new list do { gtk_tree_model_get (model, &iter, SAVE_COLUMN, &save, PATH_COLUMN, &path, /* ++ alloc path */ ITEM_COLUMN, &item, /* ++ ref item */ -1); close_ok = TRUE; if (save) { if (!swami_root_patch_save (item, path, &err)) { close_ok = FALSE; /* Don't close file if error on save */ msgdialog = gtk_message_dialog_new (GTK_WINDOW (dialog), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK_CANCEL, _("Error saving '%s': %s"), path, ipatch_gerror_message (err)); g_clear_error (&err); result = gtk_dialog_run (GTK_DIALOG (msgdialog)); gtk_widget_destroy (msgdialog); if (result == GTK_RESPONSE_CANCEL) { g_free (path); /* -- free path */ g_object_unref (item); /* -- unref item */ g_object_unref (close_list); // -- unref close list return; } } } /* Close if in close mode */ if (close_ok && (multi->flags & SWAMIGUI_MULTI_SAVE_CLOSE_MODE)) { g_object_ref (item); // ++ ref object for list close_list->items = g_list_prepend (close_list->items, item); } g_free (path); /* -- free path */ g_object_unref (item); /* -- unref item */ } while (gtk_tree_model_iter_next (model, &iter)); if (close_list->items) { close_list->items = g_list_reverse (close_list->items); if (!ipatch_close_base_list (close_list, &err)) { msgdialog = gtk_message_dialog_new (GTK_WINDOW (dialog), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Error closing files: %s"), ipatch_gerror_message (err)); g_clear_error (&err); gtk_dialog_run (GTK_DIALOG (msgdialog)); gtk_widget_destroy (msgdialog); } } g_object_unref (close_list); // -- unref close list gtk_object_destroy (GTK_OBJECT (dialog)); } /** * swamigui_multi_save_new: * @title: Title of dialog * @message: Message text * @flags: A value from #SwamiguiMultiSaveFlags or 0 * * Create a new multi file save dialog. * * Returns: New dialog widget. */ GtkWidget * swamigui_multi_save_new (char *title, char *message, guint flags) { SwamiguiMultiSave *multi; multi = g_object_new (SWAMIGUI_TYPE_MULTI_SAVE, NULL); if (title) gtk_window_set_title (GTK_WINDOW (multi), title); if (message) gtk_label_set_text (GTK_LABEL (multi->message), message); multi->flags = flags; if (flags & SWAMIGUI_MULTI_SAVE_CLOSE_MODE) gtk_button_set_label (GTK_BUTTON (multi->accept_btn), GTK_STOCK_CLOSE); return (GTK_WIDGET (multi)); } /** * swamigui_multi_save_set_selection: * @multi: Multi save widget * @selection: Selection of items to save (only #IpatchBase items or children * thereof are considered, children are followed up to their parent * #IpatchBase, duplicates are taken into account). * * Set the item selection of a multi save dialog. This is the list of items * that the user is prompted to save. */ void swamigui_multi_save_set_selection (SwamiguiMultiSave *multi, IpatchList *selection) { GHashTable *base_hash; IpatchItem *item, *base; GtkTreeSelection *treesel; GtkTreeIter iter; char *title, *path; gboolean changed, saved, close_mode; GList *p; g_return_if_fail (SWAMIGUI_IS_MULTI_SAVE (multi)); g_return_if_fail (IPATCH_IS_LIST (selection)); close_mode = (multi->flags & SWAMIGUI_MULTI_SAVE_CLOSE_MODE) != 0; gtk_list_store_clear (multi->store); /* hash to throw out duplicate base objects quickly */ base_hash = g_hash_table_new (NULL, NULL); for (p = selection->items; p; p = p->next) { item = (IpatchItem *)(p->data); base = ipatch_item_get_base (item); /* ++ ref base */ if (!base) continue; /* skip if this base object is already added to the list */ if (g_hash_table_lookup (base_hash, base)) { g_object_unref (base); /* -- unref base */ continue; } g_hash_table_insert (base_hash, base, GUINT_TO_POINTER (TRUE)); gtk_list_store_append (multi->store, &iter); /* ++ alloc title and path */ g_object_get (base, "title", &title, /* ++ alloc title */ "file-name", &path, /* ++ alloc path */ "changed", &changed, "saved", &saved, NULL); /* SAVE_COLUMN is set to TRUE if save mode or file has already been saved once */ gtk_list_store_set (multi->store, &iter, SAVE_COLUMN, !close_mode || saved, CHANGED_COLUMN, changed ? _("Yes") : _("No"), TITLE_COLUMN, title, PATH_COLUMN, path, ITEM_COLUMN, base, -1); g_free (title); /* -- free title */ g_free (path); /* -- free path */ g_object_unref (base); /* -- unref base */ } // Select first item in list if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (multi->store), &iter)) { treesel = gtk_tree_view_get_selection (GTK_TREE_VIEW (multi->treeview)); gtk_tree_selection_select_iter (treesel, &iter); } g_hash_table_destroy (base_hash); } swami-2.2.0/src/swamigui/SwamiguiMultiSave.h000066400000000000000000000052441361104770400210470ustar00rootroot00000000000000/* * SwamiguiMultiSave.h - Multiple file save dialog * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_MULTI_SAVE_H__ #define __SWAMIGUI_MULTI_SAVE_H__ #include #include typedef struct _SwamiguiMultiSave SwamiguiMultiSave; typedef struct _SwamiguiMultiSaveClass SwamiguiMultiSaveClass; #define SWAMIGUI_TYPE_MULTI_SAVE (swamigui_multi_save_get_type ()) #define SWAMIGUI_MULTI_SAVE(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_MULTI_SAVE, SwamiguiMultiSave)) #define SWAMIGUI_MULTI_SAVE_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_MULTI_SAVE, \ SwamiguiMultiSaveClass)) #define SWAMIGUI_IS_MULTI_SAVE(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_MULTI_SAVE)) #define SWAMIGUI_IS_MULTI_SAVE_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_MULTI_SAVE)) /* multi item save dialog */ struct _SwamiguiMultiSave { GtkDialog parent_instance; GtkListStore *store; /* list store */ guint flags; /* SwamiguiMultiSaveFlags */ GtkWidget *accept_btn; /* Save/Close button */ GtkWidget *treeview; /* tree view widget */ GtkWidget *icon; /* the icon of the dialog */ GtkWidget *message; /* message label at top of dialog */ GtkWidget *scroll_win; /* scroll window to put list in */ }; /* multi item save dialog class */ struct _SwamiguiMultiSaveClass { GtkDialogClass parent_class; }; /** * SwamiguiMultiSaveFlags: * @SWAMIGUI_MULTI_SAVE_CLOSE_MODE: Files will be closed upon dialog confirm and * accept button is changed to a "Close" button. * * Some flags for use with swamigui_multi_save_new(). */ typedef enum { SWAMIGUI_MULTI_SAVE_CLOSE_MODE = 1 << 0 } SwamiguiMultiSaveFlags; GType swamigui_multi_save_get_type (void); GtkWidget *swamigui_multi_save_new (char *title, char *message, guint flags); void swamigui_multi_save_set_selection (SwamiguiMultiSave *multi, IpatchList *selection); #endif swami-2.2.0/src/swamigui/SwamiguiNoteSelector.c000066400000000000000000000060721361104770400215370ustar00rootroot00000000000000/* * SwamiguiNoteSelector.c - MIDI note selector widget. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "SwamiguiNoteSelector.h" #include "i18n.h" static gint swamigui_note_selector_input (GtkSpinButton *spinbutton, gdouble *newval); static gboolean swamigui_note_selector_output (GtkSpinButton *spinbutton); /* define the note selector type */ G_DEFINE_TYPE (SwamiguiNoteSelector, swamigui_note_selector, GTK_TYPE_SPIN_BUTTON); static void swamigui_note_selector_class_init (SwamiguiNoteSelectorClass *klass) { GtkSpinButtonClass *spinbtn_class = GTK_SPIN_BUTTON_CLASS (klass); spinbtn_class->input = swamigui_note_selector_input; spinbtn_class->output = swamigui_note_selector_output; } static gint swamigui_note_selector_input (GtkSpinButton *spinbutton, gdouble *newval) { const char *text; int note; text = gtk_entry_get_text (GTK_ENTRY (spinbutton)); if (!text || strchr (text, '|')) return (FALSE); note = swami_util_midi_str_to_note (text); if (note == -1) return GTK_INPUT_ERROR; *newval = note; return (TRUE); } /* override spin button display "output" signal to show note strings */ static gboolean swamigui_note_selector_output (GtkSpinButton *spinbutton) { char notestr[9] = { 0 }; GtkAdjustment *adj; int note; adj = gtk_spin_button_get_adjustment (spinbutton); note = (int)adj->value; if (note >= 0 && note <= 127) { sprintf (notestr, "%d | ", note); swami_util_midi_note_to_str (note, notestr + strlen (notestr)); } if (strcmp (notestr, gtk_entry_get_text (GTK_ENTRY (spinbutton)))) gtk_entry_set_text (GTK_ENTRY (spinbutton), notestr); return (TRUE); } static void swamigui_note_selector_init (SwamiguiNoteSelector *notesel) { GtkAdjustment *adj; adj = (GtkAdjustment *)gtk_adjustment_new (60.0, 0.0, 127.0, 1.0, 12.0, 0.0); gtk_spin_button_configure (GTK_SPIN_BUTTON (notesel), adj, 1.0, 0); gtk_entry_set_width_chars (GTK_ENTRY (notesel), 10); } /** * swamigui_note_selector_new: * * Create a new MIDI note selector widget. * * Returns: New MIDI note selector. */ GtkWidget * swamigui_note_selector_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_NOTE_SELECTOR, NULL))); } swami-2.2.0/src/swamigui/SwamiguiNoteSelector.h000066400000000000000000000036771361104770400215540ustar00rootroot00000000000000/* * SwamiguiNoteSelector.h - MIDI note selector widget. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_NOTE_SELECTOR_H__ #define __SWAMIGUI_NOTE_SELECTOR_H__ #include #include typedef struct _SwamiguiNoteSelector SwamiguiNoteSelector; typedef struct _SwamiguiNoteSelectorClass SwamiguiNoteSelectorClass; #define SWAMIGUI_TYPE_NOTE_SELECTOR (swamigui_note_selector_get_type ()) #define SWAMIGUI_NOTE_SELECTOR(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_NOTE_SELECTOR, SwamiguiNoteSelector)) #define SWAMIGUI_NOTE_SELECTOR_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_NOTE_SELECTOR, \ SwamiguiNoteSelectorClass)) #define SWAMIGUI_IS_NOTE_SELECTOR(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_NOTE_SELECTOR)) #define SWAMIGUI_IS_NOTE_SELECTOR_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_NOTE_SELECTOR)) /* MIDI note selector widget */ struct _SwamiguiNoteSelector { GtkSpinButton parent_instance; }; /* MIDI note selector class */ struct _SwamiguiNoteSelectorClass { GtkSpinButtonClass parent_class; }; GType swamigui_note_selector_get_type (void); GtkWidget *swamigui_note_selector_new (); #endif swami-2.2.0/src/swamigui/SwamiguiPanel.c000066400000000000000000000132601361104770400201650ustar00rootroot00000000000000/* * SwamiguiPanel.c - Panel control interface type * For managing control interfaces in a plug-able way. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include "SwamiguiPanel.h" static void swamigui_panel_interface_init (SwamiguiPanelIface *panel_iface); GType swamigui_panel_get_type (void) { static GType itype = 0; if (!itype) { static const GTypeInfo info = { sizeof (SwamiguiPanelIface), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) swamigui_panel_interface_init, (GClassFinalizeFunc) NULL }; itype = g_type_register_static (G_TYPE_INTERFACE, "SwamiguiPanel", &info, 0); } return (itype); } static void swamigui_panel_interface_init (SwamiguiPanelIface *panel_iface) { g_object_interface_install_property (panel_iface, g_param_spec_object ("item-selection", "Item Selection", "Item selection list", IPATCH_TYPE_LIST, G_PARAM_READWRITE)); } /** * swamigui_panel_type_get_info: * @type: Type with a #SwamiguiPanel interface to get info on * @label: Out - User panel label (short, could be %NULL) * @blurb: Out - User panel description (longer, for tool tips, could be %NULL) * @stockid: Out - Stock ID string of icon for this panel (could be %NULL) * * Lookup info on a panel for a given @type. A %NULL value can be passed for any * of the string parameters to ignore it. Note also that a %NULL value may * be returned for any of the return parameters. The returned parameters are * internal and should not be modified or freed. */ void swamigui_panel_type_get_info (GType type, char **label, char **blurb, char **stockid) { SwamiguiPanelIface *panel_iface; GObjectClass *klass; g_return_if_fail (g_type_is_a (type, SWAMIGUI_TYPE_PANEL)); klass = g_type_class_ref (type); /* ++ ref class */ g_return_if_fail (klass != NULL); panel_iface = g_type_interface_peek (klass, SWAMIGUI_TYPE_PANEL); if (!panel_iface) g_type_class_unref (klass); /* -- unref class */ g_return_if_fail (panel_iface != NULL); if (label) *label = panel_iface->label; if (blurb) *blurb = panel_iface->blurb; if (stockid) *stockid = panel_iface->stockid; g_type_class_unref (klass); /* -- unref class */ } /** * swamigui_panel_type_check_selection: * @type: Type with a #SwamiguiPanel interface * @selection: Item selection to test support for, list should not be empty * @selection_types: 0 terminated array of unique item types in @selection or * %NULL to calculate the array * * Checks if the panel with the given @type supports the item @selection. The * @selection_types parameter is for optimization purposes, so that panel types * can quickly check if they should be active or not based on the item types * in the selection (possibly saving another iteration of the @selection), this * array will be calculated if not supplied, so its only useful if checking * many panel types for a given @selection. * * Returns: %TRUE if panel supports selection, %FALSE otherwise */ gboolean swamigui_panel_type_check_selection (GType type, IpatchList *selection, GType *selection_types) { SwamiguiPanelIface *panel_iface; GType *free_selection_types = NULL; GObjectClass *klass; gboolean retval; g_return_val_if_fail (g_type_is_a (type, SWAMIGUI_TYPE_PANEL), FALSE); g_return_val_if_fail (IPATCH_IS_LIST (selection), FALSE); g_return_val_if_fail (selection->items != NULL, FALSE); klass = g_type_class_ref (type); /* ++ ref class */ g_return_val_if_fail (klass != NULL, FALSE); panel_iface = g_type_interface_peek (klass, SWAMIGUI_TYPE_PANEL); if (!panel_iface) g_type_class_unref (klass); /* -- unref class */ g_return_val_if_fail (panel_iface != NULL, FALSE); if (!panel_iface->check_selection) { g_type_class_unref (klass); /* -- unref class */ return (TRUE); } if (!selection_types) /* ++ alloc */ free_selection_types = swamigui_panel_get_types_in_selection (selection); retval = panel_iface->check_selection (selection, selection_types); g_free (free_selection_types); /* -- free */ g_type_class_unref (klass); /* -- unref class */ return (retval); } /** * swamigui_panel_get_types_in_selection: * @selection: Item selection list * * Gets an array of unique item types in @selection. * * Returns: Newly allocated and 0 terminated array of unique types of items * in @selection. */ GType * swamigui_panel_get_types_in_selection (IpatchList *selection) { GArray *typearray; GType type; GList *p; guint i; typearray = g_array_new (TRUE, FALSE, sizeof (GType)); /* ++ alloc */ if (selection) { for (p = selection->items; p; p = p->next) { type = G_OBJECT_TYPE (p->data); for (i = 0; i < typearray->len; i++) if (g_array_index (typearray, GType, i) == type) break; if (i == typearray->len) g_array_append_val (typearray, type); } } return ((GType *)g_array_free (typearray, FALSE)); /* !! caller takes over allocation */ } swami-2.2.0/src/swamigui/SwamiguiPanel.h000066400000000000000000000052251361104770400201740ustar00rootroot00000000000000/* * SwamiguiPanel.h - Panel control interface type * For managing control interfaces in a plug-able way. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_PANEL_H__ #define __SWAMIGUI_PANEL_H__ typedef struct _SwamiguiPanel SwamiguiPanel; /* dummy typedef */ typedef struct _SwamiguiPanelIface SwamiguiPanelIface; #include #include #define SWAMIGUI_TYPE_PANEL (swamigui_panel_get_type ()) #define SWAMIGUI_PANEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PANEL, SwamiguiPanel)) #define SWAMIGUI_PANEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PANEL, SwamiguiPanelIface)) #define SWAMIGUI_IS_PANEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PANEL)) #define SWAMIGUI_PANEL_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SWAMIGUI_TYPE_PANEL, \ SwamiguiPanelIface)) /** * SwamiguiPanelCheckFunc: * @selection: Item selection to verify (will contain at least 1 item) * @selection_types: 0 terminated array of unique item GTypes found in @selection * * Function prototype used for checking if an item selection is valid for a * panel. * * Returns: Should return %TRUE if item selection is valid, %FALSE otherwise */ typedef gboolean (*SwamiguiPanelCheckFunc)(IpatchList *selection, GType *selection_types); struct _SwamiguiPanelIface { GTypeInterface parent_class; char *label; /* User label name for panel */ char *blurb; /* more descriptive text about panel */ char *stockid; /* stock ID of icon */ SwamiguiPanelCheckFunc check_selection; }; GType swamigui_panel_get_type (void); void swamigui_panel_type_get_info (GType type, char **label, char **blurb, char **stockid); gboolean swamigui_panel_type_check_selection (GType type, IpatchList *selection, GType *selection_types); GType *swamigui_panel_get_types_in_selection (IpatchList *selection); #endif swami-2.2.0/src/swamigui/SwamiguiPanelSF2Gen.c000066400000000000000000000344271361104770400211420ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include "SwamiguiControl.h" #include "SwamiguiPanelSF2Gen.h" #include "SwamiguiPanel.h" #include "SwamiguiRoot.h" #include "SwamiguiSpinScale.h" #include "icons.h" #include "util.h" #include "i18n.h" /* unit label used when generators are inactive */ #define BLANK_UNIT_LABEL "" /* structure used to keep track of generator widgets */ typedef struct { GtkWidget *button; /* value "set" toggle button */ GtkWidget *spinscale; /* SwamiguiSpinScale widget */ GtkWidget *unitlabel; /* unit label */ } GenWidgets; /* value used for generator property selection type, IPATCH_SF2_GEN_PROPS_INST * and IPATCH_SF2_GEN_PROPS_PRESET values are also used */ #define SEL_NONE -1 /* A separator (column or end) */ #define IS_SEPARATOR(genid) ((genid) >= SWAMIGUI_PANEL_SF2_GEN_COLUMN) /* Is value from SwamiguiPanelSF2GenOp? */ #define IS_OP(genid) ((genid) >= SWAMIGUI_PANEL_SF2_GEN_LABEL) enum { PROP_0, PROP_ITEM_SELECTION }; typedef struct _GenCtrl { SwamiguiPanelSF2Gen *genpanel; /* for GTK callback convenience */ GtkWidget *scale; /* scale widget */ GtkWidget *entry; /* entry widget */ GtkWidget *units; /* units label */ GtkWidget *defbtn; /* default toggle button */ guint16 genid; } GenCtrl; static void swamigui_panel_sf2_gen_panel_iface_init (SwamiguiPanelIface *panel_iface); static gboolean swamigui_sf2_ctrl_panel_iface_check_selection (IpatchList *selection, GType *selection_types); static void swamigui_panel_sf2_gen_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_panel_sf2_gen_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_panel_sf2_gen_finalize (GObject *object); static gboolean swamigui_panel_sf2_gen_real_set_selection (SwamiguiPanelSF2Gen *genpanel, IpatchList *selection); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SwamiguiPanelSF2Gen, swamigui_panel_sf2_gen, GTK_TYPE_SCROLLED_WINDOW, G_IMPLEMENT_INTERFACE (SWAMIGUI_TYPE_PANEL, swamigui_panel_sf2_gen_panel_iface_init)); static void swamigui_panel_sf2_gen_class_init (SwamiguiPanelSF2GenClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = swamigui_panel_sf2_gen_set_property; obj_class->get_property = swamigui_panel_sf2_gen_get_property; obj_class->finalize = swamigui_panel_sf2_gen_finalize; g_object_class_override_property (obj_class, PROP_ITEM_SELECTION, "item-selection"); } static void swamigui_panel_sf2_gen_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->check_selection = swamigui_sf2_ctrl_panel_iface_check_selection; } static gboolean swamigui_sf2_ctrl_panel_iface_check_selection (IpatchList *selection, GType *selection_types) { /* One item only with IpatchSF2GenItem interface */ return (!selection->items->next && g_type_is_a (G_OBJECT_TYPE (selection->items->data), IPATCH_TYPE_SF2_GEN_ITEM)); } static void swamigui_panel_sf2_gen_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiPanelSF2Gen *genpanel = SWAMIGUI_PANEL_SF2_GEN (object); switch (property_id) { case PROP_ITEM_SELECTION: swamigui_panel_sf2_gen_real_set_selection (genpanel, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_panel_sf2_gen_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiPanelSF2Gen *genpanel = SWAMIGUI_PANEL_SF2_GEN (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, genpanel->selection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_panel_sf2_gen_finalize (GObject *object) { SwamiguiPanelSF2Gen *genpanel = SWAMIGUI_PANEL_SF2_GEN (object); if (genpanel->selection) g_object_unref (genpanel->selection); G_OBJECT_CLASS (swamigui_panel_sf2_gen_parent_class)->finalize (object); } static void swamigui_panel_sf2_gen_init (SwamiguiPanelSF2Gen *genpanel) { genpanel->selection = NULL; genpanel->seltype = SEL_NONE; gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (genpanel), NULL); gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (genpanel), NULL); gtk_container_border_width (GTK_CONTAINER (genpanel), 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (genpanel), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); } /** * swamigui_panel_sf2_gen_new: * * Create a new generator control panel object. * * Returns: new widget of type #SwamiguiPanelSF2Gen */ GtkWidget * swamigui_panel_sf2_gen_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_panel_sf2_gen_get_type ()))); } /** * swamigui_panel_sf2_gen_set_controls: * @genpanel: Generator control panel * @ctrlinfo: Array of control info to configure panel with (should be static) * * Configure a SoundFont generator control panel from an array of control * info. @ctrlinfo array should be terminated with a #SWAMIGUI_PANEL_SF2_GEN_END * genid item. */ void swamigui_panel_sf2_gen_set_controls (SwamiguiPanelSF2Gen *genpanel, SwamiguiPanelSF2GenCtrlInfo *ctrlinfo) { GtkWidget *hbox; GtkWidget *table; GtkWidget *widg; GtkWidget *image; GtkWidget *btn; GtkWidget *frame; GenWidgets *genwidgets; int i, ctrlndx, genid, row; SwamiguiPanelSF2GenCtrlInfo *ctrlp; /* get ipatch_sf2_gen_info from libinstpatch library */ const IpatchSF2GenInfo * ipatch_sf2_gen_info = ipatch_sf2_get_gen_info(); g_return_if_fail (SWAMIGUI_IS_PANEL_SF2_GEN (genpanel)); g_return_if_fail (ctrlinfo != NULL); g_return_if_fail (genpanel->ctrlinfo == NULL); genpanel->ctrlinfo = ctrlinfo; genpanel->genwidget_count = 0; /* Count controls */ for (ctrlp = ctrlinfo; ctrlp->genid != SWAMIGUI_PANEL_SF2_GEN_END; ctrlp++) if (!IS_OP (ctrlp->genid)) genpanel->genwidget_count++; /* allocate array of gen widget structures */ genwidgets = g_new (GenWidgets, genpanel->genwidget_count); genpanel->genwidgets = genwidgets; hbox = gtk_hbox_new (TRUE, 4); ctrlndx = 0; next_column: frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0); /* Get number of rows in this column */ for (ctrlp = ctrlinfo, i = 0; !IS_SEPARATOR (ctrlp->genid); ctrlp++, i++); /* Create table for controls */ table = gtk_table_new (i, 5, FALSE); gtk_container_add (GTK_CONTAINER (frame), table); /* loop over controls for this page */ for (ctrlp = ctrlinfo, row = 0; !IS_SEPARATOR (ctrlp->genid); ctrlp++, row++) { genid = ctrlp->genid; if (genid == SWAMIGUI_PANEL_SF2_GEN_LABEL) { widg = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (widg), _(ctrlp->icon)); gtk_table_attach (GTK_TABLE (table), widg, 0, 5, row, row + 1, GTK_EXPAND | GTK_FILL, 0, 0, 0); continue; } /* generator icon */ btn = gtk_toggle_button_new (); genwidgets[ctrlndx].button = btn; image = gtk_image_new_from_stock (ctrlp->icon, GTK_ICON_SIZE_MENU); gtk_button_set_image (GTK_BUTTON (btn), image); gtk_table_attach (GTK_TABLE (table), btn, 0, 1, row, row+1, 0, 0, 0, 0); /* create the control for the icon value set toggle button */ swamigui_control_new_for_widget (G_OBJECT (btn)); /* do this after control creation (since it mucks with it) */ gtk_widget_set_sensitive (btn, FALSE); /* generator label */ widg = gtk_label_new (_(ipatch_sf2_gen_info[genid].label)); gtk_misc_set_alignment (GTK_MISC (widg), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), widg, 1, 2, row, row+1, GTK_FILL, 0, 2, 0); /* create the horizontal scale and spin button combo widget */ widg = swamigui_spin_scale_new (); genwidgets[ctrlndx].spinscale = widg; swamigui_spin_scale_set_order (SWAMIGUI_SPIN_SCALE (widg), TRUE); gtk_table_attach (GTK_TABLE (table), widg, 2, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); gtk_entry_set_width_chars (GTK_ENTRY (SWAMIGUI_SPIN_SCALE (widg)->spinbtn), 8); /* create control for scale/spin combo widget */ swamigui_control_new_for_widget (G_OBJECT (widg)); /* do this after control creation (since it mucks with it) */ gtk_widget_set_sensitive (widg, FALSE); /* units label */ widg = gtk_label_new (BLANK_UNIT_LABEL); genwidgets[ctrlndx].unitlabel = widg; gtk_misc_set_alignment (GTK_MISC (widg), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), widg, 4, 5, row, row+1, GTK_FILL, 0, 2, 0); ctrlndx++; } ctrlinfo = ctrlp + 1; if (ctrlp->genid == SWAMIGUI_PANEL_SF2_GEN_COLUMN) goto next_column; gtk_widget_show_all (hbox); gtk_container_border_width (GTK_CONTAINER (hbox), 4); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (genpanel), hbox); } /* the real selection set routine */ static gboolean swamigui_panel_sf2_gen_real_set_selection (SwamiguiPanelSF2Gen *genpanel, IpatchList *selection) { IpatchSF2GenItemIface *geniface; IpatchSF2GenArray *genarray; GenWidgets *genwidgets; int seltype = SEL_NONE; SwamiControl *widgctrl, *propctrl; IpatchUnitInfo *unitinfo, *unituser; GObject *item; char *labeltext; int i, genid, unit, ctrlndx; SwamiguiPanelSF2GenCtrlInfo *ctrlp; g_return_val_if_fail (SWAMIGUI_IS_PANEL_SF2_GEN (genpanel), FALSE); g_return_val_if_fail (!selection || IPATCH_IS_LIST (selection), FALSE); /* short circuit if selection is NULL and already is NULL in panel */ if (!genpanel->selection && (!selection || !selection->items)) return (FALSE); /* if selection with only one item and item has SF2 generator interface.. */ if (selection && selection->items && !selection->items->next && IPATCH_IS_SF2_GEN_ITEM (selection->items->data)) { /* get generator property type for item */ geniface = IPATCH_SF2_GEN_ITEM_GET_IFACE (selection->items->data); seltype = geniface->propstype & IPATCH_SF2_GEN_PROPS_MASK; } if (seltype == SEL_NONE) /* force selection to NULL if not valid */ { if (!genpanel->selection) return (FALSE); /* short circuit if already NULL */ selection = NULL; } if (selection == NULL) /* inactive selection? */ { genwidgets = (GenWidgets *)(genpanel->genwidgets); for (i = 0; i < genpanel->genwidget_count; i++, genwidgets++) { widgctrl = swamigui_control_lookup (G_OBJECT (genwidgets->button)); swami_control_disconnect_all (widgctrl); widgctrl = swamigui_control_lookup (G_OBJECT (genwidgets->spinscale)); swami_control_disconnect_all (widgctrl); gtk_widget_set_sensitive (genwidgets->button, FALSE); gtk_widget_set_sensitive (genwidgets->spinscale, FALSE); gtk_label_set_text (GTK_LABEL (genwidgets->unitlabel), BLANK_UNIT_LABEL); } } else /* selection is active */ { item = G_OBJECT (selection->items->data); /* allocate generator array and fill with all gens from item */ genarray = ipatch_sf2_gen_array_new (FALSE); /* ++ alloc */ ipatch_sf2_gen_item_copy_all (IPATCH_SF2_GEN_ITEM (item), genarray); genwidgets = (GenWidgets *)(genpanel->genwidgets); ctrlndx = 0; for (ctrlp = genpanel->ctrlinfo; ctrlp->genid != SWAMIGUI_PANEL_SF2_GEN_END; ctrlp++) { if (IS_OP (ctrlp->genid)) continue; genid = ctrlp->genid; widgctrl = swamigui_control_lookup (G_OBJECT (genwidgets->button)); swami_control_disconnect_all (widgctrl); propctrl = swami_get_control_prop (item, geniface->setspecs[genid]); // ++ ref swami_control_connect (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); g_object_unref (propctrl); // -- unref widgctrl = swamigui_control_lookup (G_OBJECT (genwidgets->spinscale)); swami_control_disconnect_all (widgctrl); propctrl = swami_get_control_prop (item, geniface->specs[genid]); // ++ ref swami_control_connect_transform (propctrl, widgctrl, SWAMI_CONTROL_CONN_BIDIR_SPEC_INIT, NULL, NULL, NULL, NULL, NULL, NULL); g_object_unref (propctrl); // -- unref ipatch_param_get (geniface->specs[genid], "unit-type", &unit, NULL); unitinfo = ipatch_unit_lookup (unit); if (unitinfo) unituser = ipatch_unit_class_lookup_map (IPATCH_UNIT_CLASS_USER, unitinfo->id); else unituser = NULL; swamigui_spin_scale_set_transform (SWAMIGUI_SPIN_SCALE (genwidgets->spinscale), unituser ? unit : IPATCH_UNIT_TYPE_NONE, unituser ? unituser->id : IPATCH_UNIT_TYPE_NONE); gtk_widget_set_sensitive (genwidgets->button, TRUE); gtk_widget_set_sensitive (genwidgets->spinscale, TRUE); if (unitinfo) { labeltext = unituser ? unituser->label : unitinfo->label; gtk_label_set_text (GTK_LABEL (genwidgets->unitlabel), labeltext); } ctrlndx++; genwidgets++; } } if (genpanel->selection) g_object_unref (genpanel->selection); if (selection) genpanel->selection = (IpatchList *)ipatch_list_duplicate (selection); else genpanel->selection = NULL; genpanel->seltype = seltype; return (TRUE); } swami-2.2.0/src/swamigui/SwamiguiPanelSF2Gen.h000066400000000000000000000063071361104770400211430ustar00rootroot00000000000000/* * SwamiguiCtrlSF2Gen.h - User interface generator control object header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_GEN_CTRL_H__ #define __SWAMIGUI_GEN_CTRL_H__ #include #include typedef struct _SwamiguiPanelSF2Gen SwamiguiPanelSF2Gen; typedef struct _SwamiguiPanelSF2GenClass SwamiguiPanelSF2GenClass; typedef struct _SwamiguiPanelSF2GenCtrlInfo SwamiguiPanelSF2GenCtrlInfo; #define SWAMIGUI_TYPE_PANEL_SF2_GEN (swamigui_panel_sf2_gen_get_type ()) #define SWAMIGUI_PANEL_SF2_GEN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN, \ SwamiguiPanelSF2Gen)) #define SWAMIGUI_PANEL_SF2_GEN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SF2_GEN, \ SwamiguiPanelSF2GenClass)) #define SWAMIGUI_IS_PANEL_SF2_GEN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN)) #define SWAMIGUI_IS_PANEL_SF2_GEN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PANEL_SF2_GEN)) struct _SwamiguiPanelSF2Gen { GtkScrolledWindow parent_instance; /* derived from GtkScrolledWindow */ IpatchList *selection; /* item selection */ int seltype; /* current selection type (see SelType in $.c) */ SwamiguiPanelSF2GenCtrlInfo *ctrlinfo; /* Array of control info */ gpointer genwidgets; /* GenWidget array (see GenWidget in $.c) */ int genwidget_count; /* Count of widgets in genwidgets array */ }; struct _SwamiguiPanelSF2GenClass { GtkScrolledWindowClass parent_class; }; /** * SwamiguiPanelSF2GenCtrlInfo: * @genid: Generator ID or value from #SwamiguiPanelSF2GenOp * @icon: Icon name or other string value (label text for example) */ struct _SwamiguiPanelSF2GenCtrlInfo { guint8 genid; char *icon; }; /** * SwamiguiPanelSF2GenOther: * @SWAMIGUI_PANEL_SF2_GEN_LABEL: A label * @SWAMIGUI_PANEL_SF2_GEN_COLUMN: Marks beginning of next column * @SWAMIGUI_PANEL_SF2_GEN_END: End of #SwamiguiPanelSF2GenCtrlInfo array * * Operator values stored in #SwamiguiPanelSF2GenCtrlInfo genid field. */ typedef enum { SWAMIGUI_PANEL_SF2_GEN_LABEL = 200, SWAMIGUI_PANEL_SF2_GEN_COLUMN, SWAMIGUI_PANEL_SF2_GEN_END /* End of list */ } SwamiguiPanelSF2GenOp; GType swamigui_panel_sf2_gen_get_type (void); GtkWidget *swamigui_panel_sf2_gen_new (void); void swamigui_panel_sf2_gen_set_controls (SwamiguiPanelSF2Gen *genpanel, SwamiguiPanelSF2GenCtrlInfo *ctrlinfo); #endif swami-2.2.0/src/swamigui/SwamiguiPanelSF2GenEnv.c000066400000000000000000000073511361104770400216070ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include "SwamiguiControl.h" #include "SwamiguiPanelSF2GenEnv.h" #include "SwamiguiPanel.h" #include "SwamiguiRoot.h" #include "SwamiguiSpinScale.h" #include "icons.h" #include "util.h" #include "i18n.h" SwamiguiPanelSF2GenCtrlInfo sf2_gen_env_ctrl_info[] = { { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Volume Envelope") }, { IPATCH_SF2_GEN_VOL_ENV_DELAY, SWAMIGUI_STOCK_VOLENV_DELAY }, { IPATCH_SF2_GEN_VOL_ENV_ATTACK, SWAMIGUI_STOCK_VOLENV_ATTACK }, { IPATCH_SF2_GEN_VOL_ENV_HOLD, SWAMIGUI_STOCK_VOLENV_HOLD }, { IPATCH_SF2_GEN_VOL_ENV_DECAY, SWAMIGUI_STOCK_VOLENV_DECAY }, { IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, SWAMIGUI_STOCK_VOLENV_SUSTAIN }, { IPATCH_SF2_GEN_VOL_ENV_RELEASE, SWAMIGUI_STOCK_VOLENV_RELEASE }, { IPATCH_SF2_GEN_ATTENUATION, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_COLUMN, NULL }, { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Modulation Envelope") }, { IPATCH_SF2_GEN_MOD_ENV_DELAY, SWAMIGUI_STOCK_MODENV_DELAY }, { IPATCH_SF2_GEN_MOD_ENV_ATTACK, SWAMIGUI_STOCK_MODENV_ATTACK }, { IPATCH_SF2_GEN_MOD_ENV_HOLD, SWAMIGUI_STOCK_MODENV_HOLD }, { IPATCH_SF2_GEN_MOD_ENV_DECAY, SWAMIGUI_STOCK_MODENV_DECAY }, { IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, SWAMIGUI_STOCK_MODENV_SUSTAIN }, { IPATCH_SF2_GEN_MOD_ENV_RELEASE, SWAMIGUI_STOCK_MODENV_RELEASE }, { IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_END, NULL } }; static void swamigui_panel_sf2_gen_env_panel_iface_init (SwamiguiPanelIface *panel_iface); G_DEFINE_TYPE_WITH_CODE (SwamiguiPanelSF2GenEnv, swamigui_panel_sf2_gen_env, SWAMIGUI_TYPE_PANEL_SF2_GEN, G_IMPLEMENT_INTERFACE (SWAMIGUI_TYPE_PANEL, swamigui_panel_sf2_gen_env_panel_iface_init)); static void swamigui_panel_sf2_gen_env_class_init (SwamiguiPanelSF2GenEnvClass *klass) { } static void swamigui_panel_sf2_gen_env_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("Envelopes"); panel_iface->blurb = _("Controls for SoundFont envelope parameters"); panel_iface->stockid = SWAMIGUI_STOCK_VOLENV; } static void swamigui_panel_sf2_gen_env_init (SwamiguiPanelSF2GenEnv *genpanel) { swamigui_panel_sf2_gen_set_controls (SWAMIGUI_PANEL_SF2_GEN (genpanel), sf2_gen_env_ctrl_info); } /** * swamigui_panel_sf2_gen_env_new: * * Create a new generator control panel object. * * Returns: new widget of type #SwamiguiPanelSF2GenEnv */ GtkWidget * swamigui_panel_sf2_gen_env_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_panel_sf2_gen_env_get_type ()))); } swami-2.2.0/src/swamigui/SwamiguiPanelSF2GenEnv.h000066400000000000000000000041701361104770400216100ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /** * SECTION: SwamiguiPanelSF2GenEnv * @short_description: SoundFont envelope generator control panel * @see_also: * @stability: */ #ifndef __SWAMIGUI_PANEL_SF2_GEN_ENV_H__ #define __SWAMIGUI_PANEL_SF2_GEN_ENV_H__ #include #include typedef struct _SwamiguiPanelSF2GenEnv SwamiguiPanelSF2GenEnv; typedef struct _SwamiguiPanelSF2GenEnvClass SwamiguiPanelSF2GenEnvClass; #include #define SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV (swamigui_panel_sf2_gen_env_get_type ()) #define SWAMIGUI_PANEL_SF2_GEN_ENV(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV, \ SwamiguiPanelSF2GenEnv)) #define SWAMIGUI_PANEL_SF2_GEN_ENV_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV, \ SwamiguiPanelSF2GenEnvClass)) #define SWAMIGUI_IS_PANEL_SF2_GEN_ENV(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV)) #define SWAMIGUI_IS_PANEL_SF2_GEN_ENV_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV)) struct _SwamiguiPanelSF2GenEnv { SwamiguiPanelSF2Gen parent_instance; }; struct _SwamiguiPanelSF2GenEnvClass { SwamiguiPanelSF2GenClass parent_class; }; GType swamigui_panel_sf2_gen_env_get_type (void); GtkWidget *swamigui_panel_sf2_gen_env_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiPanelSF2GenMisc.c000066400000000000000000000065561361104770400217600ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiguiPanelSF2GenMisc.h" #include "SwamiguiPanel.h" #include "icons.h" #include "util.h" #include "i18n.h" SwamiguiPanelSF2GenCtrlInfo sf2_gen_misc_ctrl_info[] = { { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Pitch") }, { IPATCH_SF2_GEN_COARSE_TUNE, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_SCALE_TUNE, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Effects") }, { IPATCH_SF2_GEN_FILTER_Q, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_FILTER_CUTOFF, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_REVERB, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_CHORUS, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_PAN, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_COLUMN, NULL }, { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Modulation LFO") }, { IPATCH_SF2_GEN_MOD_LFO_DELAY, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_MOD_LFO_FREQ, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_LABEL, N_("Vibrato LFO") }, { IPATCH_SF2_GEN_VIB_LFO_DELAY, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_VIB_LFO_FREQ, GTK_STOCK_CONNECT }, { IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, GTK_STOCK_CONNECT }, { SWAMIGUI_PANEL_SF2_GEN_END, NULL } }; static void swamigui_panel_sf2_gen_misc_panel_iface_init (SwamiguiPanelIface *panel_iface); G_DEFINE_TYPE_WITH_CODE (SwamiguiPanelSF2GenMisc, swamigui_panel_sf2_gen_misc, SWAMIGUI_TYPE_PANEL_SF2_GEN, G_IMPLEMENT_INTERFACE (SWAMIGUI_TYPE_PANEL, swamigui_panel_sf2_gen_misc_panel_iface_init)); static void swamigui_panel_sf2_gen_misc_class_init (SwamiguiPanelSF2GenMiscClass *klass) { } static void swamigui_panel_sf2_gen_misc_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("Misc. Controls"); panel_iface->blurb = _("Tuning, effects and oscillator controls"); panel_iface->stockid = SWAMIGUI_STOCK_EFFECT_CONTROL; } static void swamigui_panel_sf2_gen_misc_init (SwamiguiPanelSF2GenMisc *genpanel) { swamigui_panel_sf2_gen_set_controls (SWAMIGUI_PANEL_SF2_GEN (genpanel), sf2_gen_misc_ctrl_info); } /** * swamigui_panel_sf2_gen_misc_new: * * Create a new generator control panel object. * * Returns: new widget of type #SwamiguiPanelSF2GenMisc */ GtkWidget * swamigui_panel_sf2_gen_misc_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_panel_sf2_gen_misc_get_type ()))); } swami-2.2.0/src/swamigui/SwamiguiPanelSF2GenMisc.h000066400000000000000000000042171361104770400217550ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /** * SECTION: SwamiguiPanelSF2GenMisc * @short_description: SoundFont envelope generator control panel * @see_also: * @stability: */ #ifndef __SWAMIGUI_PANEL_SF2_GEN_MISC_H__ #define __SWAMIGUI_PANEL_SF2_GEN_MISC_H__ #include #include typedef struct _SwamiguiPanelSF2GenMisc SwamiguiPanelSF2GenMisc; typedef struct _SwamiguiPanelSF2GenMiscClass SwamiguiPanelSF2GenMiscClass; #include #define SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC (swamigui_panel_sf2_gen_misc_get_type ()) #define SWAMIGUI_PANEL_SF2_GEN_MISC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC, \ SwamiguiPanelSF2GenMisc)) #define SWAMIGUI_PANEL_SF2_GEN_MISC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC, \ SwamiguiPanelSF2GenMiscClass)) #define SWAMIGUI_IS_PANEL_SF2_GEN_MISC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC)) #define SWAMIGUI_IS_PANEL_SF2_GEN_MISC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC)) struct _SwamiguiPanelSF2GenMisc { SwamiguiPanelSF2Gen parent_instance; }; struct _SwamiguiPanelSF2GenMiscClass { SwamiguiPanelSF2GenClass parent_class; }; GType swamigui_panel_sf2_gen_misc_get_type (void); GtkWidget *swamigui_panel_sf2_gen_misc_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiPanelSelector.c000066400000000000000000000334661361104770400217000ustar00rootroot00000000000000/* * SwamiguiPanelSelector.c - Panel interface selection widget (notebook tabs) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiguiPanelSelector.h" #include "SwamiguiPanel.h" #include "i18n.h" enum { PROP_0, PROP_ITEM_SELECTION }; /* Stores information on a registered panel interface */ typedef struct { GType type; /* Panel type */ int order; /* Sort order for this panel type */ } PanelInfo; static void swamigui_panel_selector_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_panel_selector_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_panel_selector_finalize (GObject *object); static gboolean swamigui_panel_selector_button_press (GtkWidget *widget, GdkEventButton *event); static void swamigui_panel_selector_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num); static gboolean swamigui_panel_selector_real_set_selection (SwamiguiPanelSelector *selector, IpatchList *items); static gint sort_panel_info_by_order (gconstpointer a, gconstpointer b); static void swamigui_panel_selector_insert_panel (SwamiguiPanelSelector *selector, PanelInfo *info, int pos); G_DEFINE_TYPE (SwamiguiPanelSelector, swamigui_panel_selector, GTK_TYPE_NOTEBOOK); static GList *panel_list = NULL; /* list of registered panels (PanelInfo *) */ static guint panel_count = 0; /* count of items in panel_list */ #define swamigui_panel_selector_info_new() g_slice_new (PanelInfo) /** * swamigui_get_panel_selector_types: * * Get array of GType widgets which implement the #SwamiguiPanel interface and * have been registered with swamigui_register_panel_selector_type(). * * Returns: Array of GTypes (terminated with a 0 GType) which should be freed * when finished, can be %NULL if empty list. */ GType * swamigui_get_panel_selector_types (void) { GType *types = NULL; PanelInfo *info; GList *p; guint len, i; if (!panel_list) return (NULL); len = panel_count; /* atomic integer read, value increases only */ if (len == 0) return (NULL); types = g_new (GType, len + 1); /* ++ alloc */ /* copy types */ for (i = 0, p = panel_list; i < len; i++, p = p->next) { info = (PanelInfo *)(p->data); types[i] = info->type; } types[len] = 0; return (types); /* !! caller takes over allocation */ } /** * swamigui_register_panel_selector_type: * @panel_type: Type of widget with #SwamiguiPanel interface to register * @order: Order of the interface in relation to others (determines order of * notepad tabs, lower values are placed left of higher values) * * Register a panel interface for use in the panel selector notebook widget. */ void swamigui_register_panel_selector_type (GType panel_type, int order) { PanelInfo *info; g_return_if_fail (g_type_is_a (panel_type, SWAMIGUI_TYPE_PANEL)); info = swamigui_panel_selector_info_new (); info->type = panel_type; info->order = order; panel_list = g_list_append (panel_list, info); panel_count++; } static void swamigui_panel_selector_class_init (SwamiguiPanelSelectorClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); obj_class->set_property = swamigui_panel_selector_set_property; obj_class->get_property = swamigui_panel_selector_get_property; obj_class->finalize = swamigui_panel_selector_finalize; widget_class->button_press_event = swamigui_panel_selector_button_press; notebook_class->switch_page = swamigui_panel_selector_switch_page; g_object_class_install_property (obj_class, PROP_ITEM_SELECTION, g_param_spec_object ("item-selection", _("Item selection"), _("Item selection"), IPATCH_TYPE_LIST, G_PARAM_READWRITE)); } static void swamigui_panel_selector_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiPanelSelector *selector = SWAMIGUI_PANEL_SELECTOR (object); switch (property_id) { case PROP_ITEM_SELECTION: swamigui_panel_selector_real_set_selection (selector, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_panel_selector_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiPanelSelector *selector = SWAMIGUI_PANEL_SELECTOR (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, selector->selection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_panel_selector_finalize (GObject *object) { SwamiguiPanelSelector *selector = SWAMIGUI_PANEL_SELECTOR (object); g_list_free (selector->active_panels); if (selector->selection) g_object_unref (selector->selection); if (G_OBJECT_CLASS (swamigui_panel_selector_parent_class)->finalize) G_OBJECT_CLASS (swamigui_panel_selector_parent_class)->finalize (object); } static void swamigui_panel_selector_init (SwamiguiPanelSelector *selector) { } /* We focus the notebook widget so that it doesn't focus the first widget in the * child page which gets selected. Very annoying when attempts to play piano * keyboard after switching notebook tabs causes the first entry widget to get * changed. */ static gboolean swamigui_panel_selector_button_press (GtkWidget *widget, GdkEventButton *event) { gtk_widget_grab_focus (widget); if (GTK_WIDGET_CLASS (swamigui_panel_selector_parent_class)->button_press_event) return (GTK_WIDGET_CLASS (swamigui_panel_selector_parent_class)->button_press_event (widget, event)); else return (FALSE); } /* Switch page method which gets called when page is switched. * Sets the item selection of the panel for the new page. */ static void swamigui_panel_selector_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num) { SwamiguiPanelSelector *selector = SWAMIGUI_PANEL_SELECTOR (notebook); SwamiguiPanel *panel; GList *children; if (GTK_NOTEBOOK_CLASS (swamigui_panel_selector_parent_class)->switch_page) GTK_NOTEBOOK_CLASS (swamigui_panel_selector_parent_class)->switch_page (notebook, page, page_num); children = gtk_container_get_children (GTK_CONTAINER (notebook)); /* ++ alloc */ panel = (SwamiguiPanel *)g_list_nth_data (children, page_num); if (panel) g_object_set (panel, "item-selection", selector->selection, NULL); g_list_free (children); /* -- free */ } /** * swamigui_panel_selector_new: * * Create panel selector notebook widget. * * Returns: New panel selector widget. */ GtkWidget * swamigui_panel_selector_new (SwamiguiRoot *root) { GtkWidget *widg; widg = (GtkWidget *)g_object_new (SWAMIGUI_TYPE_PANEL_SELECTOR, NULL); ((SwamiguiPanelSelector *)widg)->root = root; return (widg); } /** * swamigui_panel_selector_set_selection: * @editor: Panel selector widget * @items: List of selected items or %NULL to unset selection * * Set the item selection of a panel selector widget. */ void swamigui_panel_selector_set_selection (SwamiguiPanelSelector *selector, IpatchList *items) { if (swamigui_panel_selector_real_set_selection (selector, items)) g_object_notify (G_OBJECT (selector), "item-selection"); } static gboolean swamigui_panel_selector_real_set_selection (SwamiguiPanelSelector *selector, IpatchList *selection) { GList *old_panels; GtkWidget *panel; GType *item_types; PanelInfo *info; GList *children; GList *p; guint i; g_return_val_if_fail (SWAMIGUI_IS_PANEL_SELECTOR (selector), FALSE); /* treat empty list as if NULL had been passed */ if (selection && !selection->items) selection = NULL; if (!selection && !selector->selection) return (FALSE); if (selector->selection) g_object_unref (selector->selection); if (selection) selector->selection = ipatch_list_duplicate (selection); else selector->selection = NULL; old_panels = selector->active_panels; selector->active_panels = NULL; /* ++ alloc list - get list of current notebook children */ children = gtk_container_get_children (GTK_CONTAINER (selector)); if (selection) { /* get unique item types in items list (for optimization purposes) */ item_types = swamigui_panel_get_types_in_selection (selection); /* ++ alloc */ /* loop over registered panel info */ for (i = 0, p = panel_list; i < panel_count; i++, p = p->next) { info = (PanelInfo *)(p->data); /* add panel types to list for those which selection is valid */ if (swamigui_panel_type_check_selection (info->type, selection, item_types)) selector->active_panels = g_list_prepend (selector->active_panels, p->data); } g_free (item_types); /* -- free */ /* sort the list of active panel types (by their order value) */ selector->active_panels = g_list_sort (selector->active_panels, sort_panel_info_by_order); /* Add new panels (if any), may get pulled from cache instead of created */ for (p = selector->active_panels, i = 0; p; p = p->next, i++) { info = (PanelInfo *)(p->data); /* panel not in old list - create a new panel or use cached one */ if (!g_list_find (old_panels, info)) swamigui_panel_selector_insert_panel (selector, info, i); } } /* Remove old unneeded panels and cache them */ for (p = old_panels, i = 0; p; p = p->next, i++) { if (!g_list_find (selector->active_panels, p->data)) { panel = g_list_nth_data (children, i); g_object_ref (panel); /* ++ ref for cache */ gtk_container_remove (GTK_CONTAINER (selector), GTK_WIDGET (panel)); g_object_set (panel, "item-selection", NULL, NULL); if (selector->root) selector->root->panel_cache = g_list_prepend (selector->root->panel_cache, panel); else g_object_unref (panel); } } /* update "item-selection" for currently selected page */ i = gtk_notebook_get_current_page (GTK_NOTEBOOK (selector)); if (i != -1) { panel = gtk_notebook_get_nth_page (GTK_NOTEBOOK (selector), i); g_object_set (panel, "item-selection", selector->selection, NULL); } g_list_free (children); /* -- free */ g_list_free (old_panels); /* -- free old panel info list */ return (TRUE); } /* GCompareFunc for sorting list items by panel 'order' values */ static gint sort_panel_info_by_order (gconstpointer a, gconstpointer b) { PanelInfo *ainfo = (PanelInfo *)a, *binfo = (PanelInfo *)b; return (ainfo->order - binfo->order); } /* create a new panel (or use existing identical panel from cache) and add to * panel selector notebook at a given position */ static void swamigui_panel_selector_insert_panel (SwamiguiPanelSelector *selector, PanelInfo *info, int pos) { GtkWidget *hbox, *widg; GtkWidget *panel = NULL; GtkWidget *cachepanel; char *label, *blurb, *stockid; PanelInfo *cacheinfo = NULL; GList *p; /* Check if there is already a panel of the requested type in the cache */ if (selector->root) { for (p = selector->root->panel_cache; p; p = p->next) { cachepanel = (GtkWidget *)(p->data); cacheinfo = g_object_get_data (G_OBJECT (cachepanel), "_SwamiguiPanelInfo"); if (cacheinfo == info) { panel = cachepanel; selector->root->panel_cache = g_list_delete_link (selector->root->panel_cache, p); break; } } } if (!panel) /* Not found in cache? - Create it and associate with info. */ { panel = GTK_WIDGET (g_object_new (info->type, NULL)); g_object_set_data (G_OBJECT (panel), "_SwamiguiPanelInfo", info); } gtk_widget_show (panel); hbox = gtk_hbox_new (FALSE, 0); swamigui_panel_type_get_info (info->type, &label, &blurb, &stockid); if (stockid) { widg = gtk_image_new_from_stock (stockid, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_box_pack_start (GTK_BOX (hbox), widg, FALSE, FALSE, 0); } if (label) { widg = gtk_label_new (label); gtk_box_pack_start (GTK_BOX (hbox), widg, FALSE, FALSE, 0); } if (blurb) gtk_widget_set_tooltip_text (GTK_WIDGET (hbox), blurb); gtk_widget_show_all (hbox); gtk_notebook_insert_page (GTK_NOTEBOOK (selector), panel, hbox, pos); /* -- unref the panel if it was taken from the cache */ if (cacheinfo == info) g_object_unref (panel); } /** * swamigui_panel_selector_get_selection: * @selector: Panel selector widget * * Get the list of selected items for a panel selector widget. * * Returns: New list containing selected items which has a ref count of one * which the caller owns or %NULL if no items selected. Remove the * reference when finished with it. */ IpatchList * swamigui_panel_selector_get_selection (SwamiguiPanelSelector *selector) { g_return_val_if_fail (SWAMIGUI_IS_PANEL_SELECTOR (selector), NULL); if (selector->selection && selector->selection->items) return (ipatch_list_duplicate (selector->selection)); else return (NULL); } swami-2.2.0/src/swamigui/SwamiguiPanelSelector.h000066400000000000000000000054511361104770400216760ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /** * SECTION: SwamiguiPanelSelector * @short_description: Panel user interface notebook selection widget * @see_also: #SwamiguiPanel * @stability: Stable * * Notebook widget which provides access to valid user interface panels for a * given item selection. */ #ifndef __SWAMIGUI_PANEL_SELECTOR_H__ #define __SWAMIGUI_PANEL_SELECTOR_H__ #include typedef struct _SwamiguiPanelSelector SwamiguiPanelSelector; typedef struct _SwamiguiPanelSelectorClass SwamiguiPanelSelectorClass; #include "SwamiguiRoot.h" #define SWAMIGUI_TYPE_PANEL_SELECTOR (swamigui_panel_selector_get_type ()) #define SWAMIGUI_PANEL_SELECTOR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PANEL_SELECTOR, \ SwamiguiPanelSelector)) #define SWAMIGUI_PANEL_SELECTOR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PANEL_SELECTOR, \ SwamiguiPanelSelectorClass)) #define SWAMIGUI_IS_PANEL_SELECTOR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PANEL_SELECTOR)) #define SWAMIGUI_IS_PANEL_SELECTOR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PANEL_SELECTOR)) /* Panel user interface selection object */ struct _SwamiguiPanelSelector { GtkNotebook parent; /* derived from GtkNotebook */ /*< private >*/ IpatchList *selection; /* Item selection */ /* Active panel for each page (SwamiguiPanelInfo *), list does not own allocation */ GList *active_panels; SwamiguiRoot *root; /* Root object containing panel cache */ }; /* Panel selector object class */ struct _SwamiguiPanelSelectorClass { GtkNotebookClass parent_class; }; GType *swamigui_get_panel_selector_types (void); void swamigui_register_panel_selector_type (GType panel_type, int order); GType swamigui_panel_selector_get_type (void); GtkWidget *swamigui_panel_selector_new (SwamiguiRoot *root); void swamigui_panel_selector_set_selection (SwamiguiPanelSelector *selector, IpatchList *items); IpatchList *swamigui_panel_selector_get_selection (SwamiguiPanelSelector *selector); #endif swami-2.2.0/src/swamigui/SwamiguiPaste.c000066400000000000000000000233041361104770400202020ustar00rootroot00000000000000/* * SwamiguiPaste.c - Swami item paste object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiPaste.h" #include "SwamiguiRoot.h" /* for state history */ #include "i18n.h" static void swamigui_paste_class_init (SwamiguiPasteClass *klass); static void swamigui_paste_init (SwamiguiPaste *paste); static void swamigui_paste_finalize (GObject *obj); static GObjectClass *parent_class = NULL; GType swamigui_paste_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiPasteClass), NULL, NULL, (GClassInitFunc) swamigui_paste_class_init, NULL, NULL, sizeof (SwamiguiPaste), 0, (GInstanceInitFunc) swamigui_paste_init }; obj_type = g_type_register_static (G_TYPE_OBJECT, "SwamiguiPaste", &obj_info, 0); } return (obj_type); } static void swamigui_paste_class_init (SwamiguiPasteClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swamigui_paste_finalize; } static void swamigui_paste_init (SwamiguiPaste *paste) { paste->status = SWAMIGUI_PASTE_NORMAL; paste->item_hash = g_hash_table_new_full (NULL, NULL, g_object_unref, /* key destroy func */ g_object_unref); /* value destroy func */ /* don't want paste objects to be saved for session */ swami_object_clear_flags (G_OBJECT (paste), SWAMI_OBJECT_SAVE); } static void swamigui_paste_finalize (GObject *obj) { SwamiguiPaste *paste = SWAMIGUI_PASTE (obj); if (paste->curitem) /* paste not finished? Then cancel it. */ { paste->status = SWAMIGUI_PASTE_CANCEL; swamigui_paste_process (paste); } if (paste->dstitem) g_object_unref (paste->dstitem); /* -- unref dest item */ if (paste->srcitems) /* -- unref source items */ { g_list_foreach (paste->srcitems, (GFunc)g_object_unref, NULL); g_list_free (paste->srcitems); } g_hash_table_destroy (paste->item_hash); /* clear conflict items to unref them */ swamigui_paste_set_conflict_items (paste, NULL, NULL); } /** * swamigui_paste_new: * * Create a new paste object. * * Returns: New paste object with a ref count of 1. */ SwamiguiPaste * swamigui_paste_new (void) { return (SWAMIGUI_PASTE (g_object_new (SWAMIGUI_TYPE_PASTE, NULL))); } /** * swamigui_paste_process: * @paste: Swami paste object * * Run the paste process. This function uses the IpatchPaste system to handle * paste operations. May need to be called multiple times to complete a paste * operation if decisions are required (conflict paste items, unhandled types, * or a cancel occurs). All paste status and parameters are stored in * the @paste object. This function also groups all state history actions * of the paste operation so it can be retracted if the paste is canceled. * * Returns: %TRUE if paste finished successfully, %FALSE if a decision is * required or an error occured. */ gboolean swamigui_paste_process (SwamiguiPaste *paste) { // IpatchItemIface *iface; gboolean retval = TRUE; g_return_val_if_fail (SWAMIGUI_IS_PASTE (paste), FALSE); g_return_val_if_fail (paste->dstitem != NULL, FALSE); g_return_val_if_fail (paste->srcitems != NULL, FALSE); g_return_val_if_fail (paste->status != SWAMIGUI_PASTE_ERROR, FALSE); /* skip unhandled types if requested */ if (paste->status == SWAMIGUI_PASTE_UNHANDLED && paste->decision == SWAMIGUI_PASTE_SKIP && paste->curitem) { paste->curitem = g_list_next (paste->curitem); paste->decision = SWAMIGUI_PASTE_NO_DECISION; } paste->status = SWAMIGUI_PASTE_NORMAL; /* set status to normal */ paste->decision_mask = SWAMIGUI_PASTE_SKIP | SWAMIGUI_PASTE_CHANGED | SWAMIGUI_PASTE_REPLACE; /* set decision mask to all */ // retval = ((*iface->paste)(paste->dstitem, paste)); paste->decision = SWAMIGUI_PASTE_NO_DECISION; /* clear previous decision */ return (retval); } /** * swamigui_paste_set_items: * @paste: Swami paste object * @dstitem: Destination item of paste operation * @srcitems: A list of source #IpatchItem objects * * A function to set the destination item and source item(s) of a * paste operation. This function can only be called once on a paste * object. */ void swamigui_paste_set_items (SwamiguiPaste *paste, IpatchItem *dstitem, GList *srcitems) { // IpatchItemIface *dstitem_iface; g_return_if_fail (SWAMIGUI_IS_PASTE (paste)); g_return_if_fail (IPATCH_IS_ITEM (dstitem)); g_return_if_fail (srcitems != NULL); g_return_if_fail (paste->dstitem == NULL); g_return_if_fail (paste->srcitems == NULL); /* make sure we have a paste method for the destination item */ // dstitem_iface = IPATCH_ITEM_GET_IFACE (dstitem); // g_return_if_fail (dstitem_iface->paste != NULL); g_object_ref (dstitem); /* ++ ref destination item */ paste->dstitem = dstitem; /* copy and reference source item list */ paste->srcitems = g_list_copy (srcitems); g_list_foreach (paste->srcitems, (GFunc)g_object_ref, NULL); /* ++ ref */ paste->curitem = paste->srcitems; /* set current item to first source item */ } /** * swamigui_paste_get_conflict_items: * @paste: Swami paste object * @src: Location to store a pointer to the source conflict * item (item's refcount is incremented) or %NULL * @dest: Location to store a pointer to the conflict destination * item (item's refcount is incremented) or %NULL * * Get conflict items from a @paste object (paste status should be * #SWAMIGUI_PASTE_CONFLICT or these will be %NULL). The returned * items' reference counts are incremented and the caller is * responsible for unrefing them with g_object_unref when finished using * them. */ void swamigui_paste_get_conflict_items (SwamiguiPaste *paste, IpatchItem **src, IpatchItem **dest) { g_return_if_fail (SWAMIGUI_IS_PASTE (paste)); if (src) { /* ++ ref returned src item */ if (paste->conflict_src) g_object_ref (paste->conflict_src); *src = paste->conflict_src; } if (dest) { /* ++ ref returned dest item */ if (paste->conflict_dst) g_object_ref (paste->conflict_dst); *dest = paste->conflict_dst; } } /** * swamigui_paste_set_conflict_items: * @paste: Swami paste object * @src: Source conflict item or %NULL * @dest: Destination conflict item or %NULL * * Set conflict items in a paste object. This function will cause the paste * object's status to be set to #SWAMIGUI_PASTE_CONFLICT if @src and * @dest are not %NULL, otherwise it will be set to * #SWAMIGUI_PASTE_NORMAL status and the conflict items will be cleared. */ void swamigui_paste_set_conflict_items (SwamiguiPaste *paste, IpatchItem *src, IpatchItem *dest) { g_return_if_fail (SWAMIGUI_IS_PASTE (paste)); g_return_if_fail (!src || IPATCH_IS_ITEM (src)); g_return_if_fail (!dest || IPATCH_IS_ITEM (dest)); /* unref old items if set */ if (paste->conflict_src) g_object_unref (paste->conflict_src); if (paste->conflict_dst) g_object_unref (paste->conflict_dst); if (src && dest) { paste->status = SWAMIGUI_PASTE_CONFLICT; g_object_ref (src); /* ++ ref new src conflict item */ g_object_ref (dest); /* ++ ref new dest conflict item */ } else paste->status = SWAMIGUI_PASTE_NORMAL; paste->conflict_src = src; paste->conflict_dst = dest; } /** * swamigui_paste_push_state: * @paste: Swami paste object * @state: #IpatchItem "paste" method defined state data * * This function is used by #IpatchItem interface "paste" methods to * store variable state data to be able to resume a paste operation (after * a decision is made on a conflict, etc). The @paste object stores a stack * of state data so a chain of functions can be resumed at a later time. * The methods are responsible for creating and destroying this state data * and the function chain will be resumed even on a cancel operation so this * can be accomplished properly. */ void swamigui_paste_push_state (SwamiguiPaste *paste, gpointer state) { g_return_if_fail (SWAMIGUI_IS_PASTE (paste)); g_return_if_fail (state != NULL); paste->states = g_list_prepend (paste->states, state); } /** * swamigui_paste_pop_state: * @paste: Swami paste object * * This function is used by #IpatchItem interface "paste" methods to * retrieve the next variable state data, stored by * swamigui_paste_push_state(), to be able to resume a paste * operation. The chain of functions storing state data should call * this function in the reverse order in which the states were * pushed. The state pointer is removed from the stack after this * call. * * Returns: Pointer to state data which was on the top of the stack. */ gpointer swamigui_paste_pop_state (SwamiguiPaste *paste) { gpointer state; g_return_val_if_fail (SWAMIGUI_IS_PASTE (paste), NULL); g_return_val_if_fail (paste->states != NULL, NULL); state = paste->states->data; paste->states = g_list_delete_link (paste->states, paste->states); return (state); } swami-2.2.0/src/swamigui/SwamiguiPaste.h000066400000000000000000000102221361104770400202020ustar00rootroot00000000000000/* * SwamiguiPaste.h - Header file for Swami item paste object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_PASTE_H__ #define __SWAMIGUI_PASTE_H__ /* FIXME - Put me somewhere useful * * Paste methods should iterate over the list of items and determine * if it can handle the source to destination paste operation and * update the status of the @paste context to #SWAMIGUI_PASTE_UNHANDLED * if it cannot. If handled, then the paste operation should be * carried out while checking for conflicts. If a conflict is * encountered the @paste context should be set to * #SWAMIGUI_PASTE_CONFLICT and the function should return. * swamigui_paste_save_state() can be used to save state data of each * function in the call chain to allow the operation to resume after a * decision is made. */ typedef struct _SwamiguiPaste SwamiguiPaste; typedef struct _SwamiguiPasteClass SwamiguiPasteClass; #define SWAMIGUI_TYPE_PASTE (swamigui_paste_get_type ()) #define SWAMIGUI_PASTE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PASTE, SwamiguiPaste)) #define SWAMIGUI_PASTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PASTE, SwamiguiPasteClass)) #define SWAMIGUI_IS_PASTE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PASTE)) #define SWAMIGUI_IS_PASTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PASTE)) /* Status of a patch item paste operation */ typedef enum { SWAMIGUI_PASTE_NORMAL, /* system normal */ SWAMIGUI_PASTE_ERROR, /* an error has occured */ SWAMIGUI_PASTE_UNHANDLED, /* unhandled paste types */ SWAMIGUI_PASTE_CONFLICT, /* a conflict occured, choice required */ SWAMIGUI_PASTE_CANCEL /* cancel paste operation */ } SwamiguiPasteStatus; typedef enum { SWAMIGUI_PASTE_NO_DECISION = 0, SWAMIGUI_PASTE_SKIP = 1 << 0, /* skip item (keep old conflict item) */ SWAMIGUI_PASTE_CHANGED = 1 << 1, /* item change (check for conflicts, etc) */ SWAMIGUI_PASTE_REPLACE = 1 << 2 /* replace conflict item */ } SwamiguiPasteDecision; /* Swami paste object */ struct _SwamiguiPaste { GObject parent_instance; /* derived from GObject */ SwamiguiPasteStatus status; /* current status of paste */ SwamiguiPasteDecision decision; /* decision value (set for conflicts) */ int decision_mask; /* a mask of allowable decisions for a conflict */ IpatchItem *dstitem; /* paste destination item */ GList *srcitems; /* source items (IpatchItem types) */ GList *curitem; /* current source item being processed */ GHashTable *item_hash; /* hash of item relations (choices) */ GList *states; /* state stack for methods (so we can resume operations) */ IpatchItem *conflict_src; /* source conflict item */ IpatchItem *conflict_dst; /* destination conflict item */ }; /* Swami paste class */ struct _SwamiguiPasteClass { GObjectClass parent_class; }; GType swamigui_paste_get_type (void); SwamiguiPaste *swamigui_paste_new (void); gboolean swamigui_paste_process (SwamiguiPaste *paste); void swamigui_paste_set_items (SwamiguiPaste *paste, IpatchItem *dstitem, GList *srcitems); void swamigui_paste_get_conflict_items (SwamiguiPaste *paste, IpatchItem **src, IpatchItem **dest); void swamigui_paste_set_conflict_items (SwamiguiPaste *paste, IpatchItem *src, IpatchItem *dest); void swamigui_paste_push_state (SwamiguiPaste *paste, gpointer state); gpointer swamigui_paste_pop_state (SwamiguiPaste *paste); #endif swami-2.2.0/src/swamigui/SwamiguiPiano.c000066400000000000000000001123061361104770400201750ustar00rootroot00000000000000/* * SwamiguiPiano.c - Piano canvas item * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiguiPiano.h" #include "SwamiguiRoot.h" #include "SwamiguiStatusbar.h" #include "i18n.h" #include "util.h" /* Piano keys: C C# D D# E F F# G G# A A# B */ enum { PROP_0, PROP_WIDTH_PIXELS, /* width in pixels */ PROP_HEIGHT_PIXELS, /* height in pixels */ PROP_KEY_COUNT, /* number of keys in piano */ PROP_START_OCTAVE, /* piano start octave (first key) */ PROP_LOWER_OCTAVE, /* lower keyboard octave */ PROP_UPPER_OCTAVE, /* upper keyboard octave */ PROP_LOWER_VELOCITY, /* lower keyboard velocity */ PROP_UPPER_VELOCITY, /* upper keyboard velocity */ PROP_MIDI_CONTROL, /* Piano MIDI control */ PROP_EXPRESS_CONTROL, /* Piano expression control */ PROP_MIDI_CHANNEL, /* MIDI channel to send events on */ PROP_BG_COLOR, /* color of border and in between white keys */ PROP_WHITE_KEY_COLOR, /* color of white keys */ PROP_BLACK_KEY_COLOR, /* color of black keys */ PROP_SHADOW_EDGE_COLOR, /* color of white key shadow edge */ PROP_WHITE_KEY_PLAY_COLOR, /* color of white key play highlight color */ PROP_BLACK_KEY_PLAY_COLOR /* color of black key play highlight color */ }; enum { NOTE_ON, NOTE_OFF, LAST_SIGNAL }; #define DEFAULT_BG_COLOR GNOME_CANVAS_COLOR (0, 0, 0) #define DEFAULT_WHITE_KEY_COLOR GNOME_CANVAS_COLOR (255, 255, 255) #define DEFAULT_BLACK_KEY_COLOR GNOME_CANVAS_COLOR (0, 0, 0) #define DEFAULT_SHADOW_EDGE_COLOR GNOME_CANVAS_COLOR (128, 128, 128) #define DEFAULT_WHITE_KEY_PLAY_COLOR GNOME_CANVAS_COLOR (169, 127, 255) #define DEFAULT_BLACK_KEY_PLAY_COLOR GNOME_CANVAS_COLOR (169, 127, 255) /* piano vertical line width to white key width scale */ #define PIANO_VLINE_TO_KEY_SCALE (1.0/5.0) /* piano horizontal line width to piano height scale */ #define PIANO_HLINE_TO_HEIGHT_SCALE (1.0/48.0) /* piano black key height to piano height */ #define PIANO_BLACK_TO_HEIGHT_SCALE (26.0/48.0) /* piano white key grey edge to piano height scale */ #define PIANO_GREY_TO_HEIGHT_SCALE (2.0/48.0) /* piano white key indicator width to key scale */ #define PIANO_WHITE_INDICATOR_WIDTH_SCALE (4.0/8.0) /* piano white key indicator height to piano height scale */ #define PIANO_WHITE_INDICATOR_HEIGHT_SCALE (2.0/48.0) /* piano white key velocity indicator range to height scale */ #define PIANO_WHITE_INDICATOR_RANGE_SCALE (18.0/48.0) /* piano white key velocity offset to height scale */ #define PIANO_WHITE_INDICATOR_OFS_SCALE (28.0/48.0) /* piano black key indicator width to key scale */ #define PIANO_BLACK_INDICATOR_WIDTH_SCALE (3.0/5.0) /* piano black key indicator height to piano height scale */ #define PIANO_BLACK_INDICATOR_HEIGHT_SCALE (3.0/48.0) /* piano black key velocity indicator range scale */ #define PIANO_BLACK_INDICATOR_RANGE_SCALE (22.0/28.0) /* piano black key velocity offset scale */ #define PIANO_BLACK_INDICATOR_OFS_SCALE (2.0/28.0) /* piano active black key shorten scale (to look pressed down) */ #define PIANO_BLACK_SHORTEN_SCALE (1.0/26.0) /* a structure used for each key of a piano */ typedef struct { GnomeCanvasItem *item; /* canvas item for keys (black and white) */ GnomeCanvasItem *indicator; /* indicator item (if set note is active) */ GnomeCanvasItem *shadow; /* bottom shadow edge for white keys */ } KeyInfo; static void swamigui_piano_class_init (SwamiguiPianoClass *klass); static void swamigui_piano_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_piano_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_piano_init (SwamiguiPiano *piano); static void swamigui_piano_finalize (GObject *object); static void swamigui_piano_midi_ctrl_callback (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_piano_item_realize (GnomeCanvasItem *item); static void swamigui_piano_draw (SwamiguiPiano *piano); static void swamigui_piano_destroy_keys (SwamiguiPiano *piano); static void swamigui_piano_update_key_colors (SwamiguiPiano *piano); static void swamigui_piano_draw_noteon (SwamiguiPiano *piano, int note, int velocity); static void swamigui_piano_draw_noteoff (SwamiguiPiano *piano, int note); static void swamigui_piano_update_mouse_note (SwamiguiPiano *piano, double x, double y); static gboolean swamigui_piano_cb_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data); static gboolean swamigui_piano_note_on_internal (SwamiguiPiano *piano, int note, int velocity); static gboolean swamigui_piano_note_off_internal (SwamiguiPiano *piano, int note, int velocity); static GObjectClass *parent_class = NULL; static guint piano_signals[LAST_SIGNAL] = {0}; GType swamigui_piano_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiPianoClass), NULL, NULL, (GClassInitFunc) swamigui_piano_class_init, NULL, NULL, sizeof (SwamiguiPiano), 0, (GInstanceInitFunc) swamigui_piano_init, }; obj_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "SwamiguiPiano", &obj_info, 0); } return (obj_type); } static void swamigui_piano_class_init (SwamiguiPianoClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GnomeCanvasItemClass *canvas_item_class = GNOME_CANVAS_ITEM_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_piano_set_property; obj_class->get_property = swamigui_piano_get_property; obj_class->finalize = swamigui_piano_finalize; canvas_item_class->realize = swamigui_piano_item_realize; piano_signals[NOTE_ON] = g_signal_new ("note-on", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwamiguiPianoClass, note_on), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); piano_signals[NOTE_OFF] = g_signal_new ("note-off", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwamiguiPianoClass, note_off), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_object_class_install_property (obj_class, PROP_WIDTH_PIXELS, g_param_spec_int ("width-pixels", _("Pixel Width"), _("Width in pixels"), 1, G_MAXINT, SWAMIGUI_PIANO_DEFAULT_WIDTH, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_HEIGHT_PIXELS, g_param_spec_int ("height-pixels", _("Pixel Height"), _("Height in pixels"), 1, G_MAXINT, SWAMIGUI_PIANO_DEFAULT_HEIGHT, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_KEY_COUNT, g_param_spec_int ("key-count", _("Key Count"), _("Size of piano in keys"), 1, 128, 128, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (obj_class, PROP_START_OCTAVE, g_param_spec_int ("start-octave", _("Start Octave"), _("Piano start octave (0 = MIDI note 0"), 0, 10, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOWER_OCTAVE, g_param_spec_int ("lower-octave", _("Lower Octave"), _("Lower keyboard start octave"), 0, 10, SWAMIGUI_PIANO_DEFAULT_LOWER_OCTAVE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_UPPER_OCTAVE, g_param_spec_int ("upper-octave", _("Upper Octave"), _("Upper keyboard start octave"), 0, 10, SWAMIGUI_PIANO_DEFAULT_UPPER_OCTAVE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOWER_VELOCITY, g_param_spec_int ("lower-velocity", _("Lower Velocity"), _("Lower keyboard velocity"), 0, 127, 127, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_UPPER_VELOCITY, g_param_spec_int ("upper-velocity", _("Upper Velocity"), _("Upper keyboard velocity"), 0, 127, 127, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MIDI_CONTROL, g_param_spec_object ("midi-control", _("MIDI Control"), _("Piano MIDI control"), SWAMI_TYPE_CONTROL, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_EXPRESS_CONTROL, g_param_spec_object ("expression-control", _("Expression Control"), _("Piano expression control (vertical mouse axis)"), SWAMI_TYPE_CONTROL, G_PARAM_READABLE)); g_object_class_install_property (obj_class, PROP_MIDI_CHANNEL, g_param_spec_int ("midi-channel", _("MIDI Channel"), _("MIDI channel to send events on"), 0, 15, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_BG_COLOR, ipatch_param_set (g_param_spec_uint ("bg-color", _("Background color"), _("Color of border and between white keys"), 0, G_MAXUINT, DEFAULT_BG_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_WHITE_KEY_COLOR, ipatch_param_set (g_param_spec_uint ("white-key-color", _("White key color"), _("White key color"), 0, G_MAXUINT, DEFAULT_WHITE_KEY_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_BLACK_KEY_COLOR, ipatch_param_set (g_param_spec_uint ("black-key-color", _("Black key color"), _("Black key color"), 0, G_MAXUINT, DEFAULT_BLACK_KEY_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_SHADOW_EDGE_COLOR, ipatch_param_set (g_param_spec_uint ("shadow-edge-color", _("Shadow edge color"), _("Bottom shadow edge color"), 0, G_MAXUINT, DEFAULT_SHADOW_EDGE_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_WHITE_KEY_PLAY_COLOR, ipatch_param_set (g_param_spec_uint ("white-key-play-color", _("White key player color"), _("Color of white key play highlight"), 0, G_MAXUINT, DEFAULT_WHITE_KEY_PLAY_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_BLACK_KEY_PLAY_COLOR, ipatch_param_set (g_param_spec_uint ("black-key-play-color", _("Black key play color"), _("Color of black key play highlight"), 0, G_MAXUINT, DEFAULT_BLACK_KEY_PLAY_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); } static void swamigui_piano_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiPiano *piano = SWAMIGUI_PIANO (object); switch (property_id) { case PROP_WIDTH_PIXELS: piano->width = g_value_get_int (value); piano->up2date = FALSE; swamigui_piano_draw (piano); break; case PROP_HEIGHT_PIXELS: piano->height = g_value_get_int (value); piano->up2date = FALSE; swamigui_piano_draw (piano); break; case PROP_KEY_COUNT: piano->key_count = g_value_get_int (value); swamigui_piano_destroy_keys (piano); swamigui_piano_draw (piano); break; case PROP_START_OCTAVE: piano->start_note = g_value_get_int (value) * 12; break; case PROP_LOWER_OCTAVE: piano->lower_octave = g_value_get_int (value); break; case PROP_UPPER_OCTAVE: piano->upper_octave = g_value_get_int (value); break; case PROP_LOWER_VELOCITY: piano->lower_velocity = g_value_get_int (value); break; case PROP_UPPER_VELOCITY: piano->upper_velocity = g_value_get_int (value); break; case PROP_MIDI_CHANNEL: piano->midi_chan = g_value_get_int (value); break; case PROP_BG_COLOR: piano->bg_color = g_value_get_uint (value); if (piano->bg) g_object_set (piano->bg, "fill-color", piano->bg_color, NULL); break; case PROP_WHITE_KEY_COLOR: piano->white_key_color = g_value_get_uint (value); swamigui_piano_update_key_colors (piano); break; case PROP_BLACK_KEY_COLOR: piano->black_key_color = g_value_get_uint (value); swamigui_piano_update_key_colors (piano); break; case PROP_SHADOW_EDGE_COLOR: piano->shadow_edge_color = g_value_get_uint (value); swamigui_piano_update_key_colors (piano); break; case PROP_WHITE_KEY_PLAY_COLOR: piano->white_key_play_color = g_value_get_uint (value); break; case PROP_BLACK_KEY_PLAY_COLOR: piano->black_key_play_color = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_piano_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiPiano *piano = SWAMIGUI_PIANO (object); switch (property_id) { case PROP_WIDTH_PIXELS: g_value_set_int (value, piano->width); break; case PROP_HEIGHT_PIXELS: g_value_set_int (value, piano->height); break; case PROP_KEY_COUNT: g_value_set_int (value, piano->key_count); break; case PROP_START_OCTAVE: g_value_set_int (value, piano->start_note / 12); break; case PROP_LOWER_OCTAVE: g_value_set_int (value, piano->lower_octave); break; case PROP_UPPER_OCTAVE: g_value_set_int (value, piano->upper_octave); break; case PROP_LOWER_VELOCITY: g_value_set_int (value, piano->lower_velocity); break; case PROP_UPPER_VELOCITY: g_value_set_int (value, piano->upper_velocity); break; case PROP_MIDI_CONTROL: g_value_set_object (value, piano->midi_ctrl); break; case PROP_EXPRESS_CONTROL: g_value_set_object (value, piano->express_ctrl); break; case PROP_MIDI_CHANNEL: g_value_set_int (value, piano->midi_chan); break; case PROP_BG_COLOR: g_value_set_uint (value, piano->bg_color); break; case PROP_WHITE_KEY_COLOR: g_value_set_uint (value, piano->white_key_color); break; case PROP_BLACK_KEY_COLOR: g_value_set_uint (value, piano->black_key_color); break; case PROP_SHADOW_EDGE_COLOR: g_value_set_uint (value, piano->shadow_edge_color); break; case PROP_WHITE_KEY_PLAY_COLOR: g_value_set_uint (value, piano->white_key_play_color); break; case PROP_BLACK_KEY_PLAY_COLOR: g_value_set_uint (value, piano->black_key_play_color); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_piano_init (SwamiguiPiano *piano) { int i; /* create MIDI control */ piano->midi_ctrl = SWAMI_CONTROL (swami_control_midi_new ()); swami_control_set_queue (piano->midi_ctrl, swamigui_root->ctrl_queue); swami_control_midi_set_callback (SWAMI_CONTROL_MIDI (piano->midi_ctrl), swamigui_piano_midi_ctrl_callback, piano); /* create expression control */ piano->express_ctrl = SWAMI_CONTROL (swami_control_value_new ()); swami_control_set_queue (piano->express_ctrl, swamigui_root->ctrl_queue); swami_control_set_spec (piano->express_ctrl, g_param_spec_int ("expression", "Expression", "Expression", 0, 127, 0, G_PARAM_READWRITE)); swami_control_value_alloc_value (SWAMI_CONTROL_VALUE (piano->express_ctrl)); piano->velocity = 127; /* default velocity */ piano->key_count = 128; /* FIXME: Shouldn't it get set on construction? */ piano->width = SWAMIGUI_PIANO_DEFAULT_WIDTH; piano->height = SWAMIGUI_PIANO_DEFAULT_HEIGHT; piano->key_info = NULL; piano->start_note = 0; piano->lower_octave = SWAMIGUI_PIANO_DEFAULT_LOWER_OCTAVE; piano->upper_octave = SWAMIGUI_PIANO_DEFAULT_UPPER_OCTAVE; piano->lower_velocity = 127; piano->upper_velocity = 127; piano->mouse_note = 128; /* disabled mouse key (> 127) */ piano->bg_color = DEFAULT_BG_COLOR; piano->white_key_color = DEFAULT_WHITE_KEY_COLOR; piano->black_key_color = DEFAULT_BLACK_KEY_COLOR; piano->shadow_edge_color = DEFAULT_SHADOW_EDGE_COLOR; piano->white_key_play_color = DEFAULT_WHITE_KEY_PLAY_COLOR; piano->black_key_play_color = DEFAULT_BLACK_KEY_PLAY_COLOR; /* get the white key count for the number of keys in the piano */ piano->white_count = piano->key_count / 12 * 7; /* 7 whites per oct */ i = piano->key_count % 12; /* calculate for octave remainder (if any) */ if (i < 6) piano->white_count += (i + 1) / 2; else piano->white_count += (i | 0x1) / 2 + 1; g_signal_connect (G_OBJECT (piano), "event", G_CALLBACK (swamigui_piano_cb_event), piano); } static void swamigui_piano_finalize (GObject *object) { SwamiguiPiano *piano = SWAMIGUI_PIANO (object); g_object_unref (piano->midi_ctrl); /* -- unref MIDI control */ g_object_unref (piano->express_ctrl); /* -- unref expression control */ g_free (piano->key_info); piano->key_info = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize)(object); } /* SwamiControlFunc set value func for MIDI control */ static void swamigui_piano_midi_ctrl_callback (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiPiano *piano = SWAMIGUI_PIANO (SWAMI_CONTROL_FUNC(control)->user_data); GArray *valarray = NULL; SwamiMidiEvent *midi; int i, count = 1; /* default for single values */ /* if its multiple values, fetch the value array */ if (G_VALUE_TYPE (value) == G_TYPE_ARRAY) { valarray = g_value_get_boxed (value); count = valarray->len; } i = 0; while (i < count) { if (valarray) value = &g_array_index (valarray, GValue, i); if (G_VALUE_TYPE (value) == SWAMI_TYPE_MIDI_EVENT && (midi = g_value_get_boxed (value))) { if (midi->channel == piano->midi_chan) { switch (midi->type) { case SWAMI_MIDI_NOTE_ON: swamigui_piano_note_on_internal (piano, midi->data.note.note, midi->data.note.velocity); break; case SWAMI_MIDI_NOTE_OFF: swamigui_piano_note_off_internal (piano, midi->data.note.note, midi->data.note.velocity); break; default: break; } } } i++; } } static void swamigui_piano_item_realize (GnomeCanvasItem *item) { SwamiguiPiano *piano = SWAMIGUI_PIANO (item); if (GNOME_CANVAS_ITEM_CLASS (parent_class)->realize) (*GNOME_CANVAS_ITEM_CLASS (parent_class)->realize) (item); swamigui_piano_draw (piano); } static void swamigui_piano_draw (SwamiguiPiano *piano) { GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (piano); KeyInfo *keyp; gboolean isblack; double x, x1, x2; int i, mod; int vlineh1, vlineh2; /* return if not added to a canvas yet */ if (!GNOME_CANVAS_ITEM (piano)->canvas) return; if (!piano->up2date) { /* convert pixel width and height to world values */ gnome_canvas_c2w (GNOME_CANVAS_ITEM (piano)->canvas, piano->width, piano->height, &piano->world_width, &piano->world_height); piano->key_width = piano->world_width / piano->key_count; piano->key_width_half = piano->key_width / 2; piano->vline_width = (int)(piano->key_width * PIANO_VLINE_TO_KEY_SCALE + 0.5); piano->hline_width = (int)(piano->height * PIANO_HLINE_TO_HEIGHT_SCALE + 0.5); /* black key dimensions */ piano->black_height = piano->world_height * PIANO_BLACK_TO_HEIGHT_SCALE; /* top of grey edge */ piano->shadow_top = piano->world_height - piano->hline_width - piano->world_height * PIANO_GREY_TO_HEIGHT_SCALE; /* black key velocity range and offset cache values */ piano->black_vel_ofs = piano->black_height * PIANO_BLACK_INDICATOR_OFS_SCALE; piano->black_vel_range = piano->black_height * PIANO_BLACK_INDICATOR_RANGE_SCALE; /* white key velocity range and offset cache values */ piano->white_vel_ofs = piano->world_height * PIANO_WHITE_INDICATOR_OFS_SCALE; piano->white_vel_range = piano->world_height * PIANO_WHITE_INDICATOR_RANGE_SCALE; piano->up2date = TRUE; } if (!piano->bg) /* one time canvas item creation */ piano->bg = /* black piano background (border & separators) */ gnome_canvas_item_new (group, GNOME_TYPE_CANVAS_RECT, "x1", (double)0.0, "y1", (double)0.0, "fill-color-rgba", piano->bg_color, "outline-color", NULL, NULL); /* create black and white keys if not already done */ if (!piano->key_info) { piano->key_info = g_new0 (KeyInfo, piano->key_count); for (i = 0, mod = 0, isblack = FALSE; i < piano->key_count; i++, mod++) { if (mod == 12) mod = 0; /* octave modulo */ keyp = &((KeyInfo *)(piano->key_info))[i]; /* create grey edge for white keys */ if (!isblack) { /* bottom grey edge of white keys */ keyp->shadow = gnome_canvas_item_new (group, GNOME_TYPE_CANVAS_RECT, "fill-color-rgba", piano->shadow_edge_color, "outline-color", NULL, NULL); } keyp->item = gnome_canvas_item_new (group, GNOME_TYPE_CANVAS_RECT, "fill-color-rgba", isblack ? piano->black_key_color : piano->white_key_color, NULL); /* lower white keys one step (so previous black key is above it) */ if (i > 0 && !isblack) { gnome_canvas_item_lower (keyp->item, 2); gnome_canvas_item_lower (keyp->shadow, 2); } if (mod != 4 && mod != 11) isblack ^= 1; } } gnome_canvas_item_set (piano->bg, /* set background size */ "x2", piano->world_width, "y2", piano->world_height, NULL); /* calculate halves of vline_width */ vlineh1 = (int)(piano->vline_width + 0.5); vlineh2 = vlineh1 / 2; vlineh1 -= vlineh2; for (i = 0, mod = 0, isblack = FALSE; i < piano->key_count; i++, mod++) { if (mod == 12) mod = 0; /* octave modulo */ keyp = &((KeyInfo *)(piano->key_info))[i]; x = i * piano->world_width / piano->key_count; if (isblack) /* key is black? */ { gnome_canvas_item_set (keyp->item, "x1", x, "x2", x + piano->key_width, "y1", piano->hline_width, "y2", piano->black_height, NULL); } else /* white key */ { if (mod == 0 || mod == 5) x1 = x; else x1 = x - piano->key_width_half; if (mod == 4 || mod == 11) x2 = x + piano->key_width; else if (i != piano->key_count - 1) x2 = x + piano->key_width + piano->key_width_half; else x2 = x + piano->key_width - 1.0; x1 += vlineh1; x2 -= vlineh2; gnome_canvas_item_set (keyp->item, "x1", x1, "x2", x2, "y1", piano->hline_width, "y2", piano->shadow_top, NULL); gnome_canvas_item_set (keyp->shadow, "x1", x1, "x2", x2, "y1", piano->shadow_top, "y2", piano->world_height - piano->hline_width, NULL); } if (mod != 4 && mod != 11) isblack ^= 1; } } /* called to destroy piano keys (used when key count changes for example) */ static void swamigui_piano_destroy_keys (SwamiguiPiano *piano) { KeyInfo *keyp; int i; if (!piano->key_info) return; for (i = 0; i < piano->key_count; i++) { keyp = &((KeyInfo *)(piano->key_info))[i]; if (keyp->item) gtk_object_destroy (GTK_OBJECT (keyp->item)); if (keyp->indicator) gtk_object_destroy (GTK_OBJECT (keyp->indicator)); if (keyp->shadow) gtk_object_destroy (GTK_OBJECT (keyp->shadow)); } g_free (piano->key_info); piano->key_info = NULL; } /* Update the color of keys. * keytype: 0 = white, 1 = black, 2 = grey edge */ static void swamigui_piano_update_key_colors (SwamiguiPiano *piano) { KeyInfo *keyp; gboolean isblack; int i, mod; for (i = 0, mod = 0, isblack = FALSE; i < piano->key_count; i++, mod++) { if (mod == 12) mod = 0; /* octave modulo */ keyp = &((KeyInfo *)(piano->key_info))[i]; if (!isblack) { if (keyp->item) g_object_set (keyp->item, "fill-color-rgba", piano->white_key_color, NULL); if (keyp->shadow) g_object_set (keyp->shadow, "fill-color-rgba", piano->shadow_edge_color, NULL); } else if (keyp->item) g_object_set (keyp->item, "fill-color-rgba", piano->black_key_color, NULL); if (mod != 4 && mod != 11) isblack ^= 1; } } static void swamigui_piano_draw_noteon (SwamiguiPiano *piano, int note, int velocity) { GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (piano); KeyInfo *key_info; double pos, w, vel; int note_ofs; gboolean black; /* get key info structure for note */ note_ofs = note - piano->start_note; key_info = &((KeyInfo *)(piano->key_info))[note_ofs]; pos = swamigui_piano_note_to_pos (piano, note, 0, TRUE, &black); if (!black) /* white key? */ { /* lengthen white key to make it look pressed down */ gnome_canvas_item_set (GNOME_CANVAS_ITEM (key_info->item), "y2", piano->world_height - piano->hline_width, NULL); /* calculate size and velocity position of white key indicator */ w = piano->key_width_half * PIANO_WHITE_INDICATOR_WIDTH_SCALE; vel = velocity * piano->white_vel_range / 127.0 + piano->white_vel_ofs; } else /* black key */ { /* shorten black key to make it look pressed down */ gnome_canvas_item_set (GNOME_CANVAS_ITEM (key_info->item), "y2", piano->black_height - piano->black_height * PIANO_BLACK_SHORTEN_SCALE, NULL); /* calculate size and velocity position of black key indicator */ w = piano->key_width_half * PIANO_BLACK_INDICATOR_WIDTH_SCALE; vel = velocity * piano->black_vel_range / 127.0 + piano->black_vel_ofs; } /* create the indicator */ key_info->indicator = gnome_canvas_item_new (group, GNOME_TYPE_CANVAS_RECT, "x1", pos - w, "y1", piano->hline_width, "x2", pos + w, "y2", vel, "fill-color-rgba", black ? piano->black_key_play_color : piano->white_key_play_color, "outline-color", NULL, NULL); } static void swamigui_piano_draw_noteoff (SwamiguiPiano *piano, int note) { KeyInfo *key_info; int note_ofs, mod; gboolean black; /* get key info structure for note */ note_ofs = note - piano->start_note; key_info = &((KeyInfo *)(piano->key_info))[note_ofs]; if (!key_info->indicator) return; /* destroy indicator */ gtk_object_destroy (GTK_OBJECT (key_info->indicator)); key_info->indicator = NULL; /* determine if key is black or white */ mod = note % 12; if (mod <= 4) black = mod & 1; else black = !(mod & 1); if (black) { /* reset black key back to normal length */ gnome_canvas_item_set (GNOME_CANVAS_ITEM (key_info->item), "y2", piano->black_height, NULL); } else /* reset white key back to normal length */ { gnome_canvas_item_set (GNOME_CANVAS_ITEM (key_info->item), "y2", piano->shadow_top, NULL); } } static void swamigui_piano_update_mouse_note (SwamiguiPiano *piano, double x, double y) { static guint8 last_note = -1; /* cache note number for statusbar */ KeyInfo *key_info; double indicator_y; int note, velocity = 127; char midiNote[5]; char *statusmsg; int *velp = NULL; gboolean isblack; if (x < 0.0) x = 0.0; else if (x > piano->world_width) x = piano->world_width; else velp = &velocity; if (y < 0.0 || y > piano->world_height) { y = 0.0; velp = NULL; } note = swamigui_piano_pos_to_note (piano, x, y, velp, &isblack); note = CLAMP (note, 0, 127); /* force clamp note */ key_info = &((KeyInfo *)(piano->key_info))[note]; if (isblack) indicator_y = piano->black_vel_ofs + (velocity * piano->black_vel_range / 127.0); else indicator_y = piano->white_vel_ofs + (velocity * piano->white_vel_range / 127.0); if (last_note != note) /* update statusbar note value */ { swami_util_midi_note_to_str (note, midiNote); statusmsg = g_strdup_printf (_("Note: %-3s (%d) Velocity: %d"), midiNote, note, velocity); swamigui_statusbar_msg_set_label (swamigui_root->statusbar, 0, "Global", statusmsg); g_free (statusmsg); } if (piano->mouse_note > 127) return; /* if mouse note play not active, we done */ if (note != piano->mouse_note) /* note changed? */ { swamigui_piano_note_off (piano, piano->mouse_note, 127); /* note off */ piano->mouse_note = note; swamigui_piano_note_on (piano, note, velocity); /* new note on */ } else /* same note, update indicator for velocity */ { gnome_canvas_item_set (key_info->indicator, "y2", indicator_y, NULL); } } static gboolean swamigui_piano_cb_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data) { SwamiguiPiano *piano = SWAMIGUI_PIANO (data); GdkEventButton *bevent; GdkEventMotion *mevent; int note, velocity; switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *)event; if (bevent->button != 1) break; note = swamigui_piano_pos_to_note (piano, bevent->x, bevent->y, &velocity, NULL); if (note < 0) break; /* click not on a note? */ piano->mouse_note = note; swamigui_piano_note_on (piano, note, velocity); gnome_canvas_item_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, bevent->time); return (TRUE); case GDK_BUTTON_RELEASE: if (piano->mouse_note > 127) break; /* no mouse selected note? */ note = piano->mouse_note; piano->mouse_note = 128; /* no more selected mouse note */ swamigui_piano_note_off (piano, note, 127); gnome_canvas_item_ungrab (item, event->button.time); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *)event; swamigui_piano_update_mouse_note (piano, mevent->x, mevent->y); break; case GDK_LEAVE_NOTIFY: swamigui_statusbar_msg_set_label (swamigui_root->statusbar, 0, "Global", NULL); break; default: break; } return (FALSE); } /** * swamigui_piano_note_on: * @piano: Piano object * @note: MIDI note number to turn on * @velocity: MIDI velocity number (-1 to use current keyboard velocity) * * Piano note on. Turns on note visually on piano as well as sends an event to * #SwamiControl objects connected to piano MIDI event control. */ void swamigui_piano_note_on (SwamiguiPiano *piano, int note, int velocity) { if (velocity == -1) velocity = piano->velocity; if (velocity == 0) swamigui_piano_note_off (piano, note, velocity); if (swamigui_piano_note_on_internal (piano, note, velocity)) { /* send a note on event */ swami_control_midi_transmit (SWAMI_CONTROL_MIDI (piano->midi_ctrl), SWAMI_MIDI_NOTE_ON, piano->midi_chan, note, velocity); } } /* internal function for visually displaying a note on */ static gboolean swamigui_piano_note_on_internal (SwamiguiPiano *piano, int note, int velocity) { KeyInfo *key_info; int start_note; g_return_val_if_fail (SWAMIGUI_IS_PIANO (piano), FALSE); g_return_val_if_fail (note >= 0 && note <= 127, FALSE); g_return_val_if_fail (velocity >= -1 && velocity <= 127, FALSE); start_note = piano->start_note; if (note < start_note || note >= start_note + piano->key_count) return (FALSE); key_info = &((KeyInfo *)(piano->key_info))[note - start_note]; if (key_info->indicator) return (FALSE); /* note already on? */ swamigui_piano_draw_noteon (piano, note, velocity); return (TRUE); } /** * swamigui_piano_note_off: * @piano: Piano object * @note: MIDI note number to turn off * @velocity: MIDI note off velocity * * Piano note off. Turns off note visually on piano as well as sends an event * to it's connected MIDI driver (if any). */ void swamigui_piano_note_off (SwamiguiPiano *piano, int note, int velocity) { if (swamigui_piano_note_off_internal (piano, note, velocity)) { /* send a note off event */ swami_control_midi_transmit (SWAMI_CONTROL_MIDI (piano->midi_ctrl), SWAMI_MIDI_NOTE_OFF, piano->midi_chan, note, velocity); } } /* updates drawing of piano */ static gboolean swamigui_piano_note_off_internal (SwamiguiPiano *piano, int note, int velocity) { KeyInfo *key_info; int start_note; g_return_val_if_fail (SWAMIGUI_IS_PIANO (piano), FALSE); g_return_val_if_fail (note >= 0 && note <= 127, FALSE); g_return_val_if_fail (velocity >= -1 && velocity <= 127, FALSE); start_note = piano->start_note; if (note < start_note || note >= start_note + piano->key_count) return (FALSE); key_info = &((KeyInfo *)(piano->key_info))[note - start_note]; if (!key_info->indicator) return (FALSE); /* note already off? */ swamigui_piano_draw_noteoff (piano, note); return (TRUE); } /** * swamigui_piano_pos_to_note: * @piano: Piano object * @x: X coordinate in canvas world units * @y: Y coordinate in canvas world units * @velocity: Location to store velocity or %NULL * @isblack: Location to store a boolean if key is black/white, or %NULL * * Get the MIDI note corresponding to a point on the piano. The velocity * relates to the vertical axis of the note. Positions towards the tip * of the key generate higher velocities. * * Returns: The MIDI note number or -1 if not a valid key. */ int swamigui_piano_pos_to_note (SwamiguiPiano *piano, double x, double y, int *velocity, gboolean *isblack) { gboolean black; gdouble keyofs; int note, mod; g_return_val_if_fail (SWAMIGUI_IS_PIANO (piano), -1); /* within piano bounds? */ if (x < 0.0 || x > piano->world_width || y < 0.0 || y > piano->world_height) return (-1); /* calculate note */ note = (int)(x / piano->key_width); if (note >= piano->key_count) note = piano->key_count - 1; mod = note % 12; black = (mod == 1 || mod == 3 || mod == 6 || mod == 8 || mod == 10); if (black && y > piano->black_height) /* select white key, if below black keys */ { keyofs = x - (note * piano->key_width); if (keyofs >= piano->key_width_half) { note++; if (note >= piano->key_count) note = piano->key_count - 1; } else note--; black = FALSE; } if (velocity) { if (black) { if (y < piano->black_vel_ofs) *velocity = 1; else if (y > piano->black_vel_ofs + piano->black_vel_range) *velocity = 127; else *velocity = (int)(1.0 + (y - piano->black_vel_ofs) / (piano->black_vel_range) * 126.0 + 0.5); } else /* white key */ { if (y < piano->white_vel_ofs) *velocity = 1; else if (y > piano->white_vel_ofs + piano->white_vel_range) *velocity = 127; else *velocity = (int)(1.0 + (y - piano->white_vel_ofs) / (piano->white_vel_range) * 126.0 + 0.5); } } if (isblack) *isblack = black; return (piano->start_note + note); } /** * swamigui_piano_note_to_pos (SwamiguiPiano *piano, int note, gboolean isblack) * @piano: Piano object * @note: MIDI note number to convert to canvas position * @edge: Which edge to get position of (-1 = left, 0 = center, 1 = right) * @realnote: If %TRUE then the coordinate is adjusted for the actual drawn key * rather than the active area (equal for all keys), when %FALSE is specified. * @isblack: Pointer to store a gboolean which is set if key is black, or %NULL * * Find the canvas X coordinate for a given MIDI note. * * Returns: X canvas coordinate of @note for the specified @edge. */ double swamigui_piano_note_to_pos (SwamiguiPiano *piano, int note, int edge, gboolean realnote, gboolean *isblack) { int noteofs; double pos; gboolean black; int mod; g_return_val_if_fail (SWAMIGUI_IS_PIANO (piano), 0.0); g_return_val_if_fail (note >= piano->start_note, 0.0); noteofs = note - piano->start_note; g_return_val_if_fail (noteofs < piano->key_count, 0.0); /* calculate position to note */ pos = noteofs * piano->world_width / piano->key_count; mod = note % 12; black = (mod == 1 || mod == 3 || mod == 6 || mod == 8 || mod == 10); if (!realnote || black) /* active area request or black key? */ { /* active area is equal for all keys and black keys are equal to active area */ if (edge == 0) pos += piano->key_width_half; else if (edge == 1) { pos += piano->key_width; if (noteofs == piano->key_count - 1) pos -= 1.0; } } else /* white key */ { if (edge == -1) { if (mod != 0 && mod != 5) pos -= piano->key_width_half; } else if (edge == 0) { if (mod == 0 || mod == 5) pos += (piano->key_width + piano->key_width_half) / 2.0; else if (mod == 4 || mod == 11 || noteofs == piano->key_count - 1) pos += piano->key_width_half / 2.0; else pos += piano->key_width_half; } else { if (mod == 0 || mod == 5) pos += piano->key_width + piano->key_width_half; else if (mod == 4 || mod == 11) pos += piano->key_width; else if (noteofs != piano->key_count - 1) pos += piano->key_width + piano->key_width_half; else pos += piano->key_width - 1.0; } } if (isblack) *isblack = black; return (pos); } swami-2.2.0/src/swamigui/SwamiguiPiano.h000066400000000000000000000076431361104770400202110ustar00rootroot00000000000000/* * SwamiguiPiano.h - Piano canvas item * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_PIANO_H__ #define __SWAMIGUI_PIANO_H__ #include #include #include typedef struct _SwamiguiPiano SwamiguiPiano; typedef struct _SwamiguiPianoClass SwamiguiPianoClass; #define SWAMIGUI_TYPE_PIANO (swamigui_piano_get_type ()) #define SWAMIGUI_PIANO(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_PIANO, SwamiguiPiano)) #define SWAMIGUI_PIANO_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PIANO, \ SwamiguiPianoClass)) #define SWAMIGUI_IS_PIANO(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_PIANO)) #define SWAMIGUI_IS_PIANO_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PIANO)) #define SWAMIGUI_PIANO_DEFAULT_WIDTH 640 /* default width in pixels */ #define SWAMIGUI_PIANO_DEFAULT_HEIGHT 48 /* default hight in pixels */ #define SWAMIGUI_PIANO_DEFAULT_LOWER_OCTAVE 3 /* default lower keys octave */ #define SWAMIGUI_PIANO_DEFAULT_UPPER_OCTAVE 4 /* default upper keys octave */ /* Swami Piano Object */ struct _SwamiguiPiano { GnomeCanvasGroup parent_instance; /* derived from GnomeCanvasGroup */ SwamiControl *midi_ctrl; /* MIDI control object */ SwamiControl *express_ctrl; /* expression control object */ int midi_chan; /* MIDI channel to send events on */ int velocity; /* default MIDI note on velocity to use */ int width, height; /* width and height in pixels */ gpointer key_info; /* array of KeyInfo structures (see SwamiguiPiano.c) */ GnomeCanvasItem *bg; /* black background (outline/separators) */ guint8 key_count; /* number of keys */ guint8 start_note; /* note piano starts on (always note C) */ guint8 lower_octave; /* lower computer keyboard octave # */ guint8 upper_octave; /* upper computer keyboard octave # */ guint8 lower_velocity; /* lower keyboard velocity */ guint8 upper_velocity; /* upper keyboard velocity */ guint8 mouse_note; /* mouse selected note >127 = none */ guint8 reserved; /* cached values */ int white_count; gboolean up2date; /* are variables below up to date? */ double world_width, world_height; double key_width, key_width_half; double shadow_top; double black_height; double vline_width, hline_width; double black_vel_ofs, black_vel_range; double white_vel_ofs, white_vel_range; guint32 bg_color; guint32 white_key_color; guint32 black_key_color; guint32 shadow_edge_color; guint32 white_key_play_color; guint32 black_key_play_color; }; struct _SwamiguiPianoClass { GnomeCanvasGroupClass parent_class; void (*note_on) (SwamiguiPiano *piano, guint keynum); void (*note_off) (SwamiguiPiano *piano, guint keynum); }; GType swamigui_piano_get_type (void); void swamigui_piano_note_on (SwamiguiPiano *piano, int note, int velocity); void swamigui_piano_note_off (SwamiguiPiano *piano, int note, int velocity); int swamigui_piano_pos_to_note (SwamiguiPiano *piano, double x, double y, int *velocity, gboolean *isblack); double swamigui_piano_note_to_pos (SwamiguiPiano *piano, int note, int edge, gboolean realnote, gboolean *isblack); #endif swami-2.2.0/src/swamigui/SwamiguiPref.c000066400000000000000000000574771361104770400200440ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiguiPref.h" #include "SwamiguiControl.h" #include "SwamiguiRoot.h" #include "util.h" #include "icons.h" #include "i18n.h" /* columns for preference sections list store */ enum { SECTIONS_COLUMN_ICON, SECTIONS_COLUMN_NAME, SECTIONS_COLUMN_COUNT }; /* columns for piano key binding list store */ enum { KEYBIND_COLUMN_NOTE, KEYBIND_COLUMN_KEY, KEYBIND_COLUMN_COUNT }; /* Action used for piano key binding capture */ typedef enum { BIND_MODE_INACTIVE, BIND_MODE_ADD, BIND_MODE_CHANGE } BindMode; /* Stores information on a registered preferences interface */ typedef struct { char *icon; /* Stock icon of preferences interface */ char *name; /* Name of the interface */ int order; /* Sort order for this preference interface */ SwamiguiPrefHandler handler; /* Handler to create the interface */ } PrefInfo; static gint sort_pref_info_by_order_and_name (gconstpointer a, gconstpointer b); static void swamigui_pref_cb_close_clicked (GtkWidget *button, gpointer user_data); static void swamigui_pref_section_list_change (GtkTreeSelection *selection, gpointer user_data); static GtkWidget *general_prefs_handler (void); static GtkWidget *audio_samples_prefs_handler (void); static GtkWidget *keyboard_prefs_handler (void); static void keybindings_update (GtkWidget *prefwidg, gboolean lower); static void keybindings_set_bind_mode (GtkWidget *prefwidg, BindMode mode); static void keybindings_lower_radio_toggled (GtkToggleButton *btn, gpointer user_data); static void keybindings_add_key_button_toggled (GtkToggleButton *btn, gpointer user_data); static void keybindings_change_key_button_toggled (GtkToggleButton *btn, gpointer user_data); static void keybindings_delete_key_button_clicked (GtkButton *btn, gpointer user_data); static void keybindings_reset_keys_button_clicked (GtkButton *btn, gpointer user_data); static void keybindings_reset_keys_response (GtkDialog *dialog, int response, gpointer user_data); static void keybindings_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static gboolean keybindings_key_press_event (GtkWidget *widg, GdkEventKey *event, gpointer user_data); static void keybindings_sync (GtkWidget *prefwidg); G_DEFINE_TYPE (SwamiguiPref, swamigui_pref, GTK_TYPE_DIALOG); static GList *pref_list = NULL; /* list of registered pref interfaces (PrefInfo *) */ #define swamigui_pref_info_new() g_slice_new (PrefInfo) static const char *note_names[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; /** * swamigui_register_pref_handler: * @name: Name of the preference interface (shown in list with icon) * @icon: Name of stock icon for interface (shown in list with name) * @order: Order of the interface in relation to others (the lower the number * the higher on the list, use #SWAMIGUI_PREF_ORDER_NAME to indicate that the * interface should be sorted by name - after other interfaces which specify * a specific value). * * Register a preferences interface which will become a part of the preferences * widget. */ void swamigui_register_pref_handler (const char *name, const char *icon, int order, SwamiguiPrefHandler handler) { PrefInfo *info; g_return_if_fail (name != NULL); g_return_if_fail (icon != NULL); g_return_if_fail (handler != NULL); info = swamigui_pref_info_new (); info->name = g_strdup (name); info->icon = g_strdup (icon); info->order = order; info->handler = handler; pref_list = g_list_insert_sorted (pref_list, info, sort_pref_info_by_order_and_name); } /* GCompareFunc for sorting list items by order value or name */ static gint sort_pref_info_by_order_and_name (gconstpointer a, gconstpointer b) { PrefInfo *ainfo = (PrefInfo *)a, *binfo = (PrefInfo *)b; if (ainfo->order != SWAMIGUI_PREF_ORDER_NAME && binfo->order != SWAMIGUI_PREF_ORDER_NAME) return (ainfo->order - binfo->order); if (ainfo->order != SWAMIGUI_PREF_ORDER_NAME && binfo->order == SWAMIGUI_PREF_ORDER_NAME) return (-1); if (ainfo->order == SWAMIGUI_PREF_ORDER_NAME && binfo->order != SWAMIGUI_PREF_ORDER_NAME) return (1); return (strcmp (ainfo->name, binfo->name)); } static void swamigui_pref_class_init (SwamiguiPrefClass *klass) { /* Add built in preference sections */ swamigui_register_pref_handler (_("General"), GTK_STOCK_PREFERENCES, 10, general_prefs_handler); swamigui_register_pref_handler (_("Audio Samples"), SWAMIGUI_STOCK_SAMPLE_VIEWER, 15, audio_samples_prefs_handler); /* FIXME - Install a custom keyboard icon */ swamigui_register_pref_handler (_("Keyboard Map"), GTK_STOCK_SELECT_FONT, 20, keyboard_prefs_handler); } static void swamigui_pref_init (SwamiguiPref *pref) { GtkWidget *prefwidg; GtkWidget *treeview; GtkWidget *widg; GtkWidget *btn; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkListStore *store; GtkTreeIter iter; PrefInfo *info; GList *p; gtk_window_set_title (GTK_WINDOW (pref), _("Preferences")); /* Create the preferences glade widget and pack it in the dialog */ prefwidg = swamigui_util_glade_create ("Preferences"); gtk_widget_show (prefwidg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pref)->vbox), prefwidg, TRUE, TRUE, 0); /* Add close button to button box */ btn = gtk_button_new_from_stock (GTK_STOCK_CLOSE); gtk_widget_show (btn); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (pref)->action_area), btn); g_signal_connect (btn, "clicked", G_CALLBACK (swamigui_pref_cb_close_clicked), pref); treeview = swamigui_util_glade_lookup (prefwidg, "TreeViewSections"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); g_signal_connect (selection, "changed", G_CALLBACK (swamigui_pref_section_list_change), pref); /* create the list store for the preference sections list and assign to tree view */ store = gtk_list_store_new (SECTIONS_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); /* add icon and name cell renderer columns to tree */ renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); column = gtk_tree_view_column_new_with_attributes ("icon", renderer, "stock-id", SECTIONS_COLUMN_ICON, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("name", renderer, "text", SECTIONS_COLUMN_NAME, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* each section is stored in a notebook page. Notebook tabs are invisible. */ pref->notebook = swamigui_util_glade_lookup (prefwidg, "NoteBookPanels"); /* add preferences sections to list and control interfaces to invisible notebook */ for (p = pref_list; p; p = p->next) { info = (PrefInfo *)(p->data); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, SECTIONS_COLUMN_ICON, info->icon, SECTIONS_COLUMN_NAME, info->name, -1); widg = info->handler (); gtk_notebook_append_page (GTK_NOTEBOOK (pref->notebook), widg, NULL); } /* Select the first item in the list */ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter); } static void swamigui_pref_cb_close_clicked (GtkWidget *button, gpointer user_data) { SwamiguiPref *pref = SWAMIGUI_PREF (user_data); gtk_widget_destroy (GTK_WIDGET (pref)); /* destroy dialog */ } /* callback for when preference section list selection changes */ static void swamigui_pref_section_list_change (GtkTreeSelection *selection, gpointer user_data) { SwamiguiPref *pref = SWAMIGUI_PREF (user_data); GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; gint *indices; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; path = gtk_tree_model_get_path (model, &iter); /* ++ alloc path */ indices = gtk_tree_path_get_indices (path); /* set the notebook to page corresponding to selected section index */ if (indices && indices[0] != -1) gtk_notebook_set_current_page (GTK_NOTEBOOK (pref->notebook), indices[0]); gtk_tree_path_free (path); /* -- free path */ } /** * swamigui_pref_new: * * Create preferences dialog widget. * * Returns: New preferences dialog widget. */ GtkWidget * swamigui_pref_new (void) { return ((GtkWidget *)g_object_new (SWAMIGUI_TYPE_PREF, NULL)); } /* General preferences widget handler */ static GtkWidget * general_prefs_handler (void) { GtkWidget *widg; widg = swamigui_util_glade_create ("GeneralPrefs"); swamigui_control_glade_prop_connect (widg, G_OBJECT (swamigui_root)); gtk_widget_show (widg); return (widg); } /* Audio Samples preferences widget handler */ static GtkWidget * audio_samples_prefs_handler (void) { GtkWidget *widg; widg = swamigui_util_glade_create ("SamplePrefs"); swamigui_control_glade_prop_connect (widg, G_OBJECT (swamigui_root)); gtk_widget_show (widg); return (widg); } /* Virtual keyboard mapping preferences handler */ static GtkWidget * keyboard_prefs_handler (void) { GtkWidget *prefwidg; GtkWidget *treeview; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkListStore *store; GtkWidget *widg; prefwidg = swamigui_util_glade_create ("VirtKeyboardPrefs"); treeview = swamigui_util_glade_lookup (prefwidg, "KeyTreeView"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); g_object_set_data (G_OBJECT (prefwidg), "selection", selection); // g_signal_connect (selection, "changed", // G_CALLBACK (keybindings_selection_changed), prefwidg); /* create the list store for the key bindings */ store = gtk_list_store_new (KEYBIND_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); g_object_set_data (G_OBJECT (prefwidg), "store", store); /* add note and key name cell renderer columns to tree */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Note", renderer, "text", KEYBIND_COLUMN_NOTE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Key binding", renderer, "text", KEYBIND_COLUMN_KEY, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); keybindings_update (prefwidg, TRUE); /* update key binding list to lower keys */ widg = swamigui_util_glade_lookup (prefwidg, "RadioLower"); g_signal_connect (widg, "toggled", G_CALLBACK (keybindings_lower_radio_toggled), prefwidg); widg = swamigui_util_glade_lookup (prefwidg, "BtnAddKey"); g_signal_connect (widg, "toggled", G_CALLBACK (keybindings_add_key_button_toggled), prefwidg); widg = swamigui_util_glade_lookup (prefwidg, "BtnChangeKey"); g_signal_connect (widg, "toggled", G_CALLBACK (keybindings_change_key_button_toggled), prefwidg); widg = swamigui_util_glade_lookup (prefwidg, "BtnDeleteKey"); g_signal_connect (widg, "clicked", G_CALLBACK (keybindings_delete_key_button_clicked), prefwidg); widg = swamigui_util_glade_lookup (prefwidg, "BtnResetKeys"); g_signal_connect (widg, "clicked", G_CALLBACK (keybindings_reset_keys_button_clicked), prefwidg); gtk_widget_show (prefwidg); return (prefwidg); } #if 0 /* callback when keybindings list selection changes */ static void keybindings_selection_changed (GtkTreeSelection *selection, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); GtkWidget *widg; #if 0 widg = swamigui_util_glade_lookup (prefwidg, "LabelKeyBind"); /* If key binding is active, de-activate it */ if (GTK_WIDGET_VISIBLE (widg)) { gtk_widget_hide (widg); g_signal_handlers_disconnect_by_func (prefwidg, G_CALLBACK (keybindings_key_press_event), prefwidg); } #endif /* Set change button sensitivity depending on if only one list item selected */ widg = swamigui_util_glade_lookup (prefwidg, "BtnChangeKey"); gtk_widget_set_sensitive (widg, gtk_tree_selection_count_selected_rows (selection) == 1); } #endif /* Update keybindings list to lower (lower == TRUE) or upper (lower == FALSE) */ static void keybindings_update (GtkWidget *prefwidg, gboolean lower) { char *propval; char **keys, **sptr; char notename[16]; GtkTreeIter iter; GtkListStore *store; int i; store = g_object_get_data (G_OBJECT (prefwidg), "store"); gtk_list_store_clear (store); g_object_get (swamigui_root, lower ? "piano-lower-keys" : "piano-upper-keys", &propval, NULL); /* ++ alloc string */ keys = g_strsplit (propval, ",", 0); /* ++ alloc (NULL terminated array of strings) */ g_free (propval); /* -- free string */ /* add preferences sections to list and control interfaces to invisible notebook */ for (sptr = keys, i = 0; *sptr; sptr++, i++) { gtk_list_store_append (store, &iter); sprintf (notename, "%s%d", note_names[i % 12], i / 12); gtk_list_store_set (store, &iter, KEYBIND_COLUMN_NOTE, notename, KEYBIND_COLUMN_KEY, *sptr, -1); } g_strfreev (keys); /* -- free array of strings */ } static void keybindings_set_bind_mode (GtkWidget *prefwidg, BindMode mode) { gboolean addbtn = FALSE, changebtn = FALSE; GtkWidget *widg; GtkWidget *lbl; /* Check if requested mode is already set */ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (prefwidg), "bind-mode")) == mode) return; lbl = swamigui_util_glade_lookup (prefwidg, "LabelKeyBind"); switch (mode) { case BIND_MODE_ADD: addbtn = TRUE; /* fall through */ case BIND_MODE_CHANGE: if (mode == BIND_MODE_CHANGE) changebtn = TRUE; gtk_widget_show (lbl); g_signal_connect (prefwidg, "key-press-event", G_CALLBACK (keybindings_key_press_event), prefwidg); break; case BIND_MODE_INACTIVE: gtk_widget_hide (lbl); g_signal_handlers_disconnect_by_func (prefwidg, G_CALLBACK (keybindings_key_press_event), prefwidg); break; } widg = swamigui_util_glade_lookup (prefwidg, "BtnAddKey"); g_signal_handlers_block_by_func (widg, keybindings_add_key_button_toggled, prefwidg); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widg), addbtn); g_signal_handlers_unblock_by_func (widg, keybindings_add_key_button_toggled, prefwidg); widg = swamigui_util_glade_lookup (prefwidg, "BtnChangeKey"); g_signal_handlers_block_by_func (widg, keybindings_change_key_button_toggled, prefwidg); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widg), changebtn); g_signal_handlers_unblock_by_func (widg, keybindings_change_key_button_toggled, prefwidg); /* indicate the current key bind mode */ g_object_set_data (G_OBJECT (prefwidg), "bind-mode", GUINT_TO_POINTER (mode)); } /* callback when Lower radio button is toggled */ static void keybindings_lower_radio_toggled (GtkToggleButton *btn, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); keybindings_update (prefwidg, gtk_toggle_button_get_active (btn)); } static void keybindings_add_key_button_toggled (GtkToggleButton *btn, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); keybindings_set_bind_mode (prefwidg, gtk_toggle_button_get_active (btn) ? BIND_MODE_ADD : BIND_MODE_INACTIVE); } static void keybindings_change_key_button_toggled (GtkToggleButton *btn, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); keybindings_set_bind_mode (prefwidg, gtk_toggle_button_get_active (btn) ? BIND_MODE_CHANGE : BIND_MODE_INACTIVE); } static void keybindings_delete_key_button_clicked (GtkButton *btn, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); GtkTreeSelection *selection; GtkListStore *store; GList *sel_iters = NULL, *p; GtkTreeIter *iter; /* de-activate bind mode, if any */ keybindings_set_bind_mode (prefwidg, BIND_MODE_INACTIVE); selection = g_object_get_data (G_OBJECT (prefwidg), "selection"); store = g_object_get_data (G_OBJECT (prefwidg), "store"); gtk_tree_selection_selected_foreach (selection, keybindings_selection_foreach, &sel_iters); /* ++ alloc */ for (p = sel_iters; p; p = p->next) { iter = (GtkTreeIter *)(p->data); gtk_list_store_remove (store, iter); gtk_tree_iter_free (iter); } g_list_free (sel_iters); /* -- free */ } /* Callback for when Reset keys button is clicked */ static void keybindings_reset_keys_button_clicked (GtkButton *btn, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); GtkWidget *dialog; /* de-activate bind mode, if any */ keybindings_set_bind_mode (prefwidg, BIND_MODE_INACTIVE); dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Reset all piano key bindings to defaults?")); g_signal_connect (GTK_DIALOG (dialog), "response", G_CALLBACK (keybindings_reset_keys_response), prefwidg); gtk_widget_show (dialog); } /* Callback for response to "Reset all keys?" dialog */ static void keybindings_reset_keys_response (GtkDialog *dialog, int response, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); GParamSpec *pspec; GValue value = { 0 }; GtkWidget *widg; if (response == GTK_RESPONSE_YES) /* Reset to defaults if Yes */ { pspec = g_object_class_find_property (g_type_class_peek (SWAMIGUI_TYPE_ROOT), "piano-lower-keys"); g_value_init (&value, G_TYPE_STRING); g_param_value_set_default (pspec, &value); g_object_set_property (G_OBJECT (swamigui_root), "piano-lower-keys", &value); g_value_reset (&value); pspec = g_object_class_find_property (g_type_class_peek (SWAMIGUI_TYPE_ROOT), "piano-upper-keys"); g_param_value_set_default (pspec, &value); g_object_set_property (G_OBJECT (swamigui_root), "piano-upper-keys", &value); /* Update keybindings list */ widg = swamigui_util_glade_lookup (prefwidg, "RadioLower"); keybindings_update (prefwidg, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widg))); } gtk_object_destroy (GTK_OBJECT (dialog)); } /* a tree selection foreach callback to remove selected modulators */ static void keybindings_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { GList **sel_iters = (GList **)data; GtkTreeIter *copy; copy = gtk_tree_iter_copy (iter); *sel_iters = g_list_append (*sel_iters, copy); } /* callback when a key is pressed (for key binding) */ static gboolean keybindings_key_press_event (GtkWidget *eventwidg, GdkEventKey *event, gpointer user_data) { GtkWidget *prefwidg = GTK_WIDGET (user_data); GtkTreeSelection *selection; GtkWidget *treeview; GtkListStore *store; GtkTreeIter iter; GtkTreePath *path; GList *list; guint mode; char *keyname; char notename[16]; int count; if (event->keyval == GDK_Escape) /* If escape, stop key binding mode */ { keybindings_set_bind_mode (prefwidg, BIND_MODE_INACTIVE); return (TRUE); /* Stop key propagation */ } store = g_object_get_data (G_OBJECT (prefwidg), "store"); selection = g_object_get_data (G_OBJECT (prefwidg), "selection"); treeview = swamigui_util_glade_lookup (prefwidg, "KeyTreeView"); mode = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (prefwidg), "bind-mode")); switch (mode) { case BIND_MODE_ADD: /* Add new keybinding to list */ keyname = gdk_keyval_name (event->keyval); count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); gtk_list_store_append (store, &iter); sprintf (notename, "%s%d", note_names[count % 12], count / 12); gtk_list_store_set (store, &iter, KEYBIND_COLUMN_NOTE, notename, KEYBIND_COLUMN_KEY, keyname, -1); /* select new item and scroll to it */ gtk_tree_selection_unselect_all (selection); gtk_tree_selection_select_iter (selection, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); /* ++ alloc */ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); /* -- free */ break; case BIND_MODE_CHANGE: keyname = gdk_keyval_name (event->keyval); list = gtk_tree_selection_get_selected_rows (selection, NULL); /* ++ alloc list */ /* if there is a selected item.. */ if (list && gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, (GtkTreePath *)(list->data))) { count = gtk_tree_path_get_indices ((GtkTreePath *)(list->data))[0]; sprintf (notename, "%s%d", note_names[count % 12], count / 12); gtk_list_store_set (store, &iter, KEYBIND_COLUMN_NOTE, notename, KEYBIND_COLUMN_KEY, keyname, -1); /* Select the next item in the list */ if (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)) { gtk_tree_selection_unselect_all (selection); gtk_tree_selection_select_iter (selection, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); /* ++ alloc */ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); /* -- free */ } /* No more items, de-activate change mode */ else keybindings_set_bind_mode (prefwidg, BIND_MODE_INACTIVE); } /* -- free list */ g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL); g_list_free (list); break; } /* Sync updated keys to SwamiguiRoot property */ keybindings_sync (prefwidg); return (TRUE); /* Stop key propagation */ } /* Synchronize key list to SwamiguiRoot property */ static void keybindings_sync (GtkWidget *prefwidg) { GtkTreeModel *model; GtkTreeIter iter; GString *string; gboolean lowkeys; GtkWidget *radio; char *keyname; model = GTK_TREE_MODEL (g_object_get_data (G_OBJECT (prefwidg), "store")); string = g_string_new (""); /* ++ alloc */ if (gtk_tree_model_get_iter_first (model, &iter)) { do { gtk_tree_model_get (model, &iter, KEYBIND_COLUMN_KEY, &keyname, /* ++ alloc */ -1); if (string->len != 0) g_string_append_c (string, ','); g_string_append (string, keyname); g_free (keyname); /* -- free key name */ } while (gtk_tree_model_iter_next (model, &iter)); } radio = swamigui_util_glade_lookup (prefwidg, "RadioLower"); lowkeys = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio)); g_object_set (swamigui_root, lowkeys ? "piano-lower-keys" : "piano-upper-keys", string->str, NULL); g_string_free (string, TRUE); /* -- free */ } swami-2.2.0/src/swamigui/SwamiguiPref.h000066400000000000000000000050561361104770400200330ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ /** * SECTION: SwamiguiPref * @short_description: Swami preferences widget and registration * @see_also: * @stability: Stable * * Swami GUI preferences widget and preference interface registration. */ #ifndef __SWAMIGUI_PREF_H__ #define __SWAMIGUI_PREF_H__ #include typedef struct _SwamiguiPref SwamiguiPref; typedef struct _SwamiguiPrefClass SwamiguiPrefClass; #define SWAMIGUI_TYPE_PREF (swamigui_pref_get_type ()) #define SWAMIGUI_PREF(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_PREF, SwamiguiPref)) #define SWAMIGUI_PREF_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PREF, SwamiguiPrefClass)) #define SWAMIGUI_IS_PREF(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_PREF)) #define SWAMIGUI_IS_PREF_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PREF)) /* Swami preferences widget */ struct _SwamiguiPref { GtkDialog parent; /*< private >*/ GtkWidget *notebook; /* invisible notebook with preference sections */ }; /* Swami preferences widget class */ struct _SwamiguiPrefClass { GtkDialogClass parent_class; }; /** * SwamiguiPrefHandler: * * Function prototype to create a GUI preference interface. * * Returns: The toplevel widget of the preference interface. */ typedef GtkWidget * (*SwamiguiPrefHandler)(void); /** * SWAMIGUI_PREF_ORDER_NAME: * * Value to use for order parameter of swamigui_register_pref_handler() to * sort by name. This should be used for plugins and other interfaces where * specific placement in the preferences list is not needed. */ #define SWAMIGUI_PREF_ORDER_NAME 0 void swamigui_register_pref_handler (const char *name, const char *icon, int order, SwamiguiPrefHandler handler); GType swamigui_pref_get_type (void); GtkWidget *swamigui_pref_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiProp.c000066400000000000000000000253631361104770400200550ustar00rootroot00000000000000/* * SwamiguiProp.c - GObject property GUI control object * For creating user interfaces for controlling GObject properties. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include "SwamiguiControl.h" #include "SwamiguiRoot.h" #include "SwamiguiProp.h" #include "SwamiguiPanel.h" #include "i18n.h" #include "util.h" enum { PROP_0, PROP_ITEM_SELECTION }; /* Hash value used for prop_registry */ typedef struct { char *widg_name; /* name of Glade widget or NULL if handler set */ SwamiguiPropHandler handler; /* handler function or NULL if widg_name set */ } PropInfo; /* Local Prototypes */ static PropInfo *prop_info_new (void); static void prop_info_free (gpointer data); static void swamigui_prop_class_init (SwamiguiPropClass *klass); static void swamigui_prop_panel_iface_init (SwamiguiPanelIface *panel_iface); static gboolean swamigui_prop_panel_iface_check_selection (IpatchList *selection, GType *selection_types); static void swamigui_prop_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_prop_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_prop_finalize (GObject *object); static void swamigui_prop_init (SwamiguiProp *prop); static gboolean swamigui_prop_real_set_selection (SwamiguiProp *prop, IpatchList *selection); static GObjectClass *parent_class = NULL; /* Property type registry hash (GType -> PropInfo) */ static GHashTable *prop_registry = NULL; /* function for creating a PropInfo structure */ static PropInfo * prop_info_new (void) { return (g_slice_new0 (PropInfo)); } /* function for freeing a PropInfo structure */ static void prop_info_free (gpointer data) { PropInfo *info = (PropInfo *)data; g_return_if_fail (data != NULL); g_free (info->widg_name); g_slice_free (PropInfo, data); } /** * swamigui_register_prop_glade_widg: * @objtype: Type of object which the interface will control * @name: Name of a Glade widget in the Swami Glade file * * Registers a Swami Glade widget as an interface control for a given @objtype. * The Glade widget should contain child widgets of the form "PROP::" * which will cause the given widget to control the property "" of * objects of the given type. Use swamigui_prop_register_handler() instead if * additional customization is needed to create an interface. */ void swamigui_register_prop_glade_widg (GType objtype, const char *name) { PropInfo *info; g_return_if_fail (objtype != 0); g_return_if_fail (name != NULL); info = prop_info_new (); info->widg_name = g_strdup (name); g_hash_table_insert (prop_registry, (gpointer)objtype, info); } /** * swamigui_register_prop_handler: * @objtype: Type of object which the interface will control * @handler: Handler function to create the interface * * Registers a handler function to create an interface control for a given * @objtype. The handler should create a widget (if its widg parameter is * %NULL), or use previously created widget, and connect its controls to the * properties of the passed in object. Use swamigui_prop_register_glade_widg() * instead if no special customization is needed beyond simply connecting * widgets to an object's properties. */ void swamigui_register_prop_handler (GType objtype, SwamiguiPropHandler handler) { PropInfo *info; g_return_if_fail (objtype != 0); g_return_if_fail (handler != NULL); info = prop_info_new (); info->handler = handler; g_hash_table_insert (prop_registry, (gpointer)objtype, info); } GType swamigui_prop_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiPropClass), NULL, NULL, (GClassInitFunc) swamigui_prop_class_init, NULL, NULL, sizeof (SwamiguiProp), 0, (GInstanceInitFunc) swamigui_prop_init, }; static const GInterfaceInfo panel_info = { (GInterfaceInitFunc)swamigui_prop_panel_iface_init, NULL, NULL }; obj_type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "SwamiguiProp", &obj_info, 0); g_type_add_interface_static (obj_type, SWAMIGUI_TYPE_PANEL, &panel_info); prop_registry = g_hash_table_new_full (NULL, NULL, NULL, prop_info_free); } return (obj_type); } static void swamigui_prop_class_init (SwamiguiPropClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_prop_set_property; obj_class->get_property = swamigui_prop_get_property; obj_class->finalize = swamigui_prop_finalize; g_object_class_override_property (obj_class, PROP_ITEM_SELECTION, "item-selection"); } static void swamigui_prop_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("Properties"); panel_iface->blurb = _("Edit general properties of items"); panel_iface->stockid = GTK_STOCK_PROPERTIES; panel_iface->check_selection = swamigui_prop_panel_iface_check_selection; } static gboolean swamigui_prop_panel_iface_check_selection (IpatchList *selection, GType *selection_types) { /* one item only and is a registered prop interface type */ return (!selection->items->next && g_hash_table_lookup (prop_registry, (gpointer)(*selection_types))); } static void swamigui_prop_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiProp *prop = SWAMIGUI_PROP (object); switch (property_id) { case PROP_ITEM_SELECTION: swamigui_prop_real_set_selection (prop, (IpatchList *)g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_prop_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiProp *prop = SWAMIGUI_PROP (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, prop->selection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_prop_finalize (GObject *object) { SwamiguiProp *prop = SWAMIGUI_PROP (object); if (prop->selection) g_object_unref (prop->selection); (*parent_class->finalize)(object); } static void swamigui_prop_init (SwamiguiProp *prop) { GtkObject *hadj, *vadj; gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (prop), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); hadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); vadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); g_object_set (prop, "hadjustment", hadj, "vadjustment", vadj, NULL); prop->viewport = gtk_viewport_new (GTK_ADJUSTMENT (hadj), GTK_ADJUSTMENT (vadj)); gtk_widget_show (prop->viewport); gtk_container_add (GTK_CONTAINER (prop), prop->viewport); prop->selection = NULL; } /** * swamigui_prop_new: * * Create a new Swami properties object * * Returns: Swami properties object */ GtkWidget * swamigui_prop_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_PROP, NULL))); } /** * swamigui_prop_set_selection: * @prop: Properties object * @select: Item selection to control or %NULL to unset. If list contains * multiple items, then selection will be unset. * * Set the object to control properties of. */ void swamigui_prop_set_selection (SwamiguiProp *prop, IpatchList *selection) { if (swamigui_prop_real_set_selection (prop, selection)) g_object_notify (G_OBJECT (prop), "item-selection"); } static gboolean swamigui_prop_real_set_selection (SwamiguiProp *prop, IpatchList *selection) { GtkWidget *widg = NULL; GObject *item = NULL, *olditem = NULL; gboolean same_type; PropInfo *info = NULL; g_return_val_if_fail (SWAMIGUI_IS_PROP (prop), FALSE); g_return_val_if_fail (!selection || IPATCH_IS_LIST (selection), FALSE); /* selection should be a single item, otherwise set it to NULL */ if (selection && (!selection->items || selection->items->next)) selection = NULL; if (selection) { item = (GObject *)(selection->items->data); g_return_val_if_fail (G_IS_OBJECT (item), FALSE); } if (prop->selection) olditem = G_OBJECT (prop->selection->items->data); /* same item is already set? */ if (item == olditem) return (FALSE); if (item) { info = g_hash_table_lookup (prop_registry, (gpointer)G_OBJECT_TYPE (item)); if (!info) /* No interface found for this object type? */ { if (!olditem) return (FALSE); /* already unset, return */ item = NULL; selection = NULL; } } /* selected item is of the same type? */ same_type = (item && olditem && G_OBJECT_TYPE (item) == G_OBJECT_TYPE (olditem)); /* destroy the old interface (if any) if not of the same type as the new item */ if (!same_type) gtk_container_foreach (GTK_CONTAINER (prop->viewport), (GtkCallback)gtk_object_destroy, NULL); if (item) /* setting item? */ { if (!same_type) /* create new interface for different type? */ { if (info->widg_name) /* Create by widget name? */ { widg = swamigui_util_glade_create (info->widg_name); swamigui_control_glade_prop_connect (widg, item); } else widg = info->handler (NULL, item); /* Create using handler */ gtk_widget_show (widg); gtk_container_add (GTK_CONTAINER (prop->viewport), widg); } else /* same type (re-using interface) */ { widg = gtk_bin_get_child (GTK_BIN (prop->viewport)); if (widg) { /* update the interface, by widget name method or handler method */ if (info->widg_name) swamigui_control_glade_prop_connect (widg, item); else info->handler (widg, item); } } if (!widg) selection = NULL; } if (prop->selection) g_object_unref (prop->selection); // -- unref old selection prop->selection = selection; if (prop->selection) g_object_ref (prop->selection); /* ++ reference selection */ return (TRUE); } swami-2.2.0/src/swamigui/SwamiguiProp.h000066400000000000000000000051131361104770400200510ustar00rootroot00000000000000/* * SwamiguiProp.h - GObject property GUI control object header * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_PROP_H__ #define __SWAMIGUI_PROP_H__ #include #include typedef struct _SwamiguiProp SwamiguiProp; typedef struct _SwamiguiPropClass SwamiguiPropClass; #define SWAMIGUI_TYPE_PROP (swamigui_prop_get_type ()) #define SWAMIGUI_PROP(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_PROP, SwamiguiProp)) #define SWAMIGUI_PROP_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PROP, SwamiguiPropClass)) #define SWAMIGUI_IS_PROP(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_PROP)) #define SWAMIGUI_IS_PROP_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PROP)) /* Swami Properties Object (all fields private) */ struct _SwamiguiProp { GtkScrolledWindow parent; GtkWidget *viewport; /* viewport for child interface widget */ IpatchList *selection; /* Selection list or NULL (one item only) */ }; /* Swami Properties Object class (all fields private) */ struct _SwamiguiPropClass { GtkScrolledWindowClass parent_class; }; /** * SwamiguiPropHandler: * @widg: A previously created widget (if changing @obj) or %NULL if a new * widget should be created. * @obj: Object to create a GUI property control interface for. * * Function prototype to create a GUI property control interface. * * Returns: The toplevel widget of the interface which controls @obj. */ typedef GtkWidget * (*SwamiguiPropHandler)(GtkWidget *widg, GObject *obj); void swamigui_register_prop_glade_widg (GType objtype, const char *name); void swamigui_register_prop_handler (GType objtype, SwamiguiPropHandler handler); GType swamigui_prop_get_type (void); GtkWidget *swamigui_prop_new (void); void swamigui_prop_set_selection (SwamiguiProp *prop, IpatchList *selection); #endif swami-2.2.0/src/swamigui/SwamiguiPythonView.c000066400000000000000000000235031361104770400212430ustar00rootroot00000000000000/* * SwamiguiPythonView.c - Python source view script editor and shell. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* FIXME - Hardcoded for now until... */ #define SCRIPT_PATH "/home/josh/.swami-1/scripts" #include #include #include #include #include #ifdef GTK_SOURCE_VIEW_SUPPORT #include #include #include #endif #include "SwamiguiPythonView.h" #include "swami_python.h" #include "util.h" #include "i18n.h" static void swamigui_python_view_refresh_scripts (void); static void swamigui_python_view_class_init (SwamiguiPythonViewClass *klass); static void swamigui_python_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_python_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_python_view_init (SwamiguiPythonView *pyview); static void swamigui_python_view_scripts_combo_changed (GtkComboBox *combo, gpointer user_data); static gboolean swamigui_python_view_cb_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data); static void swamigui_python_view_python_output_func (const char *output, gboolean is_stderr); static void swamigui_python_view_cb_execute_clicked (GtkButton *button, gpointer user_data); /* view to send Python output to */ static SwamiguiPythonView *output_view = NULL; static GtkListStore *scriptstore = NULL; /* list store for scripts */ /* refresh the list of python scripts */ static void swamigui_python_view_refresh_scripts (void) { const char *fname; GtkTreeIter iter; GDir *dir; if (scriptstore) gtk_list_store_clear (scriptstore); else scriptstore = gtk_list_store_new (1, G_TYPE_STRING); dir = g_dir_open (SCRIPT_PATH, 0, NULL); while ((fname = g_dir_read_name (dir))) { gtk_list_store_append (scriptstore, &iter); gtk_list_store_set (scriptstore, &iter, 0, fname, -1); } g_dir_close (dir); } GType swamigui_python_view_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiPythonViewClass), NULL, NULL, (GClassInitFunc) swamigui_python_view_class_init, NULL, NULL, sizeof (SwamiguiPythonView), 0, (GInstanceInitFunc) swamigui_python_view_init, }; obj_type = g_type_register_static (GTK_TYPE_VBOX, "SwamiguiPythonView", &obj_info, 0); } return (obj_type); } static void swamigui_python_view_class_init (SwamiguiPythonViewClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = swamigui_python_view_set_property; obj_class->get_property = swamigui_python_view_get_property; swamigui_python_view_refresh_scripts (); } static void swamigui_python_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { // SwamiguiPythonView *pyview = SWAMIGUI_PYTHON_VIEW (object); switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_python_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { // SwamiguiPythonView *pyview = SWAMIGUI_PYTHON_VIEW (object); switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_python_view_init (SwamiguiPythonView *pyview) { GtkWidget *frame; GtkWidget *btn; GtkCellRenderer *renderer; #ifdef GTK_SOURCE_VIEW_SUPPORT GtkSourceLanguagesManager *lm; GtkSourceLanguage *lang; #endif pyview->glade_widg = swamigui_util_glade_create ("PythonEditor"); gtk_container_add (GTK_CONTAINER (pyview), pyview->glade_widg); pyview->comboscripts = swamigui_util_glade_lookup (pyview->glade_widg, "ComboScripts"); gtk_combo_box_set_model (GTK_COMBO_BOX (pyview->comboscripts), GTK_TREE_MODEL (scriptstore)); gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (pyview->comboscripts), 0); g_signal_connect (pyview->comboscripts, "changed", G_CALLBACK (swamigui_python_view_scripts_combo_changed), pyview); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (pyview->comboscripts), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (pyview->comboscripts), renderer, "text", 0, NULL); frame = swamigui_util_glade_lookup (pyview->glade_widg, "ScrollScriptEditor"); #ifdef GTK_SOURCE_VIEW_SUPPORT lm = gtk_source_languages_manager_new (); lang = gtk_source_languages_manager_get_language_from_mime_type (lm, "text/x-python"); if (lang) pyview->srcbuf = GTK_TEXT_BUFFER (gtk_source_buffer_new_with_language (lang)); else pyview->srcbuf = GTK_TEXT_BUFFER (gtk_source_buffer_new (NULL)); gtk_source_buffer_set_highlight (GTK_SOURCE_BUFFER (pyview->srcbuf), TRUE); pyview->srcview = gtk_source_view_new_with_buffer (GTK_SOURCE_BUFFER (pyview->srcbuf)); #else pyview->srcbuf = gtk_text_buffer_new (NULL); pyview->srcview = gtk_text_view_new_with_buffer (pyview->srcbuf); #endif gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (pyview->srcview), GTK_WRAP_CHAR); g_signal_connect (pyview->srcview, "key-press-event", G_CALLBACK (swamigui_python_view_cb_key_press), pyview); gtk_widget_show (pyview->srcview); gtk_container_add (GTK_CONTAINER (frame), pyview->srcview); pyview->conbuf = gtk_text_buffer_new (NULL); pyview->conview = swamigui_util_glade_lookup (pyview->glade_widg, "TxtPyConsole"); gtk_text_view_set_buffer (GTK_TEXT_VIEW (pyview->conview), pyview->conbuf); btn = swamigui_util_glade_lookup (pyview->glade_widg, "BtnExecute"); g_signal_connect (btn, "clicked", G_CALLBACK (swamigui_python_view_cb_execute_clicked), pyview); } static void swamigui_python_view_scripts_combo_changed (GtkComboBox *combo, gpointer user_data) { SwamiguiPythonView *pyview = SWAMIGUI_PYTHON_VIEW (user_data); char *fname, *fullname, *buf; GtkTreeModel *model; GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (combo, &iter)) return; model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, 0, &fname, -1); fullname = g_strconcat (SCRIPT_PATH, G_DIR_SEPARATOR_S, fname, NULL); g_free (fname); if (!g_file_get_contents (fullname, &buf, NULL, NULL)) { g_free (fullname); return; } g_free (fullname); gtk_text_buffer_set_text (pyview->srcbuf, buf, -1); g_free (buf); } static gboolean swamigui_python_view_cb_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { SwamiguiPythonView *pyview = SWAMIGUI_PYTHON_VIEW (user_data); GtkWidget *btn; GtkTextMark *cursor; GtkTextIter start_iter, end_iter; char *cmd; if (event->keyval != GDK_Return) return (FALSE); /* return if not in "shell mode" (ENTER executes line) */ btn = swamigui_util_glade_lookup (pyview->glade_widg, "BtnShellMode"); if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (btn))) return (FALSE); /* get cursor position */ cursor = gtk_text_buffer_get_insert (pyview->srcbuf); /* get an iterator for the cursor (end of line) */ gtk_text_buffer_get_iter_at_mark (pyview->srcbuf, &end_iter, cursor); start_iter = end_iter; /* copy iterator */ /* set the iterator to the beginning of the line */ gtk_text_iter_set_line_offset (&start_iter, 0); cmd = gtk_text_buffer_get_text (pyview->srcbuf, &start_iter, &end_iter, FALSE); if (cmd && cmd[0]) /* some command to execute? */ { /* set current output view and Python log function */ output_view = pyview; swamigui_python_set_output_func (swamigui_python_view_python_output_func); PyRun_SimpleString (cmd); output_view = NULL; } g_free (cmd); return (FALSE); } static void swamigui_python_view_python_output_func (const char *output, gboolean is_stderr) { GtkTextIter iter; if (!output_view) return; gtk_text_buffer_get_end_iter (output_view->conbuf, &iter); gtk_text_buffer_insert (output_view->conbuf, &iter, output, -1); } /* Glade callback for Execute toolbar button */ static void swamigui_python_view_cb_execute_clicked (GtkButton *button, gpointer user_data) { SwamiguiPythonView *pyview = SWAMIGUI_PYTHON_VIEW (user_data); GtkTextIter start, end; char *script; gtk_text_buffer_get_bounds (pyview->srcbuf, &start, &end); script = gtk_text_buffer_get_text (pyview->srcbuf, &start, &end, FALSE); output_view = pyview; swamigui_python_set_output_func (swamigui_python_view_python_output_func); PyRun_SimpleString (script); output_view = NULL; g_free (script); } /** * swamigui_python_view_new: * * Create a new GUI python view shell widget. * * Returns: New swami GUI python view. */ GtkWidget * swamigui_python_view_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_PYTHON_VIEW, NULL))); } swami-2.2.0/src/swamigui/SwamiguiPythonView.h000066400000000000000000000044761361104770400212600ustar00rootroot00000000000000/* * SwamiguiPythonView.h - Header for python source viewer and shell. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_PYTHON_VIEW_H__ #define __SWAMIGUI_PYTHON_VIEW_H__ #include #include typedef struct _SwamiguiPythonView SwamiguiPythonView; typedef struct _SwamiguiPythonViewClass SwamiguiPythonViewClass; #define SWAMIGUI_TYPE_PYTHON_VIEW (swamigui_python_view_get_type ()) #define SWAMIGUI_PYTHON_VIEW(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_PYTHON_VIEW, SwamiguiPythonView)) #define SWAMIGUI_PYTHON_VIEW_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_PYTHON_VIEW, \ SwamiguiPythonViewClass)) #define SWAMIGUI_IS_PYTHON_VIEW(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_PYTHON_VIEW)) #define SWAMIGUI_IS_PYTHON_VIEW_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_PYTHON_VIEW)) /* Swami Python view/shell object */ struct _SwamiguiPythonView { GtkVBox parent_instance; GtkWidget *glade_widg; /* toplevel glade widget for python editor */ GtkTextBuffer *srcbuf; /* source editor buffer (GtkSourceBuffer or GtkTextBuffer depending on support libs) */ GtkWidget *srcview; /* source editor GtkSourceView */ GtkTextBuffer *conbuf; /* python output text buffer */ GtkWidget *conview; /* python console GtkTextBuffer */ GtkWidget *comboscripts; /* scripts combo box */ }; /* Swami Python view/shell class */ struct _SwamiguiPythonViewClass { GtkVBoxClass parent_class; }; GType swamigui_python_view_get_type (void); GtkWidget *swamigui_python_view_new (); #endif swami-2.2.0/src/swamigui/SwamiguiRoot.c000066400000000000000000001703311361104770400200540ustar00rootroot00000000000000/* * SwamiguiRoot.c - Swami main GUI object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef MAC_INTEGRATION #include #endif #include "swamigui.h" #include "i18n.h" #include "../libswami/swami_priv.h" enum { PROP_0, PROP_MAIN_WINDOW, PROP_UPDATE_INTERVAL, PROP_QUIT_CONFIRM, PROP_SPLASH_ENABLE, PROP_SPLASH_DELAY, PROP_TIPS_ENABLE, PROP_TIPS_POSITION, PROP_PIANO_LOWER_KEYS, PROP_PIANO_UPPER_KEYS, PROP_DEFAULT_PATCH_TYPE, PROP_MIDDLE_EMUL_ENABLE, PROP_MIDDLE_EMUL_MOD, PROP_TREE_STORE_LIST, PROP_SELECTION_ORIGIN, PROP_SELECTION, PROP_SELECTION_SINGLE, PROP_SOLO_ITEM_ENABLE }; enum { QUIT, LAST_SIGNAL }; /* Default splash delay in milliseconds */ #define SWAMIGUI_ROOT_DEFAULT_SPLASH_DELAY 5000 #define SWAMIGUI_ROOT_DEFAULT_LOWER_KEYS "z,s,x,d,c,v,g,b,h,n,j,m,comma,l,period,semicolon,slash" #define SWAMIGUI_ROOT_DEFAULT_UPPER_KEYS "q,2,w,3,e,r,5,t,6,y,7,u,i,9,o,0,p,bracketleft,equal,bracketright" /* external private global prototypes */ void _swamigui_ifaces_init (void); /* ifaces/ifaces.c */ void _swamigui_stock_icons_init (void); /* icons.c */ void _swamigui_control_init (void); /* SwamiguiControl.c */ void _swamigui_control_widgets_init (void); /* SwamiguiControl_widgets.c */ void _swamigui_item_menu_init (void); void _swamigui_item_menu_actions_init (void); /* SwamiguiItemMenu_actions.c */ #ifdef PYTHON_SUPPORT void _swamigui_python_init (int argc, char **argv); /* in swami_python.c */ #endif /* Local Prototypes */ static void swamigui_root_class_init (SwamiguiRootClass *klass); static void swamigui_root_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_root_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_root_quit_method (SwamiguiRoot *root); static void swamigui_root_init (SwamiguiRoot *obj); static void swamigui_root_finalize (GObject *object); static gboolean swamigui_queue_test_func (SwamiControlQueue *queue, SwamiControl *control, SwamiControlEvent *event); static gboolean swamigui_update_gui_timeout (gpointer data); static void ctrl_prop_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void ctrl_add_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void ctrl_remove_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_cb_quit_response (GtkDialog *dialog, int response, gpointer user_data); static void swamigui_real_quit (SwamiguiRoot *root); static void swamigui_root_create_main_window (SwamiguiRoot *root); static void swamigui_root_cb_solo_item (SwamiguiRoot *root, GParamSpec *pspec, gpointer user_data); static void swamigui_root_update_solo_item (SwamiguiRoot *root, GObject *solo_item); static void swamigui_root_connect_midi_keyboard_controls (SwamiguiRoot *root, GObject *midikey); static gint swamigui_root_cb_main_window_delete (GtkWidget *widget, GdkEvent *event, gpointer data); static guint *swamigui_root_parse_piano_keys (const char *str); static char *swamigui_root_encode_piano_keys (const guint *keyvals); static GtkWidget *sf2_prop_handler (GtkWidget *widg, GObject *obj); static void current_date_btn_clicked (GtkButton *button, gpointer user_data); static void cat_map_to_tree_store (GtkTreeStore *store, const IpatchSLIInstCatMapEntry *catmap, GtkTreeIter *parent); static void category_changed_cb (GtkComboBox *combo, gpointer user_data); static GtkWidget *sli_inst_prop_handler (GtkWidget *widg, GObject *obj); #ifdef MAC_INTEGRATION static void osx_accel_map_foreach_lcb(gpointer data, const gchar *accel_path, guint accel_key, GdkModifierType accel_mods, gboolean changed); static gboolean swamigui_root_cb_should_quit (GtkosxApplication *theApp, gpointer data); #endif static guint uiroot_signals[LAST_SIGNAL] = { 0 }; SwamiguiRoot *swamigui_root = NULL; SwamiRoot *swami_root = NULL; /* global enable bools, kind of hackish, but what is better? */ gboolean swamigui_disable_python = TRUE; gboolean swamigui_disable_plugins = FALSE; static GObjectClass *parent_class = NULL; /* used to determine if current thread is the GUI thread */ static GStaticPrivate is_gui_thread = G_STATIC_PRIVATE_INIT; /** * swamigui_init: * @argc: Number of arguments from main() * @argv: Command line arguments from main() * * Function to initialize Swami User Interface. Should be called before any * other Swamigui related functions. This function calls swami_init() as well. */ void swamigui_init (int *argc, char **argv[]) { static gboolean initialized = FALSE; if (initialized) return; initialized = TRUE; /* initialize glib thread support (if it hasn't been already!) */ if (!g_thread_supported ()) g_thread_init (NULL); gtk_set_locale (); gtk_init_check (argc, argv); /* initialize GTK */ /* set the application/program name, so things work right with recent file chooser * even when binary name is different (such as lt-swami) */ g_set_application_name ("swami"); #ifdef MAC_INTEGRATION /* Create singleton app instance at startup, later calls will return this */ GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL); theApp = NULL; /* we don't use it now, silence compiler warning */ #endif swami_init (); /* install icon parameter property (for assigning icons to effect controls) */ ipatch_param_install_property (g_param_spec_string ("icon", "Icon", "Icon", NULL, G_PARAM_READWRITE)); /* install icon property for types (for assigning icons to object types) */ ipatch_type_install_property (g_param_spec_string ("icon", "Icon", "Icon", NULL, G_PARAM_READWRITE)); /* set type specific icons */ ipatch_type_set (IPATCH_TYPE_DLS2, "icon", SWAMIGUI_STOCK_DLS, NULL); ipatch_type_set (IPATCH_TYPE_GIG, "icon", SWAMIGUI_STOCK_GIG, NULL); ipatch_type_set (IPATCH_TYPE_SLI, "icon", SWAMIGUI_STOCK_SLI, NULL); ipatch_type_set (IPATCH_TYPE_SF2, "icon", SWAMIGUI_STOCK_SOUNDFONT, NULL); swamigui_util_init (); _swamigui_stock_icons_init (); _swamigui_control_init (); _swamigui_control_widgets_init (); _swamigui_item_menu_init (); /* initialize Swamigui types */ swamigui_bar_get_type (); swamigui_bar_ptr_get_type (); swamigui_bar_ptr_type_get_type (); swamigui_control_adj_get_type (); swamigui_control_flags_get_type (); swamigui_control_midi_key_get_type (); swamigui_control_object_flags_get_type (); swamigui_control_rank_get_type (); swamigui_item_menu_flags_get_type (); swamigui_item_menu_get_type (); swamigui_knob_get_type (); swamigui_menu_get_type (); swamigui_mod_edit_get_type (); swamigui_panel_get_type (); swamigui_panel_selector_get_type (); swamigui_panel_sf2_gen_get_type (); swamigui_paste_decision_get_type (); swamigui_paste_get_type (); swamigui_paste_status_get_type (); swamigui_piano_get_type (); swamigui_pref_get_type (); swamigui_prop_get_type (); swamigui_quit_confirm_get_type (); swamigui_root_get_type (); swamigui_sample_canvas_get_type (); swamigui_sample_editor_get_type (); swamigui_sample_editor_marker_flags_get_type (); swamigui_sample_editor_marker_id_get_type (); swamigui_sample_editor_status_get_type (); swamigui_spectrum_canvas_get_type (); swamigui_spin_scale_get_type (); swamigui_splits_get_type (); swamigui_splits_mode_get_type (); swamigui_splits_status_get_type (); swamigui_statusbar_get_type (); swamigui_statusbar_pos_get_type (); swamigui_tree_get_type (); swamigui_tree_store_get_type (); swamigui_tree_store_patch_get_type (); swamigui_tree_store_config_get_type (); swamigui_util_unit_rgba_color_get_type (); _swamigui_item_menu_actions_init (); /* Register panel types and their order */ swamigui_register_panel_selector_type (SWAMIGUI_TYPE_PROP, 80); swamigui_register_panel_selector_type (SWAMIGUI_TYPE_SAMPLE_EDITOR, 90); swamigui_register_panel_selector_type (SWAMIGUI_TYPE_PANEL_SF2_GEN_MISC, 100); swamigui_register_panel_selector_type (SWAMIGUI_TYPE_PANEL_SF2_GEN_ENV, 105); swamigui_register_panel_selector_type (SWAMIGUI_TYPE_MOD_EDIT, 110); /* Register prop widget types */ swamigui_register_prop_handler (IPATCH_TYPE_SF2, sf2_prop_handler); swamigui_register_prop_glade_widg (IPATCH_TYPE_SF2_PRESET, "PropSF2Preset"); swamigui_register_prop_glade_widg (IPATCH_TYPE_SF2_INST, "PropSF2Inst"); swamigui_register_prop_glade_widg (IPATCH_TYPE_SF2_IZONE, "PropSF2IZone"); swamigui_register_prop_glade_widg (IPATCH_TYPE_SF2_SAMPLE, "PropSF2Sample"); swamigui_register_prop_handler (IPATCH_TYPE_SLI_INST, sli_inst_prop_handler); swamigui_register_prop_glade_widg (IPATCH_TYPE_SLI_ZONE, "PropSF2IZone"); swamigui_register_prop_glade_widg (IPATCH_TYPE_SLI_SAMPLE, "PropSF2Sample"); #ifdef PYTHON_SUPPORT if (!swamigui_disable_python) { _swamigui_python_init (*argc, *argv); swamigui_python_view_get_type (); } #endif if (!swamigui_disable_plugins) swami_plugin_load_all (); /* load plugins */ } /* Function used by libglade to initialize libswamigui widgets */ void glade_module_register_widgets (void) { swamigui_init (0, NULL); } GType swamigui_root_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (SwamiguiRootClass), NULL, NULL, (GClassInitFunc) swamigui_root_class_init, NULL, NULL, sizeof (SwamiguiRoot), 0, (GInstanceInitFunc) swamigui_root_init, }; item_type = g_type_register_static (SWAMI_TYPE_ROOT, "SwamiguiRoot", &item_info, 0); } return (item_type); } static void swamigui_root_class_init (SwamiguiRootClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_root_set_property; obj_class->get_property = swamigui_root_get_property; obj_class->finalize = swamigui_root_finalize; klass->quit = swamigui_root_quit_method; uiroot_signals[QUIT] = g_signal_new ("quit", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwamiguiRootClass, quit), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_object_class_install_property (obj_class, PROP_MAIN_WINDOW, g_param_spec_object ("main-window", _("Main window"), _("Main window"), GTK_TYPE_WIDGET, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, PROP_UPDATE_INTERVAL, g_param_spec_int ("update-interval", _("Update interval"), _("GUI update interval in milliseconds"), 10, 1000, 100, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_QUIT_CONFIRM, g_param_spec_enum ("quit-confirm", _("Quit confirm"), _("Quit confirmation method"), SWAMIGUI_TYPE_QUIT_CONFIRM, SWAMIGUI_QUIT_CONFIRM_UNSAVED, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SPLASH_ENABLE, g_param_spec_boolean ("splash-enable", _("Splash image enable"), _("Show splash on startup"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SPLASH_DELAY, g_param_spec_uint ("splash-delay", _("Splash delay"), _("Splash delay in milliseconds (0 to wait for button click)"), 0, G_MAXUINT, SWAMIGUI_ROOT_DEFAULT_SPLASH_DELAY, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TIPS_ENABLE, g_param_spec_boolean ("tips-enable", _("Tips enable"), _("Show tips on startup"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TIPS_POSITION, g_param_spec_int ("tips-position", "Tips position", "Tips position", 0, 255, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PIANO_LOWER_KEYS, g_param_spec_string ("piano-lower-keys", "Piano lower keys", "Comma delimited list of GDK key names", SWAMIGUI_ROOT_DEFAULT_LOWER_KEYS, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PIANO_UPPER_KEYS, g_param_spec_string ("piano-upper-keys", "Piano upper keys", "Comma delimited list of GDK key names", SWAMIGUI_ROOT_DEFAULT_UPPER_KEYS, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_DEFAULT_PATCH_TYPE, g_param_spec_gtype ("default-patch-type", "Default patch type", "Default patch type", IPATCH_TYPE_BASE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MIDDLE_EMUL_ENABLE, g_param_spec_boolean ("middle-emul-enable", _("Middle button emulation enable"), _("Enable middle mouse button emulation"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_MIDDLE_EMUL_MOD, g_param_spec_int ("middle-emul-mod", _("Middle button emulation modifier"), _("Middle mouse button emulation key modifier"), 0, 4, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_TREE_STORE_LIST, g_param_spec_object ("tree-store-list", "Tree store list", "List of tree stores", IPATCH_TYPE_LIST, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, PROP_SELECTION_ORIGIN, g_param_spec_object ("selection-origin", "Selection origin", "Origin of selection", G_TYPE_OBJECT, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, PROP_SELECTION, g_param_spec_object ("selection", "Item selection", "Last item selection", IPATCH_TYPE_LIST, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, PROP_SELECTION_SINGLE, g_param_spec_object ("selection-single", "Single item selection", "Last single selected item", G_TYPE_OBJECT, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property (obj_class, PROP_SOLO_ITEM_ENABLE, g_param_spec_boolean ("solo-item-enable", "Solo item enable", "Enable solo audition of active instrument", FALSE, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); } static void swamigui_root_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiRoot *root = SWAMIGUI_ROOT (object); IpatchList *list; GObject *obj = NULL; int ival; switch (property_id) { case PROP_UPDATE_INTERVAL: ival = g_value_get_int (value); if (ival != root->update_interval) { root->update_interval = ival; /* remove old timeout and install new one */ if (root->update_timeout_id) g_source_remove (root->update_timeout_id); root->update_timeout_id = g_timeout_add (root->update_interval, (GSourceFunc)swamigui_update_gui_timeout, root); } break; case PROP_QUIT_CONFIRM: root->quit_confirm = g_value_get_enum (value); break; case PROP_SPLASH_ENABLE: root->splash_enable = g_value_get_boolean (value); break; case PROP_SPLASH_DELAY: root->splash_delay = g_value_get_uint (value); break; case PROP_TIPS_ENABLE: root->tips_enable = g_value_get_boolean (value); break; case PROP_TIPS_POSITION: root->tips_position = g_value_get_int (value); break; case PROP_PIANO_LOWER_KEYS: g_free (root->piano_lower_keys); root->piano_lower_keys = swamigui_root_parse_piano_keys (g_value_get_string (value)); break; case PROP_PIANO_UPPER_KEYS: g_free (root->piano_upper_keys); root->piano_upper_keys = swamigui_root_parse_piano_keys (g_value_get_string (value)); break; case PROP_DEFAULT_PATCH_TYPE: root->default_patch_type = g_value_get_gtype (value); break; case PROP_MIDDLE_EMUL_ENABLE: root->middle_emul_enable = g_value_get_boolean (value); break; case PROP_MIDDLE_EMUL_MOD: root->middle_emul_mod = g_value_get_int (value); break; case PROP_TREE_STORE_LIST: if (root->tree_stores) g_object_unref (root->tree_stores); root->tree_stores = IPATCH_LIST (g_value_dup_object (value)); break; case PROP_SELECTION: if (root->selection) g_object_unref (root->selection); obj = g_value_dup_object (value); if (obj) root->selection = IPATCH_LIST (obj); else root->selection = NULL; g_object_notify (G_OBJECT (root), "selection-single"); break; case PROP_SELECTION_SINGLE: if (root->selection) g_object_unref (root->selection); obj = g_value_dup_object (value); if (obj) { list = ipatch_list_new (); list->items = g_list_append (list->items, obj); root->selection = list; } else root->selection = NULL; g_object_notify (G_OBJECT (root), "selection"); break; case PROP_SOLO_ITEM_ENABLE: if (root->solo_item_enabled != g_value_get_boolean (value)) { root->solo_item_enabled = !root->solo_item_enabled; if (root->solo_item_enabled && root->selection && root->selection->items && !root->selection->items->next) obj = root->selection->items->data; swamigui_root_update_solo_item (root, obj); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_root_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiRoot *root = SWAMIGUI_ROOT (object); GObject *obj = NULL; switch (property_id) { case PROP_MAIN_WINDOW: g_value_set_object (value, G_OBJECT (root->main_window)); break; case PROP_UPDATE_INTERVAL: g_value_set_int (value, root->update_interval); break; case PROP_QUIT_CONFIRM: g_value_set_enum (value, root->quit_confirm); break; case PROP_SPLASH_ENABLE: g_value_set_boolean (value, root->splash_enable); break; case PROP_SPLASH_DELAY: g_value_set_uint (value, root->splash_delay); break; case PROP_TIPS_ENABLE: g_value_set_boolean (value, root->tips_enable); break; case PROP_TIPS_POSITION: g_value_set_int (value, root->tips_position); break; case PROP_PIANO_LOWER_KEYS: g_value_take_string (value, swamigui_root_encode_piano_keys (root->piano_lower_keys)); break; case PROP_PIANO_UPPER_KEYS: g_value_take_string (value, swamigui_root_encode_piano_keys (root->piano_upper_keys)); break; case PROP_DEFAULT_PATCH_TYPE: g_value_set_gtype (value, root->default_patch_type); break; case PROP_MIDDLE_EMUL_ENABLE: g_value_set_boolean (value, root->middle_emul_enable); break; case PROP_MIDDLE_EMUL_MOD: g_value_set_int (value, root->middle_emul_mod); break; case PROP_TREE_STORE_LIST: g_value_set_object (value, root->tree_stores); break; case PROP_SELECTION_ORIGIN: if (root->selection) obj = swami_object_get_origin (G_OBJECT (root->selection)); g_value_take_object (value, obj); break; case PROP_SELECTION: g_value_set_object (value, root->selection); break; case PROP_SELECTION_SINGLE: if (root->selection && root->selection->items && !root->selection->items->next) obj = root->selection->items->data; g_value_set_object (value, obj); break; case PROP_SOLO_ITEM_ENABLE: g_value_set_boolean (value, root->solo_item_enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_root_quit_method (SwamiguiRoot *root) { swamigui_root_save_prefs (root); gtk_main_quit (); } /* Additional init operations are performed in swamigui_root_activate() */ static void swamigui_root_init (SwamiguiRoot *root) { IpatchList *list; GType type; /* having global vars for the Swami instance makes things easier */ swamigui_root = root; swami_root = SWAMI_ROOT (root); #ifdef PYTHON_SUPPORT if (!swamigui_disable_python) swamigui_python_set_root (); #endif root->update_interval = 40; root->quit_confirm = SWAMIGUI_QUIT_CONFIRM_UNSAVED; root->splash_enable = TRUE; root->splash_delay = SWAMIGUI_ROOT_DEFAULT_SPLASH_DELAY; root->tips_enable = TRUE; root->default_patch_type = IPATCH_TYPE_SF2; root->piano_lower_keys = swamigui_root_parse_piano_keys (SWAMIGUI_ROOT_DEFAULT_LOWER_KEYS); root->piano_upper_keys = swamigui_root_parse_piano_keys (SWAMIGUI_ROOT_DEFAULT_UPPER_KEYS); root->middle_emul_enable = TRUE; swami_object_set (G_OBJECT (root), "name", "Swami", "flags", SWAMI_OBJECT_SAVE | SWAMI_OBJECT_USER, NULL); /* create instrument tree store */ root->patch_store = SWAMIGUI_TREE_STORE (swamigui_tree_store_patch_new ()); /* ++ ref */ swami_object_set (G_OBJECT (root->patch_store), "name", "Patches", NULL); swami_root_add_object (SWAMI_ROOT (root), G_OBJECT (root->patch_store)); /* create config tree store */ root->config_store = swamigui_tree_store_config_new (); /* ++ ref */ swami_object_set (G_OBJECT (root->config_store), "name", "Config", NULL); swami_root_add_object (SWAMI_ROOT (root), G_OBJECT (root->config_store)); /* create GUI control queue */ root->ctrl_queue = swami_control_queue_new (); /* ++ ref control queue */ /* setup the GUI queue test function and is_gui_thread private */ g_static_private_set (&is_gui_thread, GUINT_TO_POINTER (TRUE), NULL); swami_control_queue_set_test_func (root->ctrl_queue, swamigui_queue_test_func); /* create queued patch item property changed listener */ root->ctrl_prop = swami_control_func_new (); /* ++ ref new control */ swami_control_func_assign_funcs (root->ctrl_prop, NULL /* get_func */, ctrl_prop_set_func, NULL /* destroy_func */, root); /* this control will never send (receives events only), disables event loop check */ SWAMI_CONTROL (root->ctrl_prop)->flags &= ~SWAMI_CONTROL_SENDS; /* create queued patch item add control listener */ root->ctrl_add = swami_control_func_new (); /* ++ ref new control */ swami_control_func_assign_funcs (root->ctrl_add, NULL /* get_func */, ctrl_add_set_func, NULL /* destroy_func */, root); /* this control will never send (receives events only), disables event loop check */ SWAMI_CONTROL (root->ctrl_add)->flags &= ~SWAMI_CONTROL_SENDS; /* create queued patch item remove control listener */ root->ctrl_remove = swami_control_func_new (); /* ++ ref new control */ swami_control_func_assign_funcs (root->ctrl_remove, NULL /* get_func */, ctrl_remove_set_func, NULL /* destroy_func */, root); /* this control will never send (receives events only), disables event loop check */ SWAMI_CONTROL (root->ctrl_remove)->flags &= ~SWAMI_CONTROL_SENDS; /* set the queue of the controls */ swami_control_set_queue (SWAMI_CONTROL (root->ctrl_prop), root->ctrl_queue); swami_control_set_queue (SWAMI_CONTROL (root->ctrl_add), root->ctrl_queue); swami_control_set_queue (SWAMI_CONTROL (root->ctrl_remove), root->ctrl_queue); /* connect controls to ipatch event senders */ swami_control_connect (swami_patch_get_prop_title_control(), SWAMI_CONTROL (root->ctrl_prop), 0); swami_control_connect (swami_patch_get_add_control(), SWAMI_CONTROL (root->ctrl_add), 0); swami_control_connect (swami_patch_get_remove_control(), SWAMI_CONTROL (root->ctrl_remove), 0); /* add patch store to tree store list */ list = ipatch_list_new (); list->items = g_list_append (list->items, g_object_ref (root->patch_store)); list->items = g_list_append (list->items, g_object_ref (root->config_store)); root->tree_stores = list; /* create the FluidSynth wavetable object */ type = swami_type_get_default (SWAMI_TYPE_WAVETBL); /* create new wavetable object */ if (type && (root->wavetbl = g_object_new (type, NULL))) { swami_object_set (root->wavetbl, "name", "FluidSynth1", NULL); swami_root_add_object (SWAMI_ROOT (root), G_OBJECT (root->wavetbl)); swamigui_tree_store_insert (root->config_store, G_OBJECT (root->wavetbl), NULL, NULL, NULL, 0, NULL); } } static void swamigui_root_finalize (GObject *object) { SwamiguiRoot *root = SWAMIGUI_ROOT (object); GList *p; g_object_unref (root->patch_store); g_object_unref (root->config_store); g_object_unref (root->tree_stores); if (root->selection) g_object_unref (root->selection); gtk_object_destroy (GTK_OBJECT (root->main_window)); if (root->wavetbl) { swami_wavetbl_close (root->wavetbl); g_object_unref (root->wavetbl); } if (root->solo_item) g_object_unref (root->solo_item); g_object_unref (root->ctrl_queue); if (root->update_timeout_id) g_source_remove (root->update_timeout_id); g_object_unref (root->ctrl_prop); g_object_unref (root->ctrl_add); g_object_unref (root->ctrl_remove); g_free (root->piano_lower_keys); g_free (root->piano_upper_keys); if (root->loaded_xml_config) ipatch_xml_destroy (root->loaded_xml_config); // Free widgets in panel cache for (p = root->panel_cache; p; p = g_list_delete_link (p, p)) g_object_unref (p->data); if (parent_class->finalize) parent_class->finalize (object); } /* GUI queue test function (checks if queuing of events is required) */ static gboolean swamigui_queue_test_func (SwamiControlQueue *queue, SwamiControl *control, SwamiControlEvent *event) { /* are we in the GUI thread? */ gboolean bval = GPOINTER_TO_UINT (g_static_private_get (&is_gui_thread)); return (!bval); /* FALSE sends event immediately, TRUE queues */ } /* routine called at regular intervals to synchronize GUI with patch objects */ static gboolean swamigui_update_gui_timeout (gpointer data) { SwamiguiRoot *root = SWAMIGUI_ROOT (data); swami_control_queue_run (root->ctrl_queue); /* Update splits widget if its splits-item has changed */ if (root->splits_changed) { swamigui_splits_item_changed (SWAMIGUI_SPLITS (root->splits)); root->splits_changed = FALSE; } return (TRUE); } /* patch item title property change control value set function (listens for item property change events) */ static void ctrl_prop_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiRoot *root = SWAMIGUI_ROOT (SWAMI_CONTROL_FUNC_DATA (control)); SwamiEventPropChange *prop_change; prop_change = g_value_get_boxed (&event->value); swamigui_tree_store_changed (root->patch_store, prop_change->object); } /* patch item add control value set function (listens for item add events) */ static void ctrl_add_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiRoot *root = SWAMIGUI_ROOT (SWAMI_CONTROL_FUNC_DATA (control)); IpatchItem *item, *splits_item; item = IPATCH_ITEM (g_value_get_boxed (&event->value)); swamigui_tree_store_add (root->patch_store, G_OBJECT (item)); /* Update splits widget if its active item changed */ g_object_get (root->splits, "splits-item", &splits_item, NULL); if (splits_item == ipatch_item_peek_parent (item)) root->splits_changed = TRUE; } /* patch item remove control value set function (listens for item remove events) */ static void ctrl_remove_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiRoot *root = SWAMIGUI_ROOT (SWAMI_CONTROL_FUNC_DATA (control)); SwamiEventItemRemove *remove; IpatchItem *splits_item; remove = g_value_get_boxed (&event->value); swamigui_tree_store_remove (root->patch_store, G_OBJECT (remove->item)); /* Update splits widget if its active item changed */ g_object_get (root->splits, "splits-item", &splits_item, NULL); if (splits_item == ipatch_item_peek_parent (remove->item)) root->splits_changed = TRUE; } /** * swamigui_root_new: * * Create a new Swami root user interface object which is a subclass of * #SwamiRoot. * * Returns: New Swami user interface object */ SwamiguiRoot * swamigui_root_new (void) { return (SWAMIGUI_ROOT (g_object_new (SWAMIGUI_TYPE_ROOT, NULL))); } /** * swamigui_root_activate: * @root: Swami GUI root object * * Activates Swami GUI by creating main window, loading plugins, and displaying * tips and splash image. Separate from object init function so that preferences * can be loaded or not. */ void swamigui_root_activate (SwamiguiRoot *root) { GError *err = NULL; g_return_if_fail (SWAMIGUI_IS_ROOT (root)); /* GUI update timeout */ root->update_timeout_id = g_timeout_add (root->update_interval, (GSourceFunc)swamigui_update_gui_timeout, root); if (root->wavetbl) { if (!swami_wavetbl_open (SWAMI_WAVETBL (root->wavetbl), &err)) { g_warning ("Failed to initialize wavetable driver '%s'", ipatch_gerror_message (err)); g_clear_error (&err); } } /* create main window and controls */ swamigui_root_create_main_window (root); /* pop up swami tip window if enabled */ if (root->tips_enable) swamigui_help_swamitips_create (root); /* display splash (with timeout) only if not disabled */ if (root->splash_enable) swamigui_splash_display (root->splash_delay); } /** * swamigui_root_quit: * @root: Swami GUI root object * * Quit Swami GUI. Pops a quit confirmation depending on preferences. */ void swamigui_root_quit (SwamiguiRoot *root) { IpatchList *list; IpatchIter iter; GObject *obj; gboolean changed = FALSE; GtkWidget *popup; int quit_confirm; char *s; list = swami_root_get_patch_items (SWAMI_ROOT (root)); /* ++ ref list */ ipatch_list_init_iter (list, &iter); obj = ipatch_iter_first (&iter); while (obj) { g_object_get (obj, "changed", &changed, NULL); if (changed) break; obj = ipatch_iter_next (&iter); } g_object_unref (list); /* -- unref list */ g_object_get (root, "quit-confirm", &quit_confirm, NULL); if (quit_confirm == SWAMIGUI_QUIT_CONFIRM_NEVER || (quit_confirm == SWAMIGUI_QUIT_CONFIRM_UNSAVED && !changed)) { swamigui_real_quit (root); return; } if (changed) s = _("Unsaved files, and you want to quit?"); else s = _("Are you sure you want to quit?"); popup = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", s); gtk_dialog_add_buttons (GTK_DIALOG (popup), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_QUIT, GTK_RESPONSE_OK, NULL); g_signal_connect (popup, "response", G_CALLBACK (swamigui_cb_quit_response), root); gtk_widget_show (popup); } static void swamigui_cb_quit_response (GtkDialog *dialog, int response, gpointer user_data) { SwamiguiRoot *root = SWAMIGUI_ROOT (user_data); gtk_widget_destroy (GTK_WIDGET (dialog)); if (response == GTK_RESPONSE_OK) swamigui_real_quit (root); } static void swamigui_real_quit (SwamiguiRoot *root) { g_signal_emit (root, uiroot_signals [QUIT], 0); } /** * swamigui_root_save_prefs: * @root: Swami GUI root object * * Save application preferences to XML config file. Only stores config if it * differs from existing stored config on disk. * * Returns: %TRUE on success, %FALSE otherwise (error message will be logged) */ gboolean swamigui_root_save_prefs (SwamiguiRoot *root) { char *config_dir, *pref_filename; GError *local_err = NULL; GNode *xmltree, *node, *pnode, *dupnode; GList *plugins, *p; SwamiPlugin *plugin; char *xmlstr; char *curxml; const char *attr_val; xmltree = ipatch_xml_new_node (NULL, "swami", NULL, /* ++ alloc XML tree */ "version", SWAMI_VERSION, NULL); /* Encode the root object */ if (!ipatch_xml_encode_object (xmltree, G_OBJECT (root), FALSE, &local_err)) { g_critical (_("Failed to save Swami preferences: %s"), ipatch_gerror_message (local_err)); g_clear_error (&local_err); ipatch_xml_destroy (xmltree); /* -- free XML tree */ return (FALSE); } /* Encode plugin preferences */ plugins = swami_plugin_get_list (); /* ++ alloc list */ for (p = plugins; p; p = p->next) { plugin = (SwamiPlugin *)(p->data); if (plugin->save_xml) { node = ipatch_xml_new_node (xmltree, "plugin", NULL, "name", G_TYPE_MODULE (plugin)->name, NULL); if (!swami_plugin_save_xml (plugin, node, &local_err)) { g_warning (_("Failed to save plugin %s preferences: %s"), G_TYPE_MODULE (plugin)->name, ipatch_gerror_message (local_err)); ipatch_xml_destroy (node); /* -- free the plugin node */ g_clear_error (&local_err); } } } g_list_free (plugins); /* -- free list */ /* Check if there is any plugin data which was loaded at startup which is no * longer present and append it to the config if so. In particular this * prevents plugin preferences from being lost if they are not loaded at * startup. */ if (root->loaded_xml_config) { for (node = root->loaded_xml_config->children; node; node = node->next) { if (!ipatch_xml_test_name (node, "plugin")) continue; attr_val = ipatch_xml_get_attribute (node, "name"); if (!attr_val) continue; for (pnode = xmltree->children; pnode; pnode = pnode->next) if (ipatch_xml_test_name (pnode, "plugin") && ipatch_xml_test_attribute (pnode, "name", attr_val)) break; if (!pnode) /* Not found in new XML config? */ { dupnode = ipatch_xml_copy (node); /* Duplicate the plugin info */ g_node_append (xmltree, dupnode); /* Append to xmltree */ } } } config_dir = g_build_filename (g_get_user_config_dir(), "swami", NULL); /* ++ alloc */ if (!g_file_test (config_dir, G_FILE_TEST_EXISTS)) { if (g_mkdir_with_parents (config_dir, 0755) == -1) { g_critical (_("Failed to create Swami config directory '%s': %s"), config_dir, g_strerror (errno)); g_free (config_dir); /* -- free */ ipatch_xml_destroy (xmltree); /* -- free XML tree */ return (FALSE); } } pref_filename = g_build_filename (config_dir, "preferences.xml", NULL); /* ++ alloc */ g_free (config_dir); /* -- free config dir */ xmlstr = ipatch_xml_to_str (xmltree, 2); /* ++ alloc xmlstr */ ipatch_xml_destroy (xmltree); /* -- free XML tree */ /* Check if config matches what is already saved - return TRUE if it matches */ if (g_file_get_contents (pref_filename, &curxml, NULL, NULL)) /* ++ alloc curxml data */ { if (strcmp (xmlstr, curxml) == 0) { g_free (curxml); /* -- free current XML data */ g_free (xmlstr); /* -- free xmlstr */ g_free (pref_filename); /* -- free pref_filename */ return (TRUE); } g_free (curxml); /* -- free current XML data */ } if (!g_file_set_contents (pref_filename, xmlstr, -1, &local_err)) { g_critical (_("Failed to save XML preferences to '%s': %s"), pref_filename, ipatch_gerror_message (local_err)); g_clear_error (&local_err); g_free (xmlstr); /* -- free xmlstr */ g_free (pref_filename); /* -- free */ return (FALSE); } g_free (xmlstr); /* -- free xmlstr */ g_free (pref_filename); /* -- free */ return (TRUE); } /** * swamigui_root_load_prefs: * @root: Swami GUI root object * * Restore application preferences from XML config files. * * Returns: %TRUE on success, %FALSE otherwise (error message will be logged) */ gboolean swamigui_root_load_prefs (SwamiguiRoot *root) { GNode *xmltree, *n; char *pref_filename; GParamSpec *pspec; GObjectClass *obj_class; SwamiPlugin *plugin; GError *err = NULL; const char *name; /* ++ alloc */ pref_filename = g_build_filename (g_get_user_config_dir(), "swami", "preferences.xml", NULL); /* No preferences stored yet? - Return success */ if (!g_file_test (pref_filename, G_FILE_TEST_EXISTS)) return (TRUE); xmltree = ipatch_xml_load_from_file (pref_filename, &err); /* ++ alloc XML tree */ if (!xmltree) { g_critical (_("Failed to load preferences from '%s': %s"), pref_filename, ipatch_gerror_message (err)); g_clear_error (&err); g_free (pref_filename); /* -- free filename */ return (FALSE); } name = ipatch_xml_get_name (xmltree); if (!name || strcmp (name, "swami") != 0) { g_critical (_("File '%s' is not a Swami preferences file"), pref_filename); ipatch_xml_destroy (xmltree); /* -- destroy XML tree */ g_free (pref_filename); /* -- free filename */ return (FALSE); } obj_class = G_OBJECT_GET_CLASS (root); for (n = xmltree->children; n; n = n->next) { name = ipatch_xml_get_name (n); if (!name) continue; if (strcmp (name, "prop") == 0) /* Root preference value */ { name = ipatch_xml_get_attribute (n, "name"); if (!name) continue; pspec = g_object_class_find_property (obj_class, name); if (!pspec || (pspec->flags & IPATCH_PARAM_NO_SAVE)) { g_warning (_("Invalid Swami property '%s' in preferences"), name); continue; } if (!ipatch_xml_decode_property (n, G_OBJECT (root), pspec, &err)) { g_critical (_("Failed to decode Swami preference property '%s': %s"), pspec->name, ipatch_gerror_message (err)); g_clear_error (&err); continue; } } else if (strcmp (name, "plugin") == 0) /* Plugin state */ { name = ipatch_xml_get_attribute (n, "name"); if (!name) continue; plugin = swami_plugin_find (name); if (plugin) { if (!swami_plugin_load_xml (plugin, n, &err)) { g_critical (_("Failed to load plugin '%s' preferences: %s"), name, ipatch_gerror_message (err)); g_clear_error (&err); } } } } g_free (pref_filename); /* -- free filename */ /* Set the loaded_xml_config variable so it reflects the last loaded XML config */ if (root->loaded_xml_config) ipatch_xml_destroy (root->loaded_xml_config); root->loaded_xml_config = xmltree; /* !! root takes over reference */ return (TRUE); } /** * swamigui_get_root: * @gobject: An object registered to a #SwamiguiRoot object * * Gets a #SwamiguiObject associated with a @gobject. A convenience function * really as swami_get_root() will return the same object but casted to * #SwamiRoot instead. * * Returns: A #SwamiguiRoot object or %NULL if @gobject not registered to one. * Returned object's refcount is not incremented. */ SwamiguiRoot * swamigui_get_root (gpointer gobject) { SwamiguiRoot *root; g_return_val_if_fail (G_IS_OBJECT (gobject), NULL); if (SWAMIGUI_IS_ROOT (gobject)) root = (SwamiguiRoot *)gobject; else root = (SwamiguiRoot *)swami_get_root (G_OBJECT (gobject)); /* if no root object is assigned to 'gobject' then just return the global */ if (!root) return (swamigui_root); return (root); } /* * Getter returning swamigui_root. * Useful when libswamigui is used as a shared library For example, plugins fluidsynth_gui and fluidsynth_plugin need swamigui_root. */ SwamiguiRoot * swamigui_get_swamigui_root (void) { return (swamigui_root); } /* Create main window and controls */ static void swamigui_root_create_main_window (SwamiguiRoot *root) { SwamiPropTree *proptree; SwamiControl *ctrl, *rootsel_ctrl; SwamiControl *store_ctrl; GtkWidget *vbox; GtkWidget *tempbox; GtkWidget *vpaned, *hpaned; GtkWidget *widg; GType type; SwamiControl *midihub; SwamiguiControlMidiKey *midikey; GObject *piano; SwamiControl *pctrl, *tctrl; SwamiControlMidi *wctrl; /* get root selection property control */ rootsel_ctrl = swami_get_control_prop_by_name (G_OBJECT (root), "selection"); root->main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (root->main_window), 1024, 768); gtk_window_set_title (GTK_WINDOW (root->main_window), "Swami"); g_signal_connect (root->main_window, "delete_event", G_CALLBACK (swamigui_root_cb_main_window_delete), root); #ifdef MAC_INTEGRATION GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL); g_signal_connect (theApp, "NSApplicationBlockTermination", G_CALLBACK (swamigui_root_cb_should_quit), root); #endif /* add the item menu keyboard accelerators */ gtk_window_add_accel_group (GTK_WINDOW (root->main_window), swamigui_item_menu_accel_group); vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); gtk_container_add (GTK_CONTAINER (root->main_window), vbox); hpaned = gtk_hpaned_new (); gtk_widget_show (hpaned); gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0); tempbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (tempbox); gtk_paned_pack1 (GTK_PANED (hpaned), tempbox, TRUE, TRUE); widg = swamigui_menu_new (); #ifndef MAC_INTEGRATION gtk_widget_show (widg); gtk_box_pack_start (GTK_BOX (tempbox), widg, FALSE, FALSE, 0); #else /* menu is handled a bit differently on OS X */ GtkUIManager *mgr = (SWAMIGUI_MENU (widg))->ui; GtkWidget *mitem; /* Set the global menu bar to the main menu */ mitem = gtk_ui_manager_get_widget(mgr, "/MenuBar"); gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL (mitem)); /* Move Help/About and Edit/Preferences menu items to the app menu */ mitem = gtk_ui_manager_get_widget (mgr, "/MenuBar/HelpMenu/About"); gtkosx_application_insert_app_menu_item (theApp, mitem, 0); mitem = gtk_separator_menu_item_new (); gtkosx_application_insert_app_menu_item (theApp, mitem, 1); mitem = gtk_ui_manager_get_widget (mgr, "/MenuBar/EditMenu/Preferences"); gtkosx_application_insert_app_menu_item (theApp, mitem, 2); /* Hide Quit menu item which is added automatically to the app menu */ mitem = gtk_ui_manager_get_widget (mgr, "/MenuBar/FileMenu/Quit"); gtk_widget_hide(mitem); /* Designate Help menu and create Window menu */ mitem = gtk_ui_manager_get_widget (mgr, "/MenuBar/HelpMenu"); gtkosx_application_set_help_menu (theApp, GTK_MENU_ITEM (mitem)); gtkosx_application_set_window_menu (theApp, NULL); /* Convert menu accelerator key modifiers */ gtk_accel_map_foreach (NULL, osx_accel_map_foreach_lcb); #endif root->tree = swamigui_tree_new (root->tree_stores); gtk_widget_show (root->tree); gtk_box_pack_start (GTK_BOX (tempbox), root->tree, TRUE, TRUE, 0); /* connect tree selection to root selection (++ ref ctrl) */ ctrl = swami_get_control_prop_by_name (G_OBJECT (root->tree), "selection"); swami_control_connect (ctrl, rootsel_ctrl, SWAMI_CONTROL_CONN_BIDIR); g_object_unref (ctrl); /* -- unref tree control */ /* HACK - Set initial selection (so that swamigui_root "selection" is not NULL) * We need an empty selection, since it has the origin of the tree. * So no problems occur with tree menu accelerators. */ swamigui_tree_set_selection (SWAMIGUI_TREE (root->tree), NULL); vpaned = gtk_vpaned_new (); gtk_widget_show (vpaned); gtk_paned_pack2 (GTK_PANED (hpaned), vpaned, TRUE, TRUE); tempbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (tempbox); gtk_paned_pack1 (GTK_PANED (vpaned), tempbox, TRUE, TRUE); /* create FluidSynth interface */ type = g_type_from_name ("FluidSynthGuiControl"); if (type) { widg = g_object_new (type, NULL); gtk_widget_show_all (widg); gtk_box_pack_start (GTK_BOX (tempbox), widg, FALSE, FALSE, 2); } /* create splits widget */ root->splits = swamigui_splits_new (); gtk_widget_show (root->splits); gtk_box_pack_start (GTK_BOX (tempbox), root->splits, TRUE, TRUE, 0); /* connect root selection to splits (++ ref ctrl) */ ctrl = swami_get_control_prop_by_name (G_OBJECT (root->splits), "item-selection"); swami_control_connect (rootsel_ctrl, ctrl, SWAMI_CONTROL_CONN_BIDIR); g_object_unref (ctrl); /* -- unref tree control */ root->panel_selector = swamigui_panel_selector_new (root); gtk_widget_show (root->panel_selector); gtk_paned_pack2 (GTK_PANED (vpaned), root->panel_selector, FALSE, TRUE); /* connect root selection to panel selector (++ ref ctrl) */ ctrl = swami_get_control_prop_by_name (G_OBJECT (root->panel_selector), "item-selection"); swami_control_connect (rootsel_ctrl, ctrl, 0); g_object_unref (ctrl); /* -- unref tree control */ gtk_paned_set_position (GTK_PANED (hpaned), 300); gtk_paned_set_position (GTK_PANED (vpaned), 400); /* create statusbar and pack it */ root->statusbar = SWAMIGUI_STATUSBAR (swamigui_statusbar_new ()); gtk_widget_show (GTK_WIDGET (root->statusbar)); gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (root->statusbar), FALSE, FALSE, 2); /* connect root selection to GUI switcher widget property tree variables */ proptree = SWAMI_ROOT (root)->proptree; swami_prop_tree_add_value (proptree, G_OBJECT (root), 0, "item-selection", rootsel_ctrl); /* create SwamiguiRoot tree store property control (++ ref new object) */ store_ctrl = swami_get_control_prop_by_name (G_OBJECT (root), "tree-store-list"); /* FIXME - kill hackish property tree (alternatives?) */ swami_prop_tree_add_value (proptree, G_OBJECT (root), 0, "store-list", store_ctrl); g_object_unref (store_ctrl); /* -- unref creator's reference */ g_object_unref (rootsel_ctrl); /* -- unref root control */ /* Control section of init */ /* create MIDI hub control */ midihub = SWAMI_CONTROL /* ++ ref */ (swami_root_new_object (SWAMI_ROOT (root), "SwamiControlHub")); /* create MIDI keyboard control */ midikey = SWAMIGUI_CONTROL_MIDI_KEY /* ++ ref */ (swami_root_new_object (SWAMI_ROOT (root), "SwamiguiControlMidiKey")); /* connect keyboard control to MIDI hub */ swami_control_connect (SWAMI_CONTROL (midikey), midihub, 0); /* Connect splits widget keyboard controls to MIDI keyboard object */ swamigui_root_connect_midi_keyboard_controls (root, G_OBJECT (midikey)); g_object_unref (midikey); /* -- unref creator's ref */ g_object_get (root->splits, "piano", &piano, NULL); /* ++ ref piano */ g_object_get (piano, "midi-control", &pctrl, NULL); /* ++ ref piano MIDI ctrl */ /* connect piano to MIDI hub */ swami_control_connect (pctrl, midihub, SWAMI_CONTROL_CONN_BIDIR); g_object_unref (pctrl); /* -- unref */ g_object_unref (piano); /* -- unref */ if (root->wavetbl) { /* get wavetable MIDI control */ wctrl = swami_wavetbl_get_control (SWAMI_WAVETBL (root->wavetbl), 0); /* set initial bank/program number */ swami_control_midi_send (wctrl, SWAMI_MIDI_BANK_SELECT, 0, 127, -1); swami_control_midi_send (wctrl, SWAMI_MIDI_PROGRAM_CHANGE, 0, 127, -1); /* connect wavetable to MIDI hub */ swami_control_connect (midihub, SWAMI_CONTROL (wctrl), SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_PRIORITY_HIGH); g_object_unref (wctrl); /* -- unref */ /* get control connected to root selection-single property (++ ref) */ tctrl = SWAMI_CONTROL (swami_get_control_prop_by_name (G_OBJECT (root), "selection-single")); /* get prop control to wavetbl temp item (++ ref ctrl) */ pctrl = SWAMI_CONTROL (swami_get_control_prop_by_name (G_OBJECT (root->wavetbl), "active-item")); swami_control_connect (tctrl, pctrl, 0); g_object_unref (tctrl); /* -- unref */ g_object_unref (pctrl); /* -- unref */ g_signal_connect (root, "notify::selection-single", G_CALLBACK (swamigui_root_cb_solo_item), NULL); } g_object_unref (midihub); /* -- unref creator's reference */ gtk_widget_show (root->main_window); #ifdef MAC_INTEGRATION /* Tell OS X that we are ready launching now */ gtkosx_application_ready (theApp); #endif } /* Callback for SwamiguiRoot::selection-single to update Wavetbl solo-item if enabled */ static void swamigui_root_cb_solo_item (SwamiguiRoot *root, GParamSpec *pspec, gpointer user_data) { GObject *solo_item; if (!root->solo_item_enabled) return; g_object_get (root, "selection-single", &solo_item, NULL); /* ++ ref solo item */ swamigui_root_update_solo_item (root, solo_item); if (solo_item) g_object_unref (solo_item); /* -- unref solo item */ } static void swamigui_root_update_solo_item (SwamiguiRoot *root, GObject *solo_item) { GtkTreeIter iter; int category; /* If no wavetbl driver active - return */ if (!root->wavetbl) return; /* Valid solo items are instrument or sample zones */ if (solo_item) { ipatch_type_get (G_OBJECT_TYPE (solo_item), "category", &category, NULL); if (category != IPATCH_CATEGORY_SAMPLE_REF && category != IPATCH_CATEGORY_INSTRUMENT_REF) solo_item = NULL; } /* If previous solo item, restore its icon */ if (root->solo_item) { swamigui_tree_store_change (root->patch_store, root->solo_item, NULL, root->solo_item_icon); g_object_unref (root->solo_item); // -- unref solo item root->solo_item = NULL; root->solo_item_icon = NULL; // !! solo_item_icon is a static string pointer, do not free } if (solo_item) { if (swamigui_tree_store_item_get_node (root->patch_store, solo_item, &iter)) { root->solo_item = g_object_ref (solo_item); // ++ ref solo item /* !! icon column is a static string pointer - not allocated */ gtk_tree_model_get (GTK_TREE_MODEL (root->patch_store), &iter, SWAMIGUI_TREE_STORE_ICON_COLUMN, &root->solo_item_icon, -1); gtk_tree_store_set (GTK_TREE_STORE (root->patch_store), &iter, SWAMIGUI_TREE_STORE_ICON_COLUMN, GTK_STOCK_MEDIA_PLAY, -1); } else solo_item = NULL; /* Shouldn't fail to find item in tree, but.. */ } g_object_set (root->wavetbl, "solo-item", solo_item, NULL); } static void swamigui_root_connect_midi_keyboard_controls (SwamiguiRoot *root, GObject *midikey) { GtkWidget *widget; int i; struct { char *widg_name; char *prop_name; } names[] = { { "SpinBtnLowerOctave", "lower-octave" }, { "ChkBtnJoinOctaves", "join-octaves" }, { "SpinBtnUpperOctave", "upper-octave" }, { "SpinBtnLowerVelocity", "lower-velocity" }, { "ChkBtnSameVelocity", "same-velocity" }, { "SpinBtnUpperVelocity", "upper-velocity" } }; for (i = 0; i < G_N_ELEMENTS (names); i++) { widget = swamigui_util_glade_lookup (SWAMIGUI_SPLITS (root->splits)->gladewidg, names[i].widg_name); swamigui_control_prop_connect_widget (midikey, names[i].prop_name, G_OBJECT (widget)); } } /* called when main window is closed */ static gint swamigui_root_cb_main_window_delete (GtkWidget *widget, GdkEvent *event, gpointer data) { SwamiguiRoot *root = SWAMIGUI_ROOT (data); swamigui_root_quit (root); return (TRUE); } /* converts a string of comma delimited GDK key symbols into a newly allocated zero terminated array of key values */ static guint * swamigui_root_parse_piano_keys (const char *str) { guint *keyvals; char **keynames; int count = 0, v = 0, n = 0; if (!str) return (NULL); keynames = g_strsplit (str, ",", 0); while (keynames[count]) count++; /* count key symbols */ keyvals = g_malloc ((count + 1) * sizeof (guint)); while (keynames[n]) { keyvals[v] = gdk_keyval_from_name (keynames[n]); if (keyvals[v] != GDK_VoidSymbol) v++; /* ignore invalid key names */ n++; } keyvals[v] = 0; /* zero terminated */ g_strfreev (keynames); return (keyvals); } /* converts a zero terminated array of key values into a newly allocated comma delimited GDK key symbol string */ static char * swamigui_root_encode_piano_keys (const guint *keyvals) { char **keynames; char *s; int i, count = 0; if (!keyvals) return (NULL); while (keyvals[count]) count++; /* count number of key values */ /* allocate for array of key name strings */ keynames = g_malloc ((count + 1) * sizeof (char *)); for (i = 0; i < count; i++) /* loop over each key */ { keynames[i] = gdk_keyval_name (keyvals[i]); /* get key name */ if (!keynames[i]) /* if no key name for key, fail */ { g_critical ("No GDK key name for key '%d'", keyvals[i]); g_free (keynames); return (NULL); } } keynames[count] = NULL; s = g_strjoinv (",", keynames); /* create the comma delimited key string */ g_free (keynames); return (s); } /* handler for IpatchSF2 glade property interface. * Needed in order to handle "Current Date" button. */ static GtkWidget * sf2_prop_handler (GtkWidget *widg, GObject *obj) { GtkWidget *btn, *entry; if (!widg) /* create widget? */ { widg = swamigui_util_glade_create ("PropSF2"); btn = swamigui_util_glade_lookup (widg, "BtnCurrentDate"); entry = swamigui_util_glade_lookup (widg, "PROP::date"); g_signal_connect (btn, "clicked", G_CALLBACK (current_date_btn_clicked), entry); swamigui_control_glade_prop_connect (widg, obj); } else swamigui_control_glade_prop_connect (widg, obj); /* change object */ return (widg); } /* callback when "Current Date" button is clicked */ static void current_date_btn_clicked (GtkButton *button, gpointer user_data) { struct tm *date; char datestr[64]; time_t t; t = time (NULL); date = localtime (&t); g_return_if_fail (date != NULL); /* NOTE: SoundFont standard says that conventionally the date is in the format * "April 3, 2008". This isn't international friendly though, so we instead * use the format YYYY-MM-DD which we have deemed OK, because of the use of * the word "conventionally" in the spec. */ strftime (datestr, sizeof (datestr), "%Y-%m-%d", date); gtk_entry_set_text (GTK_ENTRY (user_data), datestr); } /* handler for IpatchSLIInst glade property interface. * Creates the Category combo box and selects its initial item */ static GtkWidget * sli_inst_prop_handler (GtkWidget *widg, GObject *obj) { GtkWidget *combo; GtkTreeModel *model; GtkCellRenderer *cell; gchar *catpath; GtkTreeIter iter; if (!widg) /* create widget? */ { /* get combo box widget */ widg = swamigui_util_glade_create ("PropSLIInst"); combo = swamigui_util_glade_lookup (widg, "ComboCategory"); gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), 2); /* create and attach a tree model */ /* ++ ref new store */ model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING)); cat_map_to_tree_store (GTK_TREE_STORE (model), ipatch_sli_inst_cat_map, NULL); gtk_combo_box_set_model (GTK_COMBO_BOX (combo), model); g_object_unref (model); /* -- unref store */ /* set cell renderer */ cell = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0); /* connect callback function */ g_signal_connect (combo, "changed", G_CALLBACK (category_changed_cb), NULL); } else { combo = swamigui_util_glade_lookup (widg, "ComboCategory"); } /* connect prop controls to supplied object */ swamigui_control_glade_prop_connect (widg, obj); /* FIXME: setting object data may be a hack due to the lack of understanding * if a better way exists to get this in the callback function */ g_object_set_data (G_OBJECT (combo), "controlled-object", obj); /* set combo box to match category of the object */ /* (setting active item in combo box emits a changed signal) */ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); catpath = ipatch_sli_inst_get_category_as_path (IPATCH_SLI_INST (obj)); if (gtk_tree_model_get_iter_from_string (model, &iter, catpath)) gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); g_free (catpath); return (widg); } static void cat_map_to_tree_store (GtkTreeStore *store, const IpatchSLIInstCatMapEntry *catmap, GtkTreeIter *parent) { GtkTreeIter child; guint i; gchar *label, *prefix; g_return_if_fail (store != NULL && catmap != NULL); for (i = 0; catmap[i].code != '@'; i++) { if (parent) { gtk_tree_model_get (GTK_TREE_MODEL (store), parent, 0, &prefix, -1); label = g_strjoin (" | ", prefix, ipatch_sli_inst_cat_strings[catmap[i].name_idx], NULL); g_free (prefix); } else label = g_strdup (ipatch_sli_inst_cat_strings[catmap[i].name_idx]); gtk_tree_store_append (store, &child, parent); gtk_tree_store_set (store, &child, 0, label, -1); g_free (label); if (catmap[i].submap) /* recursion for subcat maps */ cat_map_to_tree_store (store, catmap[i].submap, &child); } /* Add Other category only at the topmost level */ if (!parent) { gtk_tree_store_append (store, &child, parent); gtk_tree_store_set (store, &child, 0, ipatch_sli_inst_cat_strings[catmap[i].name_idx], -1); } } /* callback when Category combo box is changed */ static void category_changed_cb (GtkComboBox *combo, gpointer user_data) { const IpatchSLIInstCatMapEntry *catmap = ipatch_sli_inst_cat_map; IpatchSLIInst *inst; gboolean ret; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; gint *pinds; guint old_cat, new_cat, i, d; inst = g_object_get_data (G_OBJECT (combo), "controlled-object"); g_return_if_fail (IPATCH_IS_SLI_INST (inst)); model = gtk_combo_box_get_model (combo); ret = gtk_combo_box_get_active_iter (combo, &iter); g_return_if_fail (ret == TRUE); path = gtk_tree_model_get_path (model, &iter); /* ++ ref path */ pinds = gtk_tree_path_get_indices (path); d = gtk_tree_path_get_depth (path); for (new_cat = 0, i = 0; catmap && i < d; i++) { new_cat <<= 8; new_cat |= catmap[pinds[i]].code; catmap = catmap[pinds[i]].submap; } if (i < 2) /* treat subcat as Other if only main category is selected */ { new_cat <<= 8; new_cat |= '@'; } gtk_tree_path_free (path); /* -- unref path */ g_object_get (inst, "category", &old_cat, NULL); if (new_cat != old_cat) g_object_set (inst, "category", new_cat, NULL); } #ifdef MAC_INTEGRATION /* Callback to convert Ctrl to Command in menu accelerator keys */ static void osx_accel_map_foreach_lcb(gpointer data, const gchar *accel_path, guint accel_key, GdkModifierType accel_mods, gboolean changed) { if (accel_mods & GDK_CONTROL_MASK) { accel_mods &= ~GDK_CONTROL_MASK; accel_mods |= GDK_META_MASK; gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, FALSE); } } /* Callback called for NSApplicationShouldTerminate notification * we return TRUE to veto the termination here because the user can * cancel the quit dialog */ static gboolean swamigui_root_cb_should_quit (GtkosxApplication *theApp, gpointer data) { swamigui_root_quit (SWAMIGUI_ROOT (data)); return TRUE; } #endif /** * swamigui_root_is_middle_click: * @root: Root object or NULL to use global root object * @event: Button event * * Check if a button event is a middle click, taking into account middle button emulation settings. */ gboolean swamigui_root_is_middle_click (SwamiguiRoot *root, GdkEventButton *event) { g_return_val_if_fail (!root || SWAMIGUI_IS_ROOT (root), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (event->type == GDK_BUTTON_PRESS && event->button == 2) return (TRUE); if (!root) root = swamigui_root; if (!root) return (FALSE); // Middle button emulation enabled, left mouse button click and modifier held? return (event->type == GDK_BUTTON_PRESS && event->button == 1 && root->middle_emul_enable && (event->state & (GDK_MOD1_MASK << root->middle_emul_mod))); } swami-2.2.0/src/swamigui/SwamiguiRoot.h000066400000000000000000000121401361104770400200520ustar00rootroot00000000000000/* * SwamiguiRoot.h - Main Swami user interface object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_ROOT_H__ #define __SWAMIGUI_ROOT_H__ typedef struct _SwamiguiRoot SwamiguiRoot; typedef struct _SwamiguiRootClass SwamiguiRootClass; #include #include #include #include #include #include #define SWAMIGUI_TYPE_ROOT (swamigui_root_get_type ()) #define SWAMIGUI_ROOT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_ROOT, SwamiguiRoot)) #define SWAMIGUI_ROOT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_ROOT, SwamiguiRootClass)) #define SWAMIGUI_IS_ROOT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_ROOT)) #define SWAMIGUI_IS_ROOT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_ROOT)) typedef enum { SWAMIGUI_QUIT_CONFIRM_ALWAYS, /* always pop a quit confirmation */ SWAMIGUI_QUIT_CONFIRM_UNSAVED, /* only if there are unsaved files */ SWAMIGUI_QUIT_CONFIRM_NEVER /* spontaneous combust */ } SwamiguiQuitConfirm; /* Swami User Interface Object */ struct _SwamiguiRoot { SwamiRoot parent_instance; /* derived from SwamiRoot */ SwamiguiTreeStore *patch_store; /* patch tree store */ SwamiguiTreeStore *config_store; /* config tree store */ IpatchList *tree_stores; /* list of tree stores (including above) */ IpatchList *selection; /* most recent item selection (trees, etc) */ GtkWidget *main_window; /* Main toplevel window */ GtkWidget *tree; /* Tree widget */ GtkWidget *splits; /* Note/velocity splits widget */ gboolean splits_changed; /* Set to TRUE if splits-item changed and needs updating */ GtkWidget *panel_selector; /* Panel selector widget */ SwamiguiStatusbar *statusbar; /* Main statusbar */ SwamiWavetbl *wavetbl; /* Wavetable object */ gboolean solo_item_enabled; /* TRUE if solo item enabled, FALSE otherwise */ GObject *solo_item; /* Solo item child of active-item or NULL */ char *solo_item_icon; /* Stores original icon of current solo item or NULL */ SwamiControlQueue *ctrl_queue; /* control update queue */ guint update_timeout_id; /* GSource ID for GUI update timeout */ int update_interval; /* GUI update interval in milliseconds */ SwamiControlFunc *ctrl_prop; /* patch item property change ctrl listener */ SwamiControlFunc *ctrl_add; /* patch item add control listener */ SwamiControlFunc *ctrl_remove; /* patch item remove control listener */ SwamiguiQuitConfirm quit_confirm; /* quit confirm enum */ gboolean splash_enable; /* show splash on startup? */ guint splash_delay; /* splash delay in milliseconds (0 to wait for button press) */ gboolean tips_enable; /* display next tip on startup? */ int tips_position; /* Swami tips position */ guint *piano_lower_keys; /* zero terminated array of keys (or NULL) */ guint *piano_upper_keys; /* zero terminated array of keys (or NULL) */ GType default_patch_type; /* default patch type (for File->New) */ GNode *loaded_xml_config; /* Last loaded XML config (usually only on startup) or NULL */ GList *panel_cache; /* Cache of SwamiguiPanel objects */ gboolean middle_emul_enable; /* Middle mouse button emulation enable */ gboolean middle_emul_mod; /* Middle mouse button emulation key modifier */ }; struct _SwamiguiRootClass { SwamiRootClass parent_class; /* signals */ void (*quit)(SwamiguiRoot *root); }; // Name of instrument files group for GtkRecentManager items #define SWAMIGUI_ROOT_INSTRUMENT_FILES_GROUP "Instrument Files" /* global instances of SwamiguiRoot */ extern SwamiguiRoot *swamigui_root; extern SwamiRoot *swami_root; /* just for convenience */ /* Getter returning swamigui_root. */ /* Useful when libswamigui is used as a shared library */ SwamiguiRoot *swamigui_get_swamigui_root (void); void swamigui_init (int *argc, char **argv[]); GType swamigui_root_get_type (void); SwamiguiRoot *swamigui_root_new (void); void swamigui_root_activate (SwamiguiRoot *root); void swamigui_root_quit (SwamiguiRoot *root); SwamiguiRoot *swamigui_get_root (gpointer gobject); gboolean swamigui_root_save_prefs (SwamiguiRoot *root); gboolean swamigui_root_load_prefs (SwamiguiRoot *root); gboolean swamigui_root_is_middle_click (SwamiguiRoot *root, GdkEventButton *event); #endif swami-2.2.0/src/swamigui/SwamiguiSampleCanvas.c000066400000000000000000001102031361104770400214760ustar00rootroot00000000000000/* * SwamiguiSampleCanvas.c - Sample data canvas widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiguiSampleCanvas.h" #include "util.h" #include "i18n.h" /* Sample format used internally */ #define SAMPLE_FORMAT IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST | IPATCH_SAMPLE_MONO enum { PROP_0, PROP_SAMPLE, /* sample to display */ PROP_RIGHT_CHAN, /* if sample data is stereo: use right channel? */ PROP_LOOP_MODE, /* set loop mode */ PROP_LOOP_START, /* start of loop */ PROP_LOOP_END, /* end of loop */ PROP_ADJUSTMENT, /* adjustment control */ PROP_UPDATE_ADJ, /* update adjustment values? */ PROP_X, /* x position in pixels */ PROP_Y, /* y position in pixels */ PROP_WIDTH, /* width of view in pixels */ PROP_HEIGHT, /* height of view in pixels */ PROP_START, /* sample start position */ PROP_ZOOM, /* zoom value */ PROP_ZOOM_AMPL, /* amplitude zoom */ PROP_PEAK_LINE_COLOR, /* color of peak sample lines */ PROP_LINE_COLOR, /* color of connecting sample lines */ PROP_POINT_COLOR, /* color of sample points */ PROP_LOOP_START_COLOR, /* color of sample points for start of loop */ PROP_LOOP_END_COLOR /* color of sample points for end of loop */ }; #define DEFAULT_PEAK_LINE_COLOR GNOME_CANVAS_COLOR (63, 69, 255) #define DEFAULT_LINE_COLOR GNOME_CANVAS_COLOR (63, 69, 255) #define DEFAULT_POINT_COLOR GNOME_CANVAS_COLOR (170, 170, 255) #define DEFAULT_LOOP_START_COLOR GNOME_CANVAS_COLOR (0, 255, 0) #define DEFAULT_LOOP_END_COLOR GNOME_CANVAS_COLOR (255, 0, 0) static void swamigui_sample_canvas_finalize (GObject *object); static void swamigui_sample_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_sample_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_sample_canvas_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags); static void swamigui_sample_canvas_realize (GnomeCanvasItem *item); static void set_gc_rgb (GdkGC *gc, guint color); static void swamigui_sample_canvas_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height); static inline void swamigui_sample_canvas_draw_loop (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height); static inline void swamigui_sample_canvas_draw_points (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height); static inline void swamigui_sample_canvas_draw_segments (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height); static double swamigui_sample_canvas_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item); static void swamigui_sample_canvas_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2); static void swamigui_sample_canvas_cb_adjustment_value_changed (GtkAdjustment *adj, gpointer user_data); static gboolean swamigui_sample_canvas_real_set_sample (SwamiguiSampleCanvas *canvas, IpatchSampleData *sample); static void swamigui_sample_canvas_update_adjustment (SwamiguiSampleCanvas *canvas); G_DEFINE_TYPE (SwamiguiSampleCanvas, swamigui_sample_canvas, GNOME_TYPE_CANVAS_ITEM) static void swamigui_sample_canvas_class_init (SwamiguiSampleCanvasClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (klass); obj_class->set_property = swamigui_sample_canvas_set_property; obj_class->get_property = swamigui_sample_canvas_get_property; obj_class->finalize = swamigui_sample_canvas_finalize; item_class->update = swamigui_sample_canvas_update; item_class->realize = swamigui_sample_canvas_realize; item_class->draw = swamigui_sample_canvas_draw; item_class->point = swamigui_sample_canvas_point; item_class->bounds = swamigui_sample_canvas_bounds; /* FIXME - Is there a better way to define an interface object property? */ g_object_class_install_property (obj_class, PROP_SAMPLE, g_param_spec_object ("sample", _("Sample"), _("Sample object"), IPATCH_TYPE_SAMPLE_DATA, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_RIGHT_CHAN, g_param_spec_boolean ("right-chan", _("Right Channel"), _("Use right channel of stereo samples"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOOP_MODE, g_param_spec_boolean ("loop-mode", _("Loop Mode"), _("Enable/disable loop mode"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOOP_START, g_param_spec_uint ("loop-start", _("Loop Start"), _("Start of loop in samples"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_LOOP_END, g_param_spec_uint ("loop-end", _("Loop end"), _("End of loop in samples"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ADJUSTMENT, g_param_spec_object ("adjustment", _("Adjustment"), _("Adjustment control for scrolling"), GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); /* FIXME - better way to handle this?? Problems with recursive updates when multiple SwamiguiSampleCanvas items are on a canvas. */ g_object_class_install_property (obj_class, PROP_UPDATE_ADJ, g_param_spec_boolean ("update-adj", _("Update adjustment"), _("Update adjustment object"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_X, g_param_spec_int ("x", "X", _("X position in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_Y, g_param_spec_int ("y", "Y", _("Y position in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WIDTH, g_param_spec_int ("width", _("Width"), _("Width in pixels"), 0, G_MAXINT, 1, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_HEIGHT, g_param_spec_int ("height", _("Height"), _("Height in pixels"), 0, G_MAXINT, 1, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_START, g_param_spec_uint ("start", _("View Start"), _("Start of view in samples"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ZOOM, g_param_spec_double ("zoom", _("Zoom"), _("Zoom factor in samples per pixel"), 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ZOOM_AMPL, g_param_spec_double ("zoom-ampl", _("Zoom Amplitude"), _("Amplitude zoom factor"), 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PEAK_LINE_COLOR, ipatch_param_set (g_param_spec_uint ("peak-line-color", _("Peak line color"), _("Color of peak sample lines"), 0, G_MAXUINT, DEFAULT_PEAK_LINE_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_LINE_COLOR, ipatch_param_set (g_param_spec_uint ("line-color", _("Line color"), _("Color of sample connecting lines"), 0, G_MAXUINT, DEFAULT_LINE_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_POINT_COLOR, ipatch_param_set (g_param_spec_uint ("point-color", _("Point color"), _("Color of sample points"), 0, G_MAXUINT, DEFAULT_POINT_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_LOOP_START_COLOR, ipatch_param_set (g_param_spec_uint ("loop-start-color", _("Loop start color"), _("Color of loop start sample points"), 0, G_MAXUINT, DEFAULT_LOOP_START_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); g_object_class_install_property (obj_class, PROP_LOOP_END_COLOR, ipatch_param_set (g_param_spec_uint ("loop-end-color", _("Loop end color"), _("Color of loop end sample points"), 0, G_MAXUINT, DEFAULT_LOOP_END_COLOR, G_PARAM_READWRITE), "unit-type", SWAMIGUI_UNIT_RGBA_COLOR, NULL)); } static void swamigui_sample_canvas_init (SwamiguiSampleCanvas *canvas) { canvas->adj = (GtkAdjustment *)gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); g_object_ref (canvas->adj); canvas->update_adj = FALSE; g_signal_connect (canvas->adj, "value-changed", G_CALLBACK (swamigui_sample_canvas_cb_adjustment_value_changed), canvas); canvas->sample = NULL; canvas->sample_size = 0; canvas->right_chan = FALSE; canvas->loop_mode = FALSE; canvas->loop_start = 0; canvas->loop_end = 1; canvas->start = 0; canvas->zoom = 1.0; canvas->zoom_ampl = 1.0; canvas->x = 0; canvas->y = 0; canvas->width = 0; canvas->height = 0; canvas->peak_line_color = DEFAULT_PEAK_LINE_COLOR; canvas->line_color = DEFAULT_LINE_COLOR; canvas->point_color = DEFAULT_POINT_COLOR; canvas->loop_start_color = DEFAULT_LOOP_START_COLOR; canvas->loop_end_color = DEFAULT_LOOP_END_COLOR; } static void swamigui_sample_canvas_finalize (GObject *object) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (object); if (canvas->sample) { ipatch_sample_handle_close (&canvas->handle); g_object_unref (canvas->sample); } if (canvas->adj) { g_signal_handlers_disconnect_by_func (canvas->adj, G_CALLBACK (swamigui_sample_canvas_cb_adjustment_value_changed), canvas); g_object_unref (canvas->adj); } if (canvas->peak_line_gc) g_object_unref (canvas->peak_line_gc); if (canvas->line_gc) g_object_unref (canvas->line_gc); if (canvas->point_gc) g_object_unref (canvas->point_gc); if (canvas->loop_start_gc) g_object_unref (canvas->loop_start_gc); if (canvas->loop_end_gc) g_object_unref (canvas->loop_end_gc); if (G_OBJECT_CLASS (swamigui_sample_canvas_parent_class)->finalize) (*G_OBJECT_CLASS (swamigui_sample_canvas_parent_class)->finalize) (object); } static void swamigui_sample_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object); SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (object); IpatchSampleData *sample; GtkAdjustment *gtkadj; gboolean b; switch (property_id) { case PROP_SAMPLE: swamigui_sample_canvas_real_set_sample (canvas, IPATCH_SAMPLE_DATA (g_value_get_object (value))); break; case PROP_RIGHT_CHAN: b = g_value_get_boolean (value); if (b != canvas->right_chan) { canvas->right_chan = b; if (canvas->sample) /* Unset and then re-assign the sample */ { sample = g_object_ref (canvas->sample); /* ++ temp ref */ swamigui_sample_canvas_real_set_sample (canvas, NULL); swamigui_sample_canvas_real_set_sample (canvas, sample); g_object_unref (canvas->sample); /* -- temp unref */ } } break; case PROP_LOOP_MODE: canvas->loop_mode = g_value_get_boolean (value); gnome_canvas_item_request_update (item); break; case PROP_LOOP_START: canvas->loop_start = g_value_get_uint (value); gnome_canvas_item_request_update (item); break; case PROP_LOOP_END: canvas->loop_end = g_value_get_uint (value); gnome_canvas_item_request_update (item); break; case PROP_ADJUSTMENT: gtkadj = g_value_get_object (value); g_return_if_fail (GTK_IS_ADJUSTMENT (gtkadj)); g_signal_handlers_disconnect_by_func (canvas->adj, G_CALLBACK (swamigui_sample_canvas_cb_adjustment_value_changed), canvas); g_object_unref (canvas->adj); canvas->adj = GTK_ADJUSTMENT (g_object_ref (gtkadj)); g_signal_connect (canvas->adj, "value-changed", G_CALLBACK (swamigui_sample_canvas_cb_adjustment_value_changed), canvas); /* only initialize adjustment if updates enabled */ if (canvas->update_adj) swamigui_sample_canvas_update_adjustment (canvas); break; case PROP_UPDATE_ADJ: canvas->update_adj = g_value_get_boolean (value); break; case PROP_X: canvas->x = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_Y: canvas->y = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_WIDTH: canvas->width = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->page_size = canvas->width * canvas->zoom; gtk_adjustment_changed (canvas->adj); } break; case PROP_HEIGHT: canvas->height = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_START: canvas->start = g_value_get_uint (value); gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->value = canvas->start; g_signal_handlers_block_by_func (canvas->adj, swamigui_sample_canvas_cb_adjustment_value_changed, canvas); gtk_adjustment_value_changed (canvas->adj); g_signal_handlers_unblock_by_func (canvas->adj, swamigui_sample_canvas_cb_adjustment_value_changed, canvas); } break; case PROP_ZOOM: canvas->zoom = g_value_get_double (value); gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->page_size = canvas->width * canvas->zoom; gtk_adjustment_changed (canvas->adj); } break; case PROP_ZOOM_AMPL: canvas->zoom_ampl = g_value_get_double (value); gnome_canvas_item_request_update (item); break; case PROP_PEAK_LINE_COLOR: canvas->peak_line_color = g_value_get_uint (value); set_gc_rgb (canvas->peak_line_gc, canvas->peak_line_color); gnome_canvas_item_request_update (item); break; case PROP_LINE_COLOR: canvas->line_color = g_value_get_uint (value); set_gc_rgb (canvas->line_gc, canvas->line_color); gnome_canvas_item_request_update (item); break; case PROP_POINT_COLOR: canvas->point_color = g_value_get_uint (value); set_gc_rgb (canvas->point_gc, canvas->point_color); gnome_canvas_item_request_update (item); break; case PROP_LOOP_START_COLOR: canvas->loop_start_color = g_value_get_uint (value); set_gc_rgb (canvas->loop_start_gc, canvas->loop_start_color); gnome_canvas_item_request_update (item); break; case PROP_LOOP_END_COLOR: canvas->loop_end_color = g_value_get_uint (value); set_gc_rgb (canvas->loop_end_gc, canvas->loop_end_color); gnome_canvas_item_request_update (item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); return; /* return, to skip code below */ } } static void swamigui_sample_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (object); switch (property_id) { case PROP_SAMPLE: g_value_set_object (value, canvas->sample); break; case PROP_RIGHT_CHAN: g_value_set_boolean (value, canvas->right_chan); break; case PROP_LOOP_MODE: g_value_set_boolean (value, canvas->loop_mode); break; case PROP_LOOP_START: g_value_set_uint (value, canvas->loop_start); break; case PROP_LOOP_END: g_value_set_uint (value, canvas->loop_end); break; case PROP_ADJUSTMENT: g_value_set_object (value, canvas->adj); break; case PROP_UPDATE_ADJ: g_value_set_boolean (value, canvas->update_adj); break; case PROP_X: g_value_set_int (value, canvas->x); break; case PROP_Y: g_value_set_int (value, canvas->y); break; case PROP_WIDTH: g_value_set_int (value, canvas->width); break; case PROP_HEIGHT: g_value_set_int (value, canvas->height); break; case PROP_START: g_value_set_uint (value, canvas->start); break; case PROP_ZOOM: g_value_set_double (value, canvas->zoom); break; case PROP_ZOOM_AMPL: g_value_set_double (value, canvas->zoom_ampl); break; case PROP_PEAK_LINE_COLOR: g_value_set_uint (value, canvas->peak_line_color); break; case PROP_LINE_COLOR: g_value_set_uint (value, canvas->line_color); break; case PROP_POINT_COLOR: g_value_set_uint (value, canvas->point_color); break; case PROP_LOOP_START_COLOR: g_value_set_uint (value, canvas->loop_start_color); break; case PROP_LOOP_END_COLOR: g_value_set_uint (value, canvas->loop_end_color); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* Gnome canvas item update handler */ static void swamigui_sample_canvas_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (item); if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY) && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE)) || (flags & GNOME_CANVAS_UPDATE_AFFINE) || canvas->need_bbox_update) { canvas->need_bbox_update = FALSE; gnome_canvas_update_bbox (item, canvas->x, canvas->y, canvas->x + canvas->width, canvas->y + canvas->height); } else gnome_canvas_request_redraw (item->canvas, canvas->x, canvas->y, canvas->x + canvas->width, canvas->y + canvas->height); if (GNOME_CANVAS_ITEM_CLASS (swamigui_sample_canvas_parent_class)->update) GNOME_CANVAS_ITEM_CLASS (swamigui_sample_canvas_parent_class)->update (item, affine, clip_path, flags); } static void swamigui_sample_canvas_realize (GnomeCanvasItem *item) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (item); GdkDrawable *drawable; if (GNOME_CANVAS_ITEM_CLASS (swamigui_sample_canvas_parent_class)->realize) GNOME_CANVAS_ITEM_CLASS (swamigui_sample_canvas_parent_class)->realize (item); if (canvas->peak_line_gc) return; /* return if GCs already created */ drawable = item->canvas->layout.bin_window; canvas->peak_line_gc = gdk_gc_new (drawable); /* ++ ref */ set_gc_rgb (canvas->peak_line_gc, canvas->peak_line_color); canvas->line_gc = gdk_gc_new (drawable); /* ++ ref */ set_gc_rgb (canvas->line_gc, canvas->line_color); canvas->point_gc = gdk_gc_new (drawable);/* ++ ref */ set_gc_rgb (canvas->point_gc, canvas->point_color); canvas->loop_start_gc = gdk_gc_new (drawable); /* ++ ref */ set_gc_rgb (canvas->loop_start_gc, canvas->loop_start_color); canvas->loop_end_gc = gdk_gc_new (drawable); /* ++ ref */ set_gc_rgb (canvas->loop_end_gc, canvas->loop_end_color); gdk_gc_set_function (canvas->loop_end_gc, GDK_XOR); } /* sets fg color of a Graphics Context to 32 bit GnomeCanvas style RGB value */ static void set_gc_rgb (GdkGC *gc, guint color) { GdkColor gdkcolor; gdkcolor.pixel = 0; gdkcolor.red = (((color >> 24) & 0xFF) * 65535) / 255; gdkcolor.green = (((color >> 16) & 0xFF) * 65535) / 255; gdkcolor.blue = (((color >> 8) & 0xFF) * 65535) / 255; gdk_gc_set_rgb_fg_color (gc, &gdkcolor); } /* GnomeCanvas draws in 512 pixel squares, allocate some extra for overlap */ #define STATIC_POINTS 544 static void swamigui_sample_canvas_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (item); GdkRectangle rect; if (!canvas->sample) return; /* set GC clipping rectangle */ rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; gdk_gc_set_clip_rectangle (canvas->peak_line_gc, &rect); gdk_gc_set_clip_rectangle (canvas->line_gc, &rect); gdk_gc_set_clip_rectangle (canvas->point_gc, &rect); gdk_gc_set_clip_rectangle (canvas->loop_start_gc, &rect); gdk_gc_set_clip_rectangle (canvas->loop_end_gc, &rect); if (canvas->loop_mode) swamigui_sample_canvas_draw_loop (canvas, drawable, x, y, width, height); else if (canvas->zoom <= 1.0) swamigui_sample_canvas_draw_points (canvas, drawable, x, y, width, height); else swamigui_sample_canvas_draw_segments(canvas, drawable, x, y, width, height); } /* loop mode display "connect the dots", zoom clamped <= 1.0. Overlaps display of start and end points of a loop */ static inline void swamigui_sample_canvas_draw_loop (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height) { gint16 *i16buf; int sample_ofs, sample_count, this_size; int hcenter, height_1, point_width = 0, h_point_width; int loop_index, start_index, end_index, start_ofs, end_ofs; int sample_size; double sample_mul; int xpos, ypos, i; GdkGC *gc; gboolean do_loopend_bool = TRUE; if (canvas->width < 6) return; /* hackish? */ sample_size = (int)canvas->sample_size; hcenter = canvas->width / 2 + canvas->x; /* horizontal center */ height_1 = canvas->height - 1; /* height - 1 */ sample_mul = height_1 / (double)65535.0; /* sample amplitude multiplier */ /* use larger squares for sample points? */ if (canvas->zoom < 1.0/6.0) point_width = 5; else if (canvas->zoom < 1.0/4.0) point_width = 3; h_point_width = point_width >> 1; /* calculate start and end offset, in sample points, from center to the left and right edges of the rectangular drawing area (-/+ 1 for tiling) */ start_ofs = (int)((x - hcenter) * canvas->zoom - 1); end_ofs = (int)((x + width - hcenter) * canvas->zoom + 1); /* do loop start point first, jump to 'do_loopend' label will occur for loop end point */ loop_index = canvas->loop_start; gc = canvas->loop_start_gc; do_loopend: /* jump label for end loop point run */ /* calculate loop point start/end sample indexes in view */ start_index = loop_index + start_ofs; end_index = loop_index + end_ofs; /* are there any sample points in view for loop start? */ if (start_index < sample_size && end_index >= 0) { start_index = CLAMP (start_index, 0, sample_size - 1); end_index = CLAMP (end_index, 0, sample_size - 1); sample_count = end_index - start_index + 1; this_size = canvas->max_frames; sample_ofs = start_index; while (sample_count > 0) { if (sample_count < this_size) this_size = sample_count; if (!(i16buf = ipatch_sample_handle_read (&canvas->handle, sample_ofs, this_size, NULL, NULL))) return; /* FIXME - Error reporting?? */ for (i = 0; i < this_size; i++, sample_ofs++) { /* calculate pixel offset from center */ xpos = (int)((sample_ofs - loop_index) / canvas->zoom + 0.5); xpos += hcenter - x; /* adjust to drawable coordinates */ /* calculate amplitude ypos */ ypos = (int)(height_1 - (((int)i16buf[i]) + 32768) * sample_mul - y + canvas->y); if (point_width) gdk_draw_rectangle (drawable, gc, TRUE, xpos - h_point_width, ypos - h_point_width, point_width, point_width); else gdk_draw_point (drawable, gc, xpos, ypos); } sample_count -= this_size; } } if (do_loopend_bool) { loop_index = canvas->loop_end; gc = canvas->loop_end_gc; do_loopend_bool = FALSE; goto do_loopend; } } /* "connect the dots" drawing for zooms <= 1.0 */ static inline void swamigui_sample_canvas_draw_points (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height) { GdkPoint static_points[STATIC_POINTS]; GdkPoint *points; int sample_start, sample_end, point_index; guint sample_count; int height_1; guint sample_ofs, size_left, this_size; double sample_mul; gint16 *i16buf; guint i; int sample_size = canvas->sample_size; /* calculate start sample (-1 for tiling) */ sample_start = (int)(canvas->start + x * canvas->zoom); /* calculate end sample (+1 for tiling) */ sample_end = (int)(canvas->start + ((x + width) * canvas->zoom) + 1); /* no samples in area? */ if (sample_start >= sample_size || sample_end < 0) return; sample_start = CLAMP (sample_start, 0, sample_size - 1); sample_end = CLAMP (sample_end, 0, sample_size - 1); sample_count = sample_end - sample_start + 1; height_1 = canvas->height - 1; /* height - 1 */ sample_mul = height_1 / (double)65535.0; /* sample amplitude multiplier */ /* use static point array if there is enough, otherwise fall back on malloc (shouldn't get used, but just in case) */ if (sample_count > STATIC_POINTS) points = g_new (GdkPoint, sample_count); else points = static_points; sample_ofs = sample_start; size_left = sample_count; this_size = canvas->max_frames; point_index = 0; while (size_left > 0) { if (size_left < this_size) this_size = size_left; if (!(i16buf = ipatch_sample_handle_read (&canvas->handle, sample_ofs, this_size, NULL, NULL))) { if (points != static_points) g_free (points); return; /* FIXME - Error reporting?? */ } /* loop over each sample */ for (i = 0; i < this_size; i++, sample_ofs++, point_index++) { /* calculate xpos */ points[point_index].x = (int)((sample_ofs - canvas->start) / canvas->zoom + 0.5) - x + canvas->x; /* calculate amplitude ypos */ points[point_index].y = (gint)(height_1 - (((int)i16buf[i]) + 32768) * sample_mul - y + canvas->y); } size_left -= this_size; } /* draw the connected lines between points */ gdk_draw_lines (drawable, canvas->line_gc, points, sample_count); if (canvas->zoom < 1.0/4.0) /* use larger squares for points? */ { int w, half_w; if (canvas->zoom < 1.0/6.0) w = 5; else w = 3; half_w = w >> 1; for (i = 0; i < sample_count; i++) gdk_draw_rectangle (drawable, canvas->point_gc, TRUE, points[i].x - half_w, points[i].y - half_w, w, w); } /* just use pixels for dots */ else gdk_draw_points (drawable, canvas->point_gc, points, sample_count); if (points != static_points) g_free (points); } /* peak line segment drawing for zooms > 1.0 */ static inline void swamigui_sample_canvas_draw_segments (SwamiguiSampleCanvas *canvas, GdkDrawable *drawable, int x, int y, int width, int height) { GdkSegment static_segments[STATIC_POINTS]; GdkSegment *segments; int sample_start, sample_end, sample_count, sample_ofs; int segment_index, next_index; int height_1; guint size_left, this_size; double sample_mul; gint16 *i16buf; gint16 min, max; guint i; int sample_size = canvas->sample_size; /* calculate start sample */ sample_start = (int)(canvas->start + x * canvas->zoom + 0.5); /* calculate end sample */ sample_end = (int)(canvas->start + (x + width) * canvas->zoom + 0.5); /* no samples in area? */ if (sample_start >= sample_size || sample_end < 0) return; sample_start = CLAMP (sample_start, 0, sample_size - 1); sample_end = CLAMP (sample_end, 0, sample_size - 1); sample_count = sample_end - sample_start + 1; height_1 = canvas->height - 1; /* height - 1 */ sample_mul = height_1 / (double)65535.0; /* sample amplitude multiplier */ /* use static point array if there is enough, otherwise fall back on malloc (shouldn't get used, but just in case) */ if (width > STATIC_POINTS) segments = g_new (GdkSegment, width); else segments = static_segments; sample_ofs = sample_start; size_left = sample_count; this_size = canvas->max_frames; segment_index = 0; min = max = 0; next_index = (int)(canvas->start + (x + 1) * canvas->zoom + 0.5); while (size_left > 0) { if (size_left < this_size) this_size = size_left; if (!(i16buf = ipatch_sample_handle_read (&canvas->handle, sample_ofs, this_size, NULL, NULL))) { if (segments != static_segments) g_free (segments); return; /* FIXME - Error reporting?? */ } /* look for min/max sample values */ for (i = 0; i < this_size; i++, sample_ofs++) { if (sample_ofs >= next_index) { segments[segment_index].x1 = segment_index + canvas->x; segments[segment_index].x2 = segments[segment_index].x1; segments[segment_index].y1 = (gint)(height_1 - (((int)max) + 32768) * sample_mul - y + canvas->y); segments[segment_index].y2 = (gint)(height_1 - (((int)min) + 32768) * sample_mul - y + canvas->y); min = max = 0; segment_index++; next_index = (int)(canvas->start + (x + segment_index + 1) * canvas->zoom + 0.5); } if (i16buf[i] < min) min = i16buf[i]; if (i16buf[i] > max) max = i16buf[i]; } size_left -= this_size; } gdk_draw_segments (drawable, canvas->peak_line_gc, segments, segment_index); if (segments != static_segments) g_free (segments); } static double swamigui_sample_canvas_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (item); double points[2*4]; points[0] = canvas->x; points[1] = canvas->y; points[2] = canvas->x + canvas->width; points[3] = points[1]; points[4] = points[0]; points[5] = canvas->y + canvas->height; points[6] = points[2]; points[7] = points[5]; *actual_item = item; return (gnome_canvas_polygon_to_point (points, 4, cx, cy)); } static void swamigui_sample_canvas_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (item); *x1 = canvas->x; *y1 = canvas->y; *x2 = canvas->x + canvas->width; *y2 = canvas->y + canvas->height; } static void swamigui_sample_canvas_cb_adjustment_value_changed (GtkAdjustment *adj, gpointer user_data) { SwamiguiSampleCanvas *canvas = SWAMIGUI_SAMPLE_CANVAS (user_data); guint start; gboolean update_adj; update_adj = canvas->update_adj; /* store old value of update adjustment */ canvas->update_adj = FALSE; /* disable adjustment updates to stop adjustment loop */ start = (guint)adj->value; g_object_set (canvas, "start", start, NULL); canvas->update_adj = update_adj; /* restore update adjustment setting */ } /** * swamigui_sample_canvas_set_sample: * @canvas: Sample data canvas item * @sample: Sample data to assign to the canvas * * Set the sample data source of a sample canvas item. */ void swamigui_sample_canvas_set_sample (SwamiguiSampleCanvas *canvas, IpatchSampleData *sample) { if (swamigui_sample_canvas_real_set_sample (canvas, sample)) g_object_notify (G_OBJECT (canvas), "sample"); } /* the real set sample function, returns TRUE if changed, FALSE otherwise */ static gboolean swamigui_sample_canvas_real_set_sample (SwamiguiSampleCanvas *canvas, IpatchSampleData *sample) { GError *err = NULL; int channel_map; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_CANVAS (canvas), FALSE); g_return_val_if_fail (!sample || IPATCH_IS_SAMPLE_DATA (sample), FALSE); if (sample == canvas->sample) return (FALSE); /* close previous source and unref sample */ if (canvas->sample) { ipatch_sample_handle_close (&canvas->handle); g_object_unref (canvas->sample); } canvas->sample = NULL; if (sample) { g_object_get (sample, "sample-size", &canvas->sample_size, NULL); /* use right channel of stereo if right_chan is set (and stereo data) */ if (canvas->right_chan && IPATCH_SAMPLE_FORMAT_GET_CHANNELS (ipatch_sample_data_get_native_format (sample)) == IPATCH_SAMPLE_STEREO) channel_map = IPATCH_SAMPLE_MAP_CHANNEL (0, IPATCH_SAMPLE_RIGHT); else channel_map = IPATCH_SAMPLE_MAP_CHANNEL (0, IPATCH_SAMPLE_LEFT); if (!ipatch_sample_data_open_cache_sample (sample, &canvas->handle, SAMPLE_FORMAT, channel_map, &err)) { g_critical (_("Error opening cached sample data in sample canvas: %s"), ipatch_gerror_message (err)); g_error_free (err); return (FALSE); } canvas->sample = g_object_ref (sample); /* ++ ref sample for canvas */ canvas->max_frames = ipatch_sample_handle_get_max_frames (&canvas->handle); } else canvas->sample_size = 0; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (canvas)); return (TRUE); } static void swamigui_sample_canvas_update_adjustment (SwamiguiSampleCanvas *canvas) { canvas->adj->lower = 0.0; canvas->adj->upper = canvas->sample_size; canvas->adj->value = 0.0; canvas->adj->step_increment = canvas->sample_size / 400.0; canvas->adj->page_increment = canvas->sample_size / 50.0; canvas->adj->page_size = canvas->sample_size; gtk_adjustment_changed (canvas->adj); g_signal_handlers_block_by_func (canvas->adj, swamigui_sample_canvas_cb_adjustment_value_changed, canvas); gtk_adjustment_value_changed (canvas->adj); g_signal_handlers_unblock_by_func (canvas->adj, swamigui_sample_canvas_cb_adjustment_value_changed, canvas); } /** * swamigui_sample_canvas_xpos_to_sample: * @canvas: Sample canvas item * @xpos: X pixel position * @onsample: Output: Pointer to store value indicating if given @xpos is * within sample (0 if within sample, -1 if less than 0 or no active sample, * 1 if off the end, 2 if last value after sample - useful for loop end * which is valid up to the position following the data), %NULL to ignore * * Convert an X pixel position to sample index. * * Returns: Sample index, index may be out of range of sample, use @onsample * parameter to determine that. */ int swamigui_sample_canvas_xpos_to_sample (SwamiguiSampleCanvas *canvas, int xpos, int *onsample) { int index,sample_size; if (onsample) *onsample = -1; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_CANVAS (canvas), 0); index = (int)(canvas->start + canvas->zoom * xpos); sample_size = canvas->sample_size; if (onsample && canvas->sample) { if (index < 0) *onsample = -1; else if (index > sample_size) *onsample = 1; else if (index == sample_size) *onsample = 2; else *onsample = 0; } return (index); } /** * swamigui_sample_canvas_sample_to_xpos: * @canvas: Sample canvas item * @index: Sample index * @inview: Output: Pointer to store value indicating if given sample @index * is in view (0 if in view, -1 if too low, 1 if too high). * * Convert a sample index to x pixel position. * * Returns: X position. Note that values outside of current view may be * returned (including negative numbers), @inview can be used to determine * if value is in view or not. */ int swamigui_sample_canvas_sample_to_xpos (SwamiguiSampleCanvas *canvas, int index, int *inview) { int xpos, start; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_CANVAS (canvas), 0); start = canvas->start; xpos = (int)((index - start) / canvas->zoom + 0.5); if (inview) { /* set inview output parameter as appropriate */ if (index < start) *inview = -1; else if (xpos >= canvas->width) *inview = 1; else *inview = 0; } return (xpos); } swami-2.2.0/src/swamigui/SwamiguiSampleCanvas.h000066400000000000000000000074011361104770400215100ustar00rootroot00000000000000/* * SwamiguiSampleCanvas.h - Sample data canvas item * A canvas item for sample data * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SAMPLE_CANVAS_H__ #define __SWAMIGUI_SAMPLE_CANVAS_H__ #include #include #include typedef struct _SwamiguiSampleCanvas SwamiguiSampleCanvas; typedef struct _SwamiguiSampleCanvasClass SwamiguiSampleCanvasClass; #define SWAMIGUI_TYPE_SAMPLE_CANVAS (swamigui_sample_canvas_get_type ()) #define SWAMIGUI_SAMPLE_CANVAS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_SAMPLE_CANVAS, \ SwamiguiSampleCanvas)) #define SWAMIGUI_SAMPLE_CANVAS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SAMPLE_CANVAS, \ SwamiguiSampleCanvasClass)) #define SWAMIGUI_IS_SAMPLE_CANVAS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_SAMPLE_CANVAS)) #define SWAMIGUI_IS_SAMPLE_CANVAS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_SAMPLE_CANVAS)) /* Swami Sample Object */ struct _SwamiguiSampleCanvas { GnomeCanvasItem parent_instance; /*< private >*/ IpatchSampleData *sample; /* sample being displayed */ guint sample_size; /* cached size of sample */ IpatchSampleHandle handle; /* cached sample handle */ gboolean right_chan; /* use right channel of stereo audio? */ guint max_frames; /* max sample frames that can be converted at at time */ gboolean loop_mode; /* display loop mode? */ guint loop_start, loop_end; /* cached loop start and end in samples */ GtkAdjustment *adj; /* adjustment for view */ gboolean update_adj; /* adjustment values should be updated? */ guint need_bbox_update : 1; /* set if bbox need to be updated */ guint start; /* start sample (not used in LOOP mode) */ double zoom; /* zoom factor samples/pixel */ double zoom_ampl; /* amplitude zoom factor */ int x, y; /* x, y coordinates of sample item */ int width, height; /* width and height in pixels */ GdkGC *peak_line_gc; /* GC for drawing vertical average lines */ GdkGC *line_gc; /* GC for drawing lines */ GdkGC *point_gc; /* GC for drawing sample points */ GdkGC *loop_start_gc; /* GC for looop start sample points */ GdkGC *loop_end_gc; /* GC for loop end sample points */ guint peak_line_color; /* color of peak sample lines */ guint line_color; /* color of connecting sample lines */ guint point_color; /* color of sample points */ guint loop_start_color; /* color of sample points for start of loop */ guint loop_end_color; /* color of sample points for end of loop */ }; struct _SwamiguiSampleCanvasClass { GnomeCanvasItemClass parent_class; }; GType swamigui_sample_canvas_get_type (void); void swamigui_sample_canvas_set_sample (SwamiguiSampleCanvas *canvas, IpatchSampleData *sample); int swamigui_sample_canvas_xpos_to_sample (SwamiguiSampleCanvas *canvas, int xpos, int *onsample); int swamigui_sample_canvas_sample_to_xpos (SwamiguiSampleCanvas *canvas, int index, int *inview); #endif swami-2.2.0/src/swamigui/SwamiguiSampleEditor.c000066400000000000000000002642311361104770400215240ustar00rootroot00000000000000/* * SwamiguiSampleEditor.c - Sample editor widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include /* log_if_fail() */ #include "SwamiguiControl.h" #include "SwamiguiRoot.h" #include "SwamiguiSampleEditor.h" #include "SwamiguiSampleCanvas.h" #include "SwamiguiLoopFinder.h" #include "icons.h" #include "util.h" #include "i18n.h" /* number of markers that are built in (don't get removed) */ #define BUILTIN_MARKER_COUNT 3 /* value stored in editor->sel_state for current active selection state */ typedef enum { SEL_INACTIVE, /* no selection is in progress */ SEL_MAYBE, /* a mouse click occurred by not moved enough yet */ SEL_START, /* actively changing selection from start edge */ SEL_END /* actively changing selection from end edge */ } SelState; typedef struct { IpatchSampleData *sample; gboolean right_chan; /* set to TRUE for stereo to use right channel */ GnomeCanvasItem *sample_view; GnomeCanvasItem *loop_view; GnomeCanvasItem *sample_center_line; /* sample view horizonal center line */ GnomeCanvasItem *loop_center_line; /* loop view horizontal center line */ } TrackInfo; typedef struct { guint flags; /* SwamiguiSampleEditorMarkerFlags */ gboolean visible; /* TRUE if visible, FALSE otherwise */ SwamiControl *start_ctrl; /* marker start control */ SwamiControl *end_ctrl; /* marker end control (range only) */ GnomeCanvasItem *start_line; /* marker start line canvas item */ GnomeCanvasItem *end_line; /* marker end line canvas item (range only) */ GnomeCanvasItem *range_box; /* marker range box (range only) */ guint start_pos; /* sample start position of marker */ guint end_pos; /* sample end position of marker (range only) */ SwamiguiSampleEditor *editor; /* so we can pass MarkerInfo to callbacks */ } MarkerInfo; /* min zoom value for sample and loop canvas (samples/pixel) */ #define SAMPLE_CANVAS_MIN_ZOOM 0.02 #define LOOP_CANVAS_MIN_ZOOM 0.02 #define LOOP_CANVAS_DEF_ZOOM 0.2 /* default zoom for loop canvas */ /* default colors */ #define DEFAULT_CENTER_LINE_COLOR 0x7F00FFFF #define DEFAULT_MARKER_BORDER_COLOR 0xBBBBBBFF #define DEFAULT_SNAP_LINE_COLOR 0xFF0000FF #define DEFAULT_LOOP_LINE_COLOR 0x777777FF /* default marker colors */ guint default_marker_colors[] = { GNOME_CANVAS_COLOR (0xC2, 0xFF, 0xB6), /* selection marker */ GNOME_CANVAS_COLOR (0xB5, 0x10, 0xFF), /* loop finder start marker */ GNOME_CANVAS_COLOR (0xFF, 0x10, 0x70), /* loop finder end marker */ GNOME_CANVAS_COLOR (0x21, 0xED, 0x3D), /* loop (handler defined) */ GNOME_CANVAS_COLOR (0xFF, 0xEE, 0x10), GNOME_CANVAS_COLOR (0xFF, 0x30, 0x10), GNOME_CANVAS_COLOR (0x10, 0xC4, 0xFF), GNOME_CANVAS_COLOR (0x10, 0xFF, 0x7B) }; /* default height of marker bar */ #define DEFAULT_MARKER_BAR_HEIGHT 24 /* colors are in RRGGBBAA */ #define MARKER_DEFAULT_COLOR 0x00FF00FF /* interactive marker color */ #define MARKER_NONIACTV_COLOR 0x666666FF /* non-interactive marker color */ #define MARKER_DEFAULT_WIDTH 1 /* width of markers (in pixels) */ #define MARKER_CLICK_DISTANCE 3 /* click area surrounding markers (in pixels) */ enum { PROP_0, PROP_ITEM_SELECTION, PROP_BORDER_WIDTH, PROP_MARKER_BAR_HEIGHT }; static void swamigui_sample_editor_class_init (SwamiguiSampleEditorClass *klass); static void swamigui_sample_editor_panel_iface_init (SwamiguiPanelIface *panel_iface); static gboolean swamigui_sample_editor_panel_iface_check_selection (IpatchList *selection, GType *selection_types); static void swamigui_sample_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_sample_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_sample_editor_finalize (GObject *object); static void swamigui_sample_editor_init (SwamiguiSampleEditor *editor); //#define sample_edition /* Disabled until sample editing operations are implemented by #define sample_edition */ #ifdef sample_edition static void swamigui_sample_editor_cb_cut_sample (GtkToggleToolButton *button, gpointer user_data); static void swamigui_sample_editor_cb_crop_sample (GtkToggleToolButton *button, gpointer user_data); static void swamigui_sample_editor_cb_copy_new_sample (GtkToggleToolButton *button, gpointer user_data); static IpatchSampleData *get_selection_sample_data (SwamiguiSampleEditor *editor); #endif static void swamigui_sample_editor_cb_loop_finder (GtkToggleToolButton *button, gpointer user_data); static void editor_cb_pane_size_allocate (GtkWidget *pane, GtkAllocation *allocation, gpointer user_data); static void swamigui_sample_editor_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data); static void swamigui_sample_editor_update_sizes (SwamiguiSampleEditor *editor); static void swamigui_sample_editor_update_canvas_size (SwamiguiSampleEditor *editor, GnomeCanvas *canvas); static void swamigui_sample_editor_cb_scroll (GtkAdjustment *adj, gpointer user_data); static gboolean swamigui_sample_editor_cb_sample_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data); static gboolean swamigui_sample_editor_cb_loop_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data); static MarkerInfo * pos_is_marker (SwamiguiSampleEditor *editor, int xpos, int ypos, int *marker_edge, gboolean *is_onbox); static void swamigui_sample_editor_sample_mod_update (SwamiguiCanvasMod *mod, double xzoom, double yzoom, double xscroll, double yscroll, double xpos, double ypos, gpointer user_data); static void swamigui_sample_editor_sample_mod_snap (SwamiguiCanvasMod *mod, guint actions, double xsnap, double ysnap, gpointer user_data); static void swamigui_sample_editor_loop_mod_update (SwamiguiCanvasMod *mod, double xzoom, double yzoom, double xscroll, double yscroll, double xpos, double ypos, gpointer user_data); static void swamigui_sample_editor_loop_mod_snap (SwamiguiCanvasMod *mod, guint actions, double xsnap, double ysnap, gpointer user_data); static gboolean swamigui_sample_editor_real_set_selection (SwamiguiSampleEditor *editor, IpatchList *items); static void swamigui_sample_editor_deactivate_handler (SwamiguiSampleEditor *editor); static void swamigui_sample_editor_remove_track_item (SwamiguiSampleEditor *editor, GList *p, gboolean destroy); static void swamigui_sample_editor_set_marker_info (SwamiguiSampleEditor *editor, MarkerInfo *marker_info, guint start, guint end); static void swamigui_sample_editor_remove_marker_item (SwamiguiSampleEditor *editor, GList *p, gboolean destroy); static void start_marker_control_get_value_func (SwamiControl *control, GValue *value); static void start_marker_control_set_value_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void end_marker_control_get_value_func (SwamiControl *control, GValue *value); static void end_marker_control_set_value_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void marker_control_update (MarkerInfo *marker_info); static void marker_control_update_all (SwamiguiSampleEditor *editor); static gboolean swamigui_sample_editor_default_handler (SwamiguiSampleEditor *editor); static gboolean swamigui_sample_editor_default_handler_check_func (IpatchList *selection, GType *selection_types); static void swamigui_sample_editor_loopsel_ctrl_get (SwamiControl *control, GValue *value); static void swamigui_sample_editor_loopsel_ctrl_set (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_sample_editor_loopsel_cb_changed (GtkComboBox *widget, gpointer user_data); static GObjectClass *parent_class = NULL; /* list of SwamiguiSampleEditorHandlers */ static GList *sample_editor_handlers = NULL; /* list of SwamiguiPanelCheckFunc corresponding to sample_editor_handlers */ static GList *sample_editor_check_funcs = NULL; /* loop type info for IpatchSampleLoopType enum GtkComboBox */ struct { int loop_type; char *icon; char *label; char *tooltip; } loop_type_info[] = { { IPATCH_SAMPLE_LOOP_NONE, SWAMIGUI_STOCK_LOOP_NONE, N_("None"), N_("No looping") }, { IPATCH_SAMPLE_LOOP_STANDARD, SWAMIGUI_STOCK_LOOP_STANDARD, N_("Standard"), N_("Standard continuous loop") }, { IPATCH_SAMPLE_LOOP_RELEASE, SWAMIGUI_STOCK_LOOP_RELEASE, N_("Release"), N_("Loop until key is released then play to end of sample") }, { IPATCH_SAMPLE_LOOP_PINGPONG, SWAMIGUI_STOCK_LOOP_STANDARD, /* FIXME */ N_("Ping Pong"), N_("Alternate playing loop forwards and backwards") } }; /* columns from GtkListStore model for loop selector GtkComboBox */ enum { LOOPSEL_COL_LOOP_TYPE, LOOPSEL_COL_ICON, LOOPSEL_COL_LABEL, LOOPSEL_COL_TOOLTIP, LOOPSEL_COL_COUNT }; GType swamigui_sample_editor_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiSampleEditorClass), NULL, NULL, (GClassInitFunc) swamigui_sample_editor_class_init, NULL, NULL, sizeof (SwamiguiSampleEditor), 0, (GInstanceInitFunc) swamigui_sample_editor_init, }; static const GInterfaceInfo panel_info = { (GInterfaceInitFunc)swamigui_sample_editor_panel_iface_init, NULL, NULL }; obj_type = g_type_register_static (GTK_TYPE_HBOX, "SwamiguiSampleEditor", &obj_info, 0); g_type_add_interface_static (obj_type, SWAMIGUI_TYPE_PANEL, &panel_info); /* register default sample editor handler */ swamigui_sample_editor_register_handler (swamigui_sample_editor_default_handler, swamigui_sample_editor_default_handler_check_func); } return (obj_type); } static void swamigui_sample_editor_class_init (SwamiguiSampleEditorClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_sample_editor_set_property; obj_class->get_property = swamigui_sample_editor_get_property; obj_class->finalize = swamigui_sample_editor_finalize; g_object_class_override_property (obj_class, PROP_ITEM_SELECTION, "item-selection"); g_object_class_install_property (obj_class, PROP_MARKER_BAR_HEIGHT, g_param_spec_int ("marker-bar-height", _("Marker bar height"), _("Height of marker and loop meter bar"), 0, 100, DEFAULT_MARKER_BAR_HEIGHT, G_PARAM_READWRITE)); } static void swamigui_sample_editor_panel_iface_init (SwamiguiPanelIface *panel_iface) { panel_iface->label = _("Sample Editor"); panel_iface->blurb = _("Sample data and loop editor"); panel_iface->stockid = SWAMIGUI_STOCK_SAMPLE_VIEWER; panel_iface->check_selection = swamigui_sample_editor_panel_iface_check_selection; } static gboolean swamigui_sample_editor_panel_iface_check_selection (IpatchList *selection, GType *selection_types) { GList *p; /* loop over handler check functions */ for (p = sample_editor_check_funcs; p; p = p->next) { if (((SwamiguiPanelCheckFunc)(p->data))(selection, selection_types)) return (TRUE); } return (FALSE); } static void swamigui_sample_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (object); IpatchList *list; int i; switch (property_id) { case PROP_ITEM_SELECTION: list = g_value_get_object (value); swamigui_sample_editor_real_set_selection (editor, list); break; case PROP_MARKER_BAR_HEIGHT: i = g_value_get_int (value); if (i != editor->marker_bar_height) { editor->marker_bar_height = i; swamigui_sample_editor_update_sizes (editor); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_sample_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, editor->selection); break; case PROP_MARKER_BAR_HEIGHT: g_value_set_int (value, editor->marker_bar_height); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_sample_editor_finalize (GObject *object) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (object); GList *p, *temp; /* remove all markers (don't destroy GtkObjects though) */ p = editor->markers; while (p) { temp = p; p = g_list_next (p); swamigui_sample_editor_remove_marker_item (editor, temp, FALSE); } /* remove all tracks (don't destroy GtkObjects though) */ p = editor->tracks; while (p) { temp = p; p = g_list_next (p); swamigui_sample_editor_remove_track_item (editor, temp, FALSE); } if (editor->selection) g_object_unref (editor->selection); if (editor->loop_start_hub) g_object_unref (editor->loop_start_hub); if (editor->loop_end_hub) g_object_unref (editor->loop_end_hub); if (editor->loopsel_ctrl) g_object_unref (editor->loopsel_ctrl); if (editor->loopsel_store) g_object_unref (editor->loopsel_store); editor->selection = NULL; editor->loop_start_hub = NULL; editor->loop_end_hub = NULL; editor->loopsel_ctrl = NULL; editor->loopsel_store = NULL; /* remove ref, necessary since pane is added/removed from container */ g_object_unref (editor->loop_finder_pane); if (parent_class->finalize) (*parent_class->finalize)(object); } static void swamigui_sample_editor_init (SwamiguiSampleEditor *editor) { GtkWidget *vbox, *hbox; GtkWidget *frame; GtkWidget *pane; GtkWidget *image; GtkToolItem *item; GtkStyle *style; GtkCellRenderer *renderer; GtkTooltips *tips; SwamiLoopFinder *loop_finder; SwamiControl *marker_start, *marker_end; int id; editor->selection = NULL; editor->marker_bar_height = DEFAULT_MARKER_BAR_HEIGHT; editor->sel_marker = -1; editor->zoom_all = TRUE; editor->sel_state = SEL_INACTIVE; editor->center_line_color = DEFAULT_CENTER_LINE_COLOR; editor->marker_border_color = DEFAULT_MARKER_BORDER_COLOR; editor->snap_line_color = DEFAULT_SNAP_LINE_COLOR; editor->loop_line_color = DEFAULT_LOOP_LINE_COLOR; editor->loop_zoom = LOOP_CANVAS_DEF_ZOOM; /* create zoom/scroll modulator for sample canvas */ editor->sample_mod = swamigui_canvas_mod_new (); /* attach to "update" and "snap" signals */ g_signal_connect (editor->sample_mod, "update", G_CALLBACK (swamigui_sample_editor_sample_mod_update), editor); g_signal_connect (editor->sample_mod, "snap", G_CALLBACK (swamigui_sample_editor_sample_mod_snap), editor); /* create zoom modulator for loop canvas */ editor->loop_mod = swamigui_canvas_mod_new (); /* attach to "update" and "snap" signals */ g_signal_connect (editor->loop_mod, "update", G_CALLBACK (swamigui_sample_editor_loop_mod_update), editor); g_signal_connect (editor->loop_mod, "snap", G_CALLBACK (swamigui_sample_editor_loop_mod_snap), editor); /* create control hubs to control sample loop views for all tracks */ /* editor retains holds references on controls */ editor->loop_start_hub = SWAMI_CONTROL (swami_control_hub_new ()); /* ++ ref */ swamigui_control_set_queue (editor->loop_start_hub); editor->loop_end_hub = SWAMI_CONTROL (swami_control_hub_new ()); /* ++ ref */ swamigui_control_set_queue (editor->loop_end_hub); tips = gtk_tooltips_new (); /* create loop finder widget and pane, only gets packed when active */ editor->loop_finder_gui = SWAMIGUI_LOOP_FINDER (swamigui_loop_finder_new ()); editor->loop_finder_pane = gtk_hpaned_new (); gtk_paned_pack1 (GTK_PANED (editor->loop_finder_pane), GTK_WIDGET (editor->loop_finder_gui), FALSE, TRUE); gtk_widget_show_all (editor->loop_finder_pane); /* we add an extra ref to finder hpane since it will be added/removed as needed */ g_object_ref (editor->loop_finder_pane); /* create main vbox for tool bar and sample canvases and pack in editor hbox */ editor->mainvbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (editor), editor->mainvbox, TRUE, TRUE, 0); /* create toolbox area horizontal box */ editor->toolbar = gtk_toolbar_new (); editor->loopsel_store /* ++ ref - editor holds reference to loopsel_store */ = gtk_list_store_new (LOOPSEL_COL_COUNT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); editor->loopsel = gtk_combo_box_new_with_model (GTK_TREE_MODEL (editor->loopsel_store)); gtk_widget_set_sensitive (editor->loopsel, FALSE); item = gtk_tool_item_new (); gtk_container_add (GTK_CONTAINER (item), editor->loopsel); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), item, -1); /* create the control for the loop type selector */ /* ++ ref - editor holds reference to it */ editor->loopsel_ctrl = SWAMI_CONTROL (swami_control_func_new ()); /* attach to the changed signal of the combo box */ g_signal_connect (editor->loopsel, "changed", G_CALLBACK (swamigui_sample_editor_loopsel_cb_changed), editor); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (editor->loopsel_ctrl), swamigui_sample_editor_loopsel_ctrl_get, swamigui_sample_editor_loopsel_ctrl_set, NULL, editor); renderer = gtk_cell_renderer_pixbuf_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editor->loopsel), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (editor->loopsel), renderer, "stock-id", LOOPSEL_COL_ICON, NULL); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (editor->loopsel), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (editor->loopsel), renderer, "text", LOOPSEL_COL_LABEL, NULL); /* create spin button and spin control */ editor->spinbtn_start = gtk_spin_button_new (NULL, 1.0, 0); gtk_spin_button_set_range (GTK_SPIN_BUTTON (editor->spinbtn_start), 0.0, (gdouble)G_MAXINT); gtk_tooltips_set_tip (tips, editor->spinbtn_start, _("Set loop start position"), NULL); editor->spinbtn_start_ctrl = swamigui_control_new_for_widget_full (G_OBJECT (editor->spinbtn_start), G_TYPE_UINT, NULL, 0); item = gtk_tool_item_new (); gtk_container_add (GTK_CONTAINER (item), editor->spinbtn_start); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), item, -1); /* spin button and spin control */ editor->spinbtn_end = gtk_spin_button_new (NULL, 1.0, 0); gtk_spin_button_set_range (GTK_SPIN_BUTTON (editor->spinbtn_end), 0.0, (gdouble)G_MAXINT); gtk_tooltips_set_tip (tips, editor->spinbtn_end, _("Set loop end position"), NULL); editor->spinbtn_end_ctrl = swamigui_control_new_for_widget_full (G_OBJECT (editor->spinbtn_end), G_TYPE_UINT, NULL, 0); item = gtk_tool_item_new (); gtk_container_add (GTK_CONTAINER (item), editor->spinbtn_end); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), item, -1); /* Disabled until sample editing operations are implemented by #define sample_edition */ #ifdef sample_edition /* add separator */ gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), gtk_separator_tool_item_new (), -1); /* create cut button */ image = gtk_image_new_from_stock (GTK_STOCK_CUT, GTK_ICON_SIZE_SMALL_TOOLBAR); editor->cut_button = GTK_WIDGET (gtk_tool_button_new (image, _("Cut"))); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (editor->cut_button), tips, _("Cut selected sample data"), NULL); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), GTK_TOOL_ITEM (editor->cut_button), -1); g_signal_connect (editor->cut_button, "clicked", G_CALLBACK (swamigui_sample_editor_cb_cut_sample), editor); /* create crop button */ image = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_SMALL_TOOLBAR); editor->crop_button = GTK_WIDGET (gtk_tool_button_new (image, _("Crop"))); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (editor->crop_button), tips, _("Crop to current selection"), NULL); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), GTK_TOOL_ITEM (editor->crop_button), -1); g_signal_connect (editor->crop_button, "clicked", G_CALLBACK (swamigui_sample_editor_cb_crop_sample), editor); /* create copy to new button */ image = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_SMALL_TOOLBAR); editor->copy_new_button = GTK_WIDGET (gtk_tool_button_new (image, _("Copy to new"))); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (editor->copy_new_button), tips, _("Copy selection to new sample"), NULL); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), GTK_TOOL_ITEM (editor->copy_new_button), -1); g_signal_connect (editor->copy_new_button, "clicked", G_CALLBACK (swamigui_sample_editor_cb_copy_new_sample), editor); /* create sample selector button */ editor->samplesel_button = GTK_WIDGET (gtk_toggle_tool_button_new ()); gtk_tool_button_set_label (GTK_TOOL_BUTTON (editor->samplesel_button), _("Sample selector")); image = gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (editor->samplesel_button), image); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (editor->samplesel_button), tips, _("Make selection the entire sample"), NULL); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), GTK_TOOL_ITEM (editor->samplesel_button), -1); #endif /* add separator */ gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), gtk_separator_tool_item_new (), -1); /* create loop finder button */ editor->finder_button = GTK_WIDGET (gtk_toggle_tool_button_new ()); gtk_tool_button_set_label (GTK_TOOL_BUTTON (editor->finder_button), _("Find loops")); image = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (editor->finder_button), image); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (editor->finder_button), tips, _("Loop point finder"), NULL); gtk_toolbar_insert (GTK_TOOLBAR (editor->toolbar), GTK_TOOL_ITEM (editor->finder_button), -1); g_signal_connect (editor->finder_button, "toggled", G_CALLBACK (swamigui_sample_editor_cb_loop_finder), editor); gtk_box_pack_start (GTK_BOX (editor->mainvbox), editor->toolbar, FALSE, FALSE, 0); /* lower inset frame for sample viewer */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 0); gtk_box_pack_start (GTK_BOX (editor->mainvbox), frame, TRUE, TRUE, 0); /* vbox within frame to put sample canvas */ vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); pane = gtk_hpaned_new (); gtk_box_pack_start (GTK_BOX (hbox), pane, TRUE, TRUE, 0); /* connect to the size-allocate signal so we can initialize the gutter pos */ g_signal_connect_after (pane, "size-allocate", G_CALLBACK (editor_cb_pane_size_allocate), NULL); /* create sample editor canvas */ editor->sample_canvas = GNOME_CANVAS (gnome_canvas_new ()); gnome_canvas_set_center_scroll_region (GNOME_CANVAS (editor->sample_canvas), FALSE); gtk_paned_pack1 (GTK_PANED (pane), GTK_WIDGET (editor->sample_canvas), TRUE, TRUE); g_signal_connect (editor->sample_canvas, "event", G_CALLBACK (swamigui_sample_editor_cb_sample_canvas_event), editor); g_signal_connect (editor->sample_canvas, "size-allocate", G_CALLBACK (swamigui_sample_editor_cb_canvas_size_allocate), editor); /* change background color of canvas to black */ style = gtk_style_copy (gtk_widget_get_style (GTK_WIDGET (editor->sample_canvas))); style->bg[GTK_STATE_NORMAL] = style->black; gtk_widget_set_style (GTK_WIDGET (editor->sample_canvas), style); /* create sample view marker bar border horizontal line */ editor->sample_border_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->marker_border_color, "width-pixels", 1, NULL); /* create snap lines */ editor->xsnap_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->snap_line_color, "width-pixels", 2, NULL); gnome_canvas_item_hide (editor->xsnap_line); editor->ysnap_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->snap_line_color, "width-pixels", 2, NULL); gnome_canvas_item_hide (editor->ysnap_line); /* create sample loop canvas */ editor->loop_canvas = GNOME_CANVAS (gnome_canvas_new ()); gnome_canvas_set_center_scroll_region (GNOME_CANVAS (editor->loop_canvas), FALSE); gtk_paned_pack2 (GTK_PANED (pane), GTK_WIDGET (editor->loop_canvas), FALSE, TRUE); g_signal_connect (editor->loop_canvas, "event", G_CALLBACK (swamigui_sample_editor_cb_loop_canvas_event), editor); g_signal_connect (editor->loop_canvas, "size-allocate", G_CALLBACK (swamigui_sample_editor_cb_canvas_size_allocate), editor); /* change background color of canvas to black */ style = gtk_style_copy (gtk_widget_get_style (GTK_WIDGET (editor->loop_canvas))); style->bg[GTK_STATE_NORMAL] = style->black; gtk_widget_set_style (GTK_WIDGET (editor->loop_canvas), style); /* create vertical center line for loop view */ editor->loop_line = gnome_canvas_item_new (gnome_canvas_root (editor->loop_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->loop_line_color, "width-pixels", 1, NULL); /* create loop view marker bar border horizontal line */ editor->loop_border_line = gnome_canvas_item_new (gnome_canvas_root (editor->loop_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->marker_border_color, "width-pixels", 1, NULL); /* create zoom snap line for loop view */ editor->loop_snap_line = gnome_canvas_item_new (gnome_canvas_root (editor->loop_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->snap_line_color, "width-pixels", 2, NULL); gnome_canvas_item_hide (editor->loop_snap_line); /* attach a horizontal scrollbar to the sample viewer */ editor->hscrollbar = gtk_hscrollbar_new (NULL); gtk_box_pack_start (GTK_BOX (editor->mainvbox), editor->hscrollbar, FALSE, FALSE, 0); g_signal_connect_after (gtk_range_get_adjustment (GTK_RANGE (editor->hscrollbar)), "value-changed", G_CALLBACK (swamigui_sample_editor_cb_scroll), editor); gtk_widget_show_all (editor->mainvbox); /* create marker 0 for selection and hide it */ id = swamigui_sample_editor_add_marker (editor, 0, NULL, NULL); swamigui_sample_editor_show_marker (editor, id, FALSE); loop_finder = editor->loop_finder_gui->loop_finder; /* create marker for loop finder start search window and hide it */ id = swamigui_sample_editor_add_marker (editor, 0, &marker_start, &marker_end); swamigui_sample_editor_show_marker (editor, id, FALSE); /* connect the marker controls to the loop finder properties */ swami_control_prop_connect_to_control (G_OBJECT (loop_finder), "window1-start", marker_start, SWAMI_CONTROL_CONN_BIDIR_INIT); swami_control_prop_connect_to_control (G_OBJECT (loop_finder), "window1-end", marker_end, SWAMI_CONTROL_CONN_BIDIR_INIT); /* create marker for loop finder end search window and hide it */ id = swamigui_sample_editor_add_marker (editor, 0, &marker_start, &marker_end); swamigui_sample_editor_show_marker (editor, id, FALSE); /* connect the marker controls to the loop finder properties */ swami_control_prop_connect_to_control (G_OBJECT (loop_finder), "window2-start", marker_start, SWAMI_CONTROL_CONN_BIDIR_INIT); swami_control_prop_connect_to_control (G_OBJECT (loop_finder), "window2-end", marker_end, SWAMI_CONTROL_CONN_BIDIR_INIT); } /* Disabled until sample editing operations are implemented by #define sample_edition */ #ifdef sample_edition static void swamigui_sample_editor_cb_cut_sample (GtkToggleToolButton *button, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); MarkerInfo *marker; IpatchSampleData *data; IpatchSampleStore *store; /* shouldn't happen, but.. */ if (!editor->markers || !editor->markers->data) return; /* first marker is selection */ marker = (MarkerInfo *)(editor->markers->data); if (!marker->visible) return; data = get_selection_sample_data (editor); /* ++ ref */ if (!data) return; store = ipatch_sample_data_get_default_store (data); /* ++ ref */ list = ipatch_sample_list_new (); ipatch_sample_list_append (list, store, 0, 0, 0); ipatch_sample_list_cut (list, marker->start_pos, marker->end_pos - marker->start_pos + 1); g_object_unref (data); /* -- unref */ } static void swamigui_sample_editor_cb_crop_sample (GtkToggleToolButton *button, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); IpatchSampleData *data; data = get_selection_sample_data (editor); if (!data) return; } static void swamigui_sample_editor_cb_copy_new_sample (GtkToggleToolButton *button, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); IpatchSampleData *data; data = get_selection_sample_data (editor); if (!data) return; } /* returns a IpatchSampleData object for the current selection or NULL if * not a single item which has an IpatchSample interface which allows * get/set of sample data. Caller owns ref to sample data */ static IpatchSampleData * get_selection_sample_data (SwamiguiSampleEditor *editor) { IpatchSample *sample; if (!editor->selection || !editor->selection->items || editor->selection->items->next || !IPATCH_IS_SAMPLE (editor->selection->items->data)) return (NULL); sample = IPATCH_SAMPLE (editor->selection->items->data); if (!ipatch_sample_has_data (sample)) return (NULL); return (ipatch_sample_get_data (sample)); /* !! caller takes reference */ } #endif static void swamigui_sample_editor_cb_loop_finder (GtkToggleToolButton *button, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); editor->loop_finder_active = gtk_toggle_tool_button_get_active (button); if (editor->loop_finder_active) { /* repack the main vbox into the loop finder pane */ g_object_ref (editor->mainvbox); gtk_container_remove (GTK_CONTAINER (editor), editor->mainvbox); gtk_paned_pack2 (GTK_PANED (editor->loop_finder_pane), editor->mainvbox, TRUE, TRUE); g_object_unref (editor->mainvbox); /* pack the loop finder pane into the editor widget */ gtk_box_pack_start (GTK_BOX (editor), editor->loop_finder_pane, TRUE, TRUE, 0); } else { /* remove the loop finder pane from the editor widget */ gtk_container_remove (GTK_CONTAINER (editor), editor->loop_finder_pane); /* repack the main vbox into the editor */ g_object_ref (editor->mainvbox); gtk_container_remove (GTK_CONTAINER (editor->loop_finder_pane), editor->mainvbox); gtk_box_pack_start (GTK_BOX (editor), editor->mainvbox, TRUE, TRUE, 0); g_object_unref (editor->mainvbox); } /* show/hide finder markers */ swamigui_sample_editor_show_marker (editor, SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_START, editor->loop_finder_active); swamigui_sample_editor_show_marker (editor, SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_END, editor->loop_finder_active); } /* size-allocate callback to initialize hpane gutter position */ static void editor_cb_pane_size_allocate (GtkWidget *pane, GtkAllocation *allocation, gpointer user_data) { int width; /* set loop view size to 1/5 total size or 160 pixels whichever is smallest */ width = allocation->width / 5; if (width > 160) width = 160; /* disconnect the from the signal (one time only init) */ g_signal_handlers_disconnect_by_func (pane, editor_cb_pane_size_allocate, user_data); gtk_paned_set_position (GTK_PANED (pane), allocation->width - width); } static void swamigui_sample_editor_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); GnomeCanvas *canvas = GNOME_CANVAS (widget); swamigui_sample_editor_update_canvas_size (editor, canvas); } static void swamigui_sample_editor_update_sizes (SwamiguiSampleEditor *editor) { swamigui_sample_editor_update_canvas_size (editor, editor->sample_canvas); swamigui_sample_editor_update_canvas_size (editor, editor->loop_canvas); } static void swamigui_sample_editor_update_canvas_size (SwamiguiSampleEditor *editor, GnomeCanvas *canvas) { TrackInfo *track_info; GnomeCanvasItem *item; int width, height, sample_height, count, y, y2, i; double zoom, fullzoom = 0.0; GList *p; width = GTK_WIDGET (canvas)->allocation.width; height = GTK_WIDGET (canvas)->allocation.height; sample_height = height - editor->marker_bar_height; if (width <= 0) width = 1; if (height <= 0) height = 1; if (sample_height <= 0) sample_height = 1; /* update marker bar border line */ item = canvas == editor->sample_canvas ? editor->sample_border_line : editor->loop_border_line; swamigui_util_canvas_line_set (item, 0, editor->marker_bar_height - 1, width, editor->marker_bar_height - 1); p = editor->tracks; if (!p) return; /* get count and last track in list */ count = 1; while (p->next) { p = g_list_next (p); count++; } /* if sample canvas, calculate full sample zoom and use it if current zoom * would cause complete sample view to be less than the canvas width */ if (canvas == editor->sample_canvas) { fullzoom = (double)(editor->sample_size) / width; track_info = (TrackInfo *)(p->data); g_object_get (track_info->sample_view, "zoom", &zoom, NULL); } /* loop in reverse over list (top sample down) */ for (i = 1, y = editor->marker_bar_height; p; p = g_list_previous (p), i++) { y2 = i * sample_height / count + editor->marker_bar_height; track_info = (TrackInfo *)(p->data); /* update data view */ item = (canvas == editor->sample_canvas) ? track_info->sample_view : track_info->loop_view; g_object_set (item, "y", y, "width", width, "height", y2 - y, NULL); /* set zoom value if current sample zoom would be less than canvas width, * or zoom_all is currently set */ if (canvas == editor->sample_canvas && (editor->zoom_all || fullzoom < zoom)) g_object_set (item, "zoom", fullzoom, NULL); /* update horizontal center line */ item = (canvas == editor->sample_canvas) ? track_info->sample_center_line : track_info->loop_center_line; swamigui_util_canvas_line_set (item, 0, y + (y2 - y) / 2, width - 1, y + (y2 - y) / 2); y = y2 + 1; } if (canvas == editor->sample_canvas) marker_control_update_all (editor); /* update all markers */ else if (canvas == editor->loop_canvas) swamigui_util_canvas_line_set (editor->loop_line, width / 2, 0, width / 2, height); } /* horizontal scroll bar value changed callback */ static void swamigui_sample_editor_cb_scroll (GtkAdjustment *adj, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); marker_control_update_all (editor); /* update all markers */ } static gboolean swamigui_sample_editor_cb_sample_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (data); SwamiguiSampleCanvas *sample_view; TrackInfo *track_info; MarkerInfo *marker_info; GdkEventButton *btn_event; GdkEventMotion *motion_event; GdkCursor *cursor; int val; guint newstart, newend; gboolean onbox; int sample_size = editor->sample_size; if (!editor->tracks) return (FALSE); track_info = (TrackInfo *)(editor->tracks->data); sample_view = SWAMIGUI_SAMPLE_CANVAS (track_info->sample_view); if (event->type == GDK_BUTTON_PRESS) btn_event = (GdkEventButton *)event; /* don't process zoom/scroll mod events if its a middle marker click */ if (event->type != GDK_BUTTON_PRESS || btn_event->button != 2 || btn_event->y > editor->marker_bar_height) { /* handle zoom/scroll related events */ if (swamigui_canvas_mod_handle_event (editor->sample_mod, event)) return (FALSE); /* was zoom/scroll event - return */ } switch (event->type) { case GDK_MOTION_NOTIFY: motion_event = (GdkEventMotion *)event; if (editor->sel_marker != -1) /* active marker? */ { marker_info = g_list_nth_data (editor->markers, editor->sel_marker); if (!marker_info) break; val = swamigui_sample_canvas_xpos_to_sample (sample_view, (int)motion_event->x, NULL); newstart = marker_info->start_pos; newend = marker_info->end_pos; if (editor->sel_marker_edge == -1) /* start edge selected? */ newstart = CLAMP (val, 0, sample_size); else if (editor->sel_marker_edge == 1) /* end edge selected? */ newend = CLAMP (val, 0, sample_size); else /* range move (both edges selected) */ { val -= editor->move_range_ofs; newstart = CLAMP (val, 0, (int)(sample_size - (newend - newstart))); newend += newstart - marker_info->start_pos; } /* swap selection if endpoints in reverse order */ if (newstart > newend) editor->sel_marker_edge = -editor->sel_marker_edge; /* set the marker */ swamigui_sample_editor_set_marker_info (editor, marker_info, newstart, newend); break; } else if (editor->sel_state != SEL_INACTIVE) /* if drag sel not inactive.. */ { val = swamigui_sample_canvas_xpos_to_sample (sample_view, (int)motion_event->x, NULL); val = CLAMP (val, 0, sample_size); /* get selection marker info */ marker_info = g_list_nth_data (editor->markers, 0); if (editor->sel_state == SEL_MAYBE) /* drag just begun? */ { if (val == editor->sel_temp) break; /* drag position not changed? */ /* just assume end drag, swap below if needed */ editor->sel_state = SEL_END; newstart = editor->sel_temp; newend = val; } else if (editor->sel_state == SEL_START) { newstart = val; newend = marker_info->end_pos; } else { newstart = marker_info->start_pos; newend = val; } if (newstart > newend) /* swap start/end if needed */ editor->sel_state = editor->sel_state == SEL_START ? SEL_END : SEL_START; /* set selection marker - start/end swapped in function if needed */ /* Disabled until sample editing operations are implemented by #define sample_edition */ #ifdef sample_edition swamigui_sample_editor_show_marker (editor, 0, TRUE); #endif swamigui_sample_editor_set_marker (editor, 0, newstart, newend); } else /* no marker sel or range sel active - mouse cursor over marker? */ { /* see if mouse is over a marker */ marker_info = pos_is_marker (editor, (int)motion_event->x, (int)motion_event->y, NULL, &onbox); /* see if cursor should be changed */ if (!onbox && (marker_info != NULL) != editor->marker_cursor) { if (marker_info) cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); /* ++ ref */ else cursor = gdk_cursor_new (GDK_LEFT_PTR); gdk_window_set_cursor (GTK_WIDGET (editor->sample_canvas)->window, cursor); gdk_cursor_unref (cursor); editor->marker_cursor = marker_info != NULL; } } break; case GDK_BUTTON_PRESS: btn_event = (GdkEventButton *)event; if (swamigui_root_is_middle_click (NULL, btn_event)) /* middle click moves marker ranges */ { marker_info = pos_is_marker (editor, (int)btn_event->x, (int)btn_event->y, NULL, &onbox); if (marker_info && onbox) /* if click on a range box.. */ { editor->sel_marker = g_list_index (editor->markers, marker_info); editor->sel_marker_edge = 0; /* calculate offset in samples from range start to click pos */ val = swamigui_sample_canvas_xpos_to_sample (sample_view, (int)btn_event->x, NULL); editor->move_range_ofs = val - marker_info->start_pos; } break; } /* make sure its button 1 */ if (btn_event->button != 1) break; /* is it a marker click? */ marker_info = pos_is_marker (editor, (int)btn_event->x, (int)btn_event->y, &editor->sel_marker_edge, NULL); if (marker_info) { editor->sel_marker = g_list_index (editor->markers, marker_info); break; } /* get sample position of click and assign to sel_temp */ editor->sel_temp = swamigui_sample_canvas_xpos_to_sample (sample_view, (int)btn_event->x, NULL); /* if click is within the sample, we have a potential selection */ if (editor->sel_temp >= 0 && editor->sel_temp < sample_size) editor->sel_state = SEL_MAYBE; break; case GDK_BUTTON_RELEASE: btn_event = (GdkEventButton *)event; if (btn_event->button == 1) { editor->sel_marker = -1; editor->sel_state = SEL_INACTIVE; } else if (btn_event->button == 2) editor->sel_marker = -1; break; default: break; } return (FALSE); } static gboolean swamigui_sample_editor_cb_loop_canvas_event (GnomeCanvas *canvas, GdkEvent *event, gpointer data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (data); /* handle zoom/scroll related events */ swamigui_canvas_mod_handle_event (editor->loop_mod, event); return (FALSE); /* was zoom/scroll event - return */ } /* check if a given xpos is on a marker */ static MarkerInfo * pos_is_marker (SwamiguiSampleEditor *editor, int xpos, int ypos, int *marker_edge, gboolean *is_onbox) { MarkerInfo *marker_info, *match = NULL; gboolean match_marker_edge = -1; SwamiguiSampleCanvas *sample_view; GnomeCanvasItem *item; int x, ofs = -1; int inview, onsample, pos; int startdiff, enddiff; GList *p; int i; if (is_onbox) *is_onbox = FALSE; if (marker_edge) *marker_edge = -1; if (!editor->markers || !editor->tracks) return (NULL); sample_view = SWAMIGUI_SAMPLE_CANVAS (((TrackInfo *)(editor->tracks->data))->sample_view); /* click in marker bar area? */ if (ypos <= editor->marker_bar_height) { /* retrieve canvas item at the given coordinates */ item = gnome_canvas_get_item_at (editor->sample_canvas, xpos, ypos); if (!item) return (NULL); for (i=0, p = editor->markers; p; i++, p = p->next) { #ifndef sample_edition /* Marker 0 is ignored until sample edition will be implemented by #define sample_edition */ if(i == 0) continue; #endif /* don't search marker 1 and 2 if loop finder isn't active */ if( ((i == 1) || (i == 2)) && !editor->loop_finder_active) continue; marker_info = (MarkerInfo *)(p->data); if (marker_info->range_box == item) /* range box matches? */ { if (marker_edge) { pos = swamigui_sample_canvas_xpos_to_sample (sample_view, xpos, &onsample); startdiff = marker_info->start_pos - pos; enddiff = marker_info->end_pos - pos; if (ABS (startdiff) <= ABS (enddiff)) *marker_edge = -1; else *marker_edge = 1; } if (is_onbox) *is_onbox = TRUE; return (marker_info); } } return (NULL); } /* click not in marker bar area */ for (i=0, p = editor->markers; p; i++, p = p->next) { #ifndef sample_edition /* Marker 0 is ignored until sample edition will be implemented by #define sample_edition */ if(i == 0) continue; #endif /* don't search marker 1 and 2 if loop finder isn't active */ if( ((i == 1) || (i == 2)) && !editor->loop_finder_active) continue; marker_info = (MarkerInfo *)(p->data); x = swamigui_sample_canvas_sample_to_xpos (sample_view, marker_info->start_pos, &inview); if (inview == 0) { x = ABS (xpos - x); if (x <= MARKER_CLICK_DISTANCE && (ofs == -1 || x < ofs)) { match = marker_info; match_marker_edge = -1; ofs = x; } } x = swamigui_sample_canvas_sample_to_xpos (sample_view, marker_info->end_pos, &inview); if (inview == 0) { x = ABS (xpos - x); if (x <= MARKER_CLICK_DISTANCE && (ofs == -1 || x < ofs)) { match = marker_info; match_marker_edge = 1; ofs = x; } } } if (marker_edge) *marker_edge = match_marker_edge; if (is_onbox) *is_onbox = FALSE; return (match); } /* canvas zoom/scroll modulator update signal handler */ static void swamigui_sample_editor_sample_mod_update (SwamiguiCanvasMod *mod, double xzoom, double yzoom, double xscroll, double yscroll, double xpos, double ypos, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); GnomeCanvasItem *sample_view; double zoom; int val; if (!editor->tracks) return; if (xzoom != 1.0) swamigui_sample_editor_zoom_ofs (editor, xzoom, xpos); if (xscroll != 0.0) { /* get first sample view item */ sample_view = ((TrackInfo *)(editor->tracks->data))->sample_view; /* get zoom from first sample canvas (samples/pixel) */ g_object_get (sample_view, "zoom", &zoom, NULL); xscroll *= zoom; /* convert from scroll pixels/sec to samples/sec */ /* do accumulation of scroll value, since we can only scroll by samples */ editor->scroll_acc += xscroll; if (editor->scroll_acc >= 1.0) val = (int)editor->scroll_acc; else if (editor->scroll_acc <= -1.0) val = -(int)(-editor->scroll_acc); else val = 0; if (val != 0) { editor->scroll_acc -= val; swamigui_sample_editor_scroll_ofs (editor, val); } } /* no support for Y zoom/scroll yet */ } /* canvas zoom/scroll modulator snap signal handler */ static void swamigui_sample_editor_sample_mod_snap (SwamiguiCanvasMod *mod, guint actions, double xsnap, double ysnap, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); int height, width; width = GTK_WIDGET (editor->sample_canvas)->allocation.width; height = GTK_WIDGET (editor->sample_canvas)->allocation.height; if (actions & (SWAMIGUI_CANVAS_MOD_ZOOM_X | SWAMIGUI_CANVAS_MOD_SCROLL_X)) { swamigui_util_canvas_line_set (editor->xsnap_line, xsnap, editor->marker_bar_height, xsnap, height); gnome_canvas_item_show (editor->xsnap_line); editor->scroll_acc = 0.0; } else gnome_canvas_item_hide (editor->xsnap_line); if (actions & (SWAMIGUI_CANVAS_MOD_ZOOM_Y | SWAMIGUI_CANVAS_MOD_SCROLL_Y)) { swamigui_util_canvas_line_set (editor->ysnap_line, 0, ysnap, width, ysnap); gnome_canvas_item_show (editor->ysnap_line); } else gnome_canvas_item_hide (editor->ysnap_line); } /* loop canvas zoom modulator update signal handler (scrolling does not apply) */ static void swamigui_sample_editor_loop_mod_update (SwamiguiCanvasMod *mod, double xzoom, double yzoom, double xscroll, double yscroll, double xpos, double ypos, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); if (xzoom != 1.0) swamigui_sample_editor_loop_zoom (editor, xzoom); /* no support for Y zoom yet */ } /* loop canvas zoom modulator snap signal handler */ static void swamigui_sample_editor_loop_mod_snap (SwamiguiCanvasMod *mod, guint actions, double xsnap, double ysnap, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); int height; height = GTK_WIDGET (editor->sample_canvas)->allocation.height; if (actions & SWAMIGUI_CANVAS_MOD_ZOOM_X) { swamigui_util_canvas_line_set (editor->loop_snap_line, xsnap, editor->marker_bar_height, xsnap, height); gnome_canvas_item_show (editor->loop_snap_line); } else gnome_canvas_item_hide (editor->loop_snap_line); } /** * swamigui_sample_editor_new: * * Create a new sample view object * * Returns: new widget of type SwamiguiSampleEditor */ GtkWidget * swamigui_sample_editor_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_sample_editor_get_type ()))); } /** * swamigui_sample_editor_zoom_ofs: * @editor: Sample editor object * @zoom_amt: Zoom multiplier (> 1 = zoom in, < 1 = zoom out) * @zoom_xpos: X coordinate position to keep stationary * * Zoom the sample canvas the specified scale amount and modify the * start sample position to keep the given X coordinate stationary. */ void swamigui_sample_editor_zoom_ofs (SwamiguiSampleEditor *editor, double zoom_amt, double zoom_xpos) { GnomeCanvasItem *sample_view; TrackInfo *track_info; double zoom, newzoom, sample_ofs; guint start, newstart; int width; GList *p; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); if (!editor->tracks) return; /* get first sample view item */ sample_view = ((TrackInfo *)(editor->tracks->data))->sample_view; /* get values from first sample in editor */ g_object_get (sample_view, "zoom", &zoom, "start", &start, "width", &width, NULL); sample_ofs = zoom_xpos * zoom; /* offset, in samples, to zoom xpos */ newzoom = zoom * (1.0 / zoom_amt); newstart = start; editor->zoom_all = FALSE; /* do some bounds checking on the zoom value */ if (newzoom < SAMPLE_CANVAS_MIN_ZOOM) newzoom = SAMPLE_CANVAS_MIN_ZOOM; else if (width * newzoom > editor->sample_size) { /* view exceeds sample data */ newstart = 0; newzoom = editor->sample_size / (double)width; editor->zoom_all = TRUE; } else { sample_ofs -= zoom_xpos * newzoom; /* subtract new zoom offset */ if (sample_ofs < 0.0 && -sample_ofs > newstart) newstart = 0; else newstart += (guint)(sample_ofs + 0.5); /* make sure sample doesn't end in the middle of the display */ if (newstart + width * newzoom > editor->sample_size) newstart = (guint)(editor->sample_size - width * newzoom); } if (newzoom == zoom && newstart == start) return; p = editor->tracks; while (p) { track_info = (TrackInfo *)(p->data); g_object_set (track_info->sample_view, "zoom", newzoom, "start", newstart, NULL); p = g_list_next (p); } } /** * swamigui_sample_editor_scroll_ofs: * @editor: Sample editor object * @sample_ofs: Offset amount in samples * * Scroll the sample canvas by a given offset. */ void swamigui_sample_editor_scroll_ofs (SwamiguiSampleEditor *editor, int sample_ofs) { GnomeCanvasItem *sample_view; double zoom; guint start_sample; int newstart, width; int last_sample; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); if (sample_ofs == 0) return; /* get first sample view item */ sample_view = ((TrackInfo *)(editor->tracks->data))->sample_view; g_object_get (sample_view, "start", &start_sample, "zoom", &zoom, "width", &width, NULL); last_sample = (int)(editor->sample_size - zoom * width); if (last_sample < 0) return; /* sample too small for current zoom? */ newstart = (int)start_sample + sample_ofs; newstart = CLAMP (newstart, 0, last_sample); if (newstart == start_sample) return; g_object_set (sample_view, "start", newstart, NULL); } /** * swamigui_sample_editor_loop_zoom: * @editor: Sample editor object * @zoom_amt: Zoom multiplier (> 1 = zoom in, < 1 = zoom out) * * Zoom the loop viewer canvas the specified scale amount. Zoom always occurs * around center of loop cross overlap. */ void swamigui_sample_editor_loop_zoom (SwamiguiSampleEditor *editor, double zoom_amt) { GnomeCanvasItem *loop_view; TrackInfo *track_info; double zoom, newzoom; GList *p; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); if (!editor->tracks) return; /* get first loop view item */ loop_view = ((TrackInfo *)(editor->tracks->data))->loop_view; /* get values from first sample in editor */ g_object_get (loop_view, "zoom", &zoom, NULL); newzoom = zoom * (1.0 / zoom_amt); /* do some bounds checking on the zoom value */ if (newzoom < LOOP_CANVAS_MIN_ZOOM) newzoom = LOOP_CANVAS_MIN_ZOOM; else if (newzoom > 1.0) newzoom = 1.0; if (newzoom == zoom) return; editor->loop_zoom = newzoom; p = editor->tracks; while (p) { track_info = (TrackInfo *)(p->data); g_object_set (track_info->loop_view, "zoom", newzoom, NULL); p = g_list_next (p); } } /** * swamigui_sample_editor_set_selection: * @editor: Sample editor object * @items: List of selected items or %NULL to unset selection * * Set the items of a sample editor widget. The @items list will usually * contain a single patch item that has sample data associated with it, * although sometimes multiple items will be handled as in the case of * stereo pairs. */ void swamigui_sample_editor_set_selection (SwamiguiSampleEditor *editor, IpatchList *items) { if (swamigui_sample_editor_real_set_selection (editor, items)) g_object_notify (G_OBJECT (editor), "item-selection"); } static gboolean swamigui_sample_editor_real_set_selection (SwamiguiSampleEditor *editor, IpatchList *items) { SwamiguiSampleEditorHandler hfunc; GList *p; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), FALSE); g_return_val_if_fail (!items || IPATCH_IS_LIST (items), FALSE); if (editor->selection) g_object_unref (editor->selection); /* -- unref old */ /* if items list then duplicate it and take over the reference */ if (items) editor->selection = ipatch_list_duplicate (items); /* !! */ else editor->selection = NULL; if (editor->handler) /* active handler? */ { editor->status = SWAMIGUI_SAMPLE_EDITOR_UPDATE; if (!items || !(*editor->handler)(editor)) swamigui_sample_editor_deactivate_handler (editor); } if (items && !editor->handler) /* re-test in case it was de-activated */ { editor->status = SWAMIGUI_SAMPLE_EDITOR_INIT; p = sample_editor_handlers; while (p) /* try handlers */ { hfunc = (SwamiguiSampleEditorHandler)(p->data); if ((*hfunc)(editor)) /* selection handled? */ { editor->handler = hfunc; break; } p = g_list_next (p); } } editor->status = SWAMIGUI_SAMPLE_EDITOR_NORMAL; return (TRUE); } static void swamigui_sample_editor_deactivate_handler (SwamiguiSampleEditor *editor) { swamigui_sample_editor_reset (editor); editor->handler = NULL; editor->handler_data = NULL; } /** * swamigui_sample_editor_get_selection: * @editor: Sample editor widget * * Get the list of active items in a sample editor widget. * * Returns: New list containing selected items which has a ref count of one * which the caller owns or %NULL if no items selected. Remove the * reference when finished with it. */ IpatchList * swamigui_sample_editor_get_selection (SwamiguiSampleEditor *editor) { g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), NULL); if (editor->selection && editor->selection->items) return (ipatch_list_duplicate (editor->selection)); else return (NULL); } /** * swamigui_sample_editor_register_handler: * @handler: Sample editor handler function to register * @check_func: Function used to check if an item selection is valid for this handler * * Registers a new handler for sample editor widgets. Sample editor handlers * interface patch item's of particular types with sample data and related * parameters (such as looping params). * * MT: This function should only be called within GUI thread. */ void swamigui_sample_editor_register_handler (SwamiguiSampleEditorHandler handler, SwamiguiPanelCheckFunc check_func) { g_return_if_fail (handler != NULL); g_return_if_fail (check_func != NULL); sample_editor_handlers = g_list_prepend (sample_editor_handlers, handler); sample_editor_check_funcs = g_list_prepend (sample_editor_check_funcs, check_func); } /** * swamigui_sample_editor_unregister_handler: * @handler: Handler function to unregister * * Unregisters a handler previously registered with * swamigui_sample_editor_register_handler(). * * MT: This function should only be called in GUI thread. */ void swamigui_sample_editor_unregister_handler (SwamiguiSampleEditorHandler handler) { int handler_index; GList *p; g_return_if_fail (handler != NULL); handler_index = g_list_index (sample_editor_handlers, handler); g_return_if_fail (handler_index != -1); sample_editor_handlers = g_list_remove (sample_editor_handlers, handler); /* remove the corresponding check function */ p = g_list_nth (sample_editor_check_funcs, handler_index); sample_editor_check_funcs = g_list_delete_link (sample_editor_check_funcs, p); } /** * swamigui_sample_editor_reset: * @editor: Sample editor widget * * Resets a sample editor by removing all tracks and markers and disconnecting * loop view controls. Usually only used by sample editor handlers. */ void swamigui_sample_editor_reset (SwamiguiSampleEditor *editor) { g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); g_object_set (editor->loop_finder_gui->loop_finder, "sample", NULL, NULL); swamigui_sample_editor_remove_all_tracks (editor); swamigui_sample_editor_remove_all_markers (editor); /* disable loop type selector */ swamigui_sample_editor_set_loop_types (editor, NULL, FALSE); /* disconnect any controls connected to loop view hub controls */ swami_control_disconnect_all (editor->loop_start_hub); swami_control_disconnect_all (editor->loop_end_hub); swami_control_disconnect_all (editor->loopsel_ctrl); /* disconnect any controls to spin buttons */ swami_control_disconnect_all (editor->spinbtn_start_ctrl); swami_control_disconnect_all (editor->spinbtn_end_ctrl); } /** * swamigui_sample_editor_get_loop_controls: * @editor: Sample editor object * @loop_start: Output - Loop view start point control (%NULL to ignore) * @loop_end: Output - Loop view end point control (%NULL to ignore) * * Get the loop start and end controls that are connected to all loop view * start and end properties. Essentially controls for the loop view start * and end points. */ void swamigui_sample_editor_get_loop_controls (SwamiguiSampleEditor *editor, SwamiControl **loop_start, SwamiControl **loop_end) { g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); if (loop_start) *loop_start = editor->loop_start_hub; if (loop_end) *loop_end = editor->loop_end_hub; } /** * swamigui_sample_editor_add_track: * @editor: Sample editor object * @sample: Object with an #IpatchSample interface to add * @right_chan: Only used if @sample is stereo. If %TRUE the right channel * will be used, %FALSE will use the left channel. * * Add a sample track to a sample editor. Usually only done by * sample editor handlers. This function can be used to add multiple * samples for stereo or multi-track audio. * * Returns: The index of the new track in the sample editor object (starting * from 0). */ int swamigui_sample_editor_add_track (SwamiguiSampleEditor *editor, IpatchSampleData *sample, gboolean right_chan) { TrackInfo *track_info; SwamiControl *ctrl; double zoom; guint sample_size; int width; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), 0); g_return_val_if_fail (IPATCH_IS_SAMPLE_DATA (sample), 0); /* calculate zoom to view entire sample */ g_object_get (sample, "sample-size", &sample_size, NULL); width = GTK_WIDGET (editor->sample_canvas)->allocation.width; zoom = (double)sample_size / width; if (!editor->tracks) /* if no samples have been added yet */ editor->sample_size = sample_size; /* cache sample size */ track_info = g_new (TrackInfo, 1); track_info->sample = g_object_ref (sample); /* ++ ref sample */ track_info->right_chan = right_chan; /* create sample view horizontal center line */ track_info->sample_center_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->center_line_color, "width-pixels", 1, NULL); track_info->sample_view = /* create the sample view canvas item */ gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), SWAMIGUI_TYPE_SAMPLE_CANVAS, "sample", sample, "right-chan", right_chan, "zoom", zoom, /* only the first sample item will update adj. */ "update-adj", editor->tracks ? FALSE : TRUE, "adjustment", gtk_range_get_adjustment (GTK_RANGE (editor->hscrollbar)), NULL); /* create loop view horizontal center line */ track_info->loop_center_line = gnome_canvas_item_new (gnome_canvas_root (editor->loop_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", editor->center_line_color, "width-pixels", 1, NULL); track_info->loop_view = /* create the loop view canvas item */ gnome_canvas_item_new (gnome_canvas_root (editor->loop_canvas), SWAMIGUI_TYPE_SAMPLE_CANVAS, "sample", sample, "right-chan", right_chan, "loop-mode", TRUE, "zoom", editor->loop_zoom, NULL); gnome_canvas_item_lower_to_bottom (track_info->sample_center_line); gnome_canvas_item_lower_to_bottom (track_info->loop_center_line); gnome_canvas_item_lower_to_bottom (track_info->sample_view); gnome_canvas_item_lower_to_bottom (track_info->loop_view); gnome_canvas_item_lower_to_bottom (editor->loop_line); /* add track to track list */ editor->tracks = g_list_append (editor->tracks, track_info); /* create loop view "loop-start" property control and connect to hub */ ctrl = SWAMI_CONTROL (swami_get_control_prop_by_name /* ++ ref */ (G_OBJECT (track_info->loop_view), "loop-start")); swami_control_connect (editor->loop_start_hub, ctrl, 0); /* hub is queued */ g_object_unref (ctrl); /* -- unref */ /* create loop view "loop-end" property control and connect to hub */ ctrl = SWAMI_CONTROL (swami_get_control_prop_by_name /* ++ ref */ (G_OBJECT (track_info->loop_view), "loop-end")); swami_control_connect (editor->loop_end_hub, ctrl, 0); /* hub is queued */ g_object_unref (ctrl); /* -- unref */ swamigui_sample_editor_update_sizes (editor); return (g_list_length (editor->tracks) - 1); } /** * swamigui_sample_editor_get_track_info: * @editor: Sample editor object * @track: Track index to get info from (starting from 0) * @sample: Output - Sample object (%NULL to ignore) * @sample_view: Output - The sample view canvas (%NULL to ignore) * @loop_view: Output - The loop view canvas (%NULL to ignore) * * Get info for a sample track. No reference counting is done, since * track will not get removed unless the sample editor handler does * so. Returned object pointers should only be used within editor * callback or references should be added. * * Returns: %TRUE if @index is a valid sample index, %FALSE otherwise in * which case the returned pointers are undefined. */ gboolean swamigui_sample_editor_get_track_info (SwamiguiSampleEditor *editor, guint track, IpatchSampleData **sample, SwamiguiSampleCanvas **sample_view, SwamiguiSampleCanvas **loop_view) { TrackInfo *track_info; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), FALSE); track_info = (TrackInfo *)g_list_nth_data (editor->tracks, track); if (!track_info) return (FALSE); if (sample) *sample = track_info->sample; if (sample_view) *sample_view = SWAMIGUI_SAMPLE_CANVAS (track_info->sample_view); if (loop_view) *loop_view = SWAMIGUI_SAMPLE_CANVAS (track_info->loop_view); return (TRUE); } /** * swamigui_sample_editor_remove_track: * @editor: Sample editor object * @track: Index of track to remove * * Remove a track from a sample editor by its index (starts from 0). */ void swamigui_sample_editor_remove_track (SwamiguiSampleEditor *editor, guint track) { GList *found_track; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); found_track = g_list_nth (editor->tracks, track); g_return_if_fail (found_track != NULL); swamigui_sample_editor_remove_track_item (editor, found_track, TRUE); swamigui_sample_editor_update_sizes (editor); } /* internal function for removing by track info list node, destroy flag is used by finalize (set to FALSE since no need to destroy GtkObjects) */ static void swamigui_sample_editor_remove_track_item (SwamiguiSampleEditor *editor, GList *p, gboolean destroy) { TrackInfo *track_info; track_info = (TrackInfo *)(p->data); g_object_unref (track_info->sample); /* -- unref sample */ if (destroy) { /* destroy sample canvas items */ gtk_object_destroy (GTK_OBJECT (track_info->sample_view)); gtk_object_destroy (GTK_OBJECT (track_info->loop_view)); /* destroy horizontal center lines */ gtk_object_destroy (GTK_OBJECT (track_info->sample_center_line)); gtk_object_destroy (GTK_OBJECT (track_info->loop_center_line)); } g_free (track_info); /* if removing the first track and there are others.. */ if (p == editor->tracks && editor->tracks->next) { track_info = (TrackInfo *)(editor->tracks->next->data); /* the new first sample canvas item now is adjustment master */ g_object_set (track_info->sample_view, "update-adj", TRUE, NULL); } /* remove from list */ editor->tracks = g_list_delete_link (editor->tracks, p); } /** * swamigui_sample_editor_remove_all_tracks: * @editor: Sample editor object * * Remove all tracks from a sample editor. */ void swamigui_sample_editor_remove_all_tracks (SwamiguiSampleEditor *editor) { GList *p, *temp; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); p = editor->tracks; while (p) { temp = p; p = g_list_next (p); swamigui_sample_editor_remove_track_item (editor, temp, TRUE); } } /** * swamigui_sample_editor_add_marker: * @editor: Sample editor object * @flags: #SwamiguiSampleEditorMarkerFlags * - #SWAMIGUI_SAMPLE_EDITOR_MARKER_SINGLE is used for markers which define * a single value. * - #SWAMIGUI_SAMPLE_EDITOR_MARKER_VIEW is used for markers which are view * only and not controllable by user. * - #SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE is used to define a start/size * marker instead of a start/end marker. This causes the second control to * be the current size of the marker instead of the end position. * Note that passing 0 defines a interactive range marker. * @start: Output - New control for start of marker or %NULL to ignore * @end: Output - New control for end/size of marker (ranges only) or %NULL to * ignore * * Add a new marker to a sample editor. Markers can be used for loop points, * sample start/end points, selections and other position indicators/controls. * Marker 0 is the sample selection marker and it is always present although it * may be hidden. * * Returns: New marker index */ guint swamigui_sample_editor_add_marker (SwamiguiSampleEditor *editor, guint flags, SwamiControl **start, SwamiControl **end) { MarkerInfo *mark_info; guint color; int id; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), 0); mark_info = g_new0 (MarkerInfo, 1); mark_info->flags = flags; mark_info->editor = editor; mark_info->visible = TRUE; id = g_list_length (editor->markers); color = default_marker_colors [id % G_N_ELEMENTS (default_marker_colors)]; mark_info->start_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", color, "width-pixels", MARKER_DEFAULT_WIDTH, NULL); gnome_canvas_item_hide (mark_info->start_line); /* create start marker control (!! editor takes over ref) */ mark_info->start_ctrl = swamigui_control_new (SWAMI_TYPE_CONTROL_FUNC); swami_control_set_value_type (mark_info->start_ctrl, G_TYPE_UINT); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (mark_info->start_ctrl), start_marker_control_get_value_func, start_marker_control_set_value_func, NULL, mark_info); /* create end line and range box if a range marker */ if (!(flags & SWAMIGUI_SAMPLE_EDITOR_MARKER_SINGLE)) { mark_info->end_line = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", color, "width-pixels", MARKER_DEFAULT_WIDTH, NULL); gnome_canvas_item_hide (mark_info->end_line); mark_info->range_box = gnome_canvas_item_new (gnome_canvas_root (editor->sample_canvas), GNOME_TYPE_CANVAS_RECT, "fill-color-rgba", color, NULL); gnome_canvas_item_hide (mark_info->range_box); /* create end/size marker control (!! editor takes over ref) */ mark_info->end_ctrl = swamigui_control_new (SWAMI_TYPE_CONTROL_FUNC); swami_control_set_value_type (mark_info->end_ctrl, G_TYPE_UINT); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (mark_info->end_ctrl), end_marker_control_get_value_func, end_marker_control_set_value_func, NULL, mark_info); } editor->markers = g_list_append (editor->markers, mark_info); marker_control_update_all (editor); if (start) *start = mark_info->start_ctrl; if (end) *end = mark_info->end_ctrl; return (id); } /** * swamigui_sample_editor_get_marker_info: * @editor: Sample editor object * @marker: Marker index to get info on (starts at 0 - the selection marker) * @flags: #SwamiguiSampleEditorMarkerFlags * @start_line: Output - Gnome canvas line for the start marker (%NULL to ignore) * @end_line: Output - Gnome canvas line for the end marker (%NULL to ignore), * will be %NULL for single markers (non ranges) * @start_ctrl: Output - Control for the start marker (%NULL to ignore) * @end_ctrl: Output - Control for the end/size marker (%NULL to ignore), will be * %NULL for single markers (non ranges) * * Get info for the given @marker index. No reference counting is * done, since marker will not get removed unless the sample editor * handler does so. Returned object pointers should only be used * within editor callback or references should be added. * * Returns: %TRUE if a marker with the given index exists, %FALSE otherwise. */ gboolean swamigui_sample_editor_get_marker_info (SwamiguiSampleEditor *editor, guint marker, guint *flags, GnomeCanvasItem **start_line, GnomeCanvasItem **end_line, SwamiControl **start_ctrl, SwamiControl **end_ctrl) { MarkerInfo *marker_info; g_return_val_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor), FALSE); marker_info = (MarkerInfo *)g_list_nth_data (editor->markers, marker); if (!marker_info) return (FALSE); if (flags) *flags = marker_info->flags; if (start_line) *start_line = marker_info->start_line; if (end_line) *end_line = marker_info->end_line; if (start_ctrl) *start_ctrl = marker_info->start_ctrl; if (end_ctrl) *end_ctrl = marker_info->end_ctrl; return (TRUE); } /** * swamigui_sample_editor_set_marker: * @editor: Sample editor widget * @marker: Marker number * @start: Marker start position * @end: Marker end position * * Set the marker start and end positions. */ void swamigui_sample_editor_set_marker (SwamiguiSampleEditor *editor, guint marker, guint start, guint end) { MarkerInfo *marker_info; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); marker_info = (MarkerInfo *)g_list_nth_data (editor->markers, marker); if (!marker_info) return; swamigui_sample_editor_set_marker_info (editor, marker_info, start, end); } static void swamigui_sample_editor_set_marker_info (SwamiguiSampleEditor *editor, MarkerInfo *marker_info, guint start, guint end) { GValue value = { 0 }; gboolean size_marker; gboolean start_changed = FALSE; guint temp; if (start > end) /* swap if backwards */ { temp = start; start = end; end = temp; } size_marker = (marker_info->flags & SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE) != 0; g_value_init (&value, G_TYPE_UINT); if (start != marker_info->start_pos) { marker_info->start_pos = start; g_value_set_uint (&value, start); swami_control_transmit_value (marker_info->start_ctrl, &value); start_changed = TRUE; } if (end != marker_info->end_pos || (size_marker && start_changed)) { marker_info->end_pos = end; if (size_marker) g_value_set_uint (&value, end - start + 1); else g_value_set_uint (&value, end); swami_control_transmit_value (marker_info->end_ctrl, &value); } g_value_unset (&value); marker_control_update (marker_info); /* update the marker GUI */ } /** * swamigui_sample_editor_remove_marker: * @editor: Sample editor object * @marker: Index of marker to remove (builtin markers are hidden rather than * removed) * * Remove a marker. */ void swamigui_sample_editor_remove_marker (SwamiguiSampleEditor *editor, guint marker) { GList *p; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); if (marker < BUILTIN_MARKER_COUNT) { swamigui_sample_editor_show_marker (editor, marker, FALSE); return; } p = g_list_nth (editor->markers, marker); if (!p) return; swamigui_sample_editor_remove_marker_item (editor, p, TRUE); } /** * swamigui_sample_editor_remove_all_markers: * @editor: Sample editor object * * Remove all markers (except selection marker 0, which is always present). */ void swamigui_sample_editor_remove_all_markers (SwamiguiSampleEditor *editor) { GList *p, *temp; int i; p = g_list_nth (editor->markers, BUILTIN_MARKER_COUNT); while (p) { temp = p; p = g_list_next (p); swamigui_sample_editor_remove_marker_item (editor, temp, TRUE); } /* hide builtin markers */ for (i = 0; i < BUILTIN_MARKER_COUNT; i++) swamigui_sample_editor_show_marker (editor, i, FALSE); } /* remove a marker, destroy flag is used by finalize function since GtkObjects dont need to be destroyed on finalize */ static void swamigui_sample_editor_remove_marker_item (SwamiguiSampleEditor *editor, GList *p, gboolean destroy) { MarkerInfo *marker_info; SwamiControl *ctrl; marker_info = (MarkerInfo *)(p->data); /* remove from marker list */ editor->markers = g_list_delete_link (editor->markers, p); /* destroy line canvas item */ if (destroy) { gtk_object_destroy (GTK_OBJECT (marker_info->start_line)); if (marker_info->end_line) gtk_object_destroy (GTK_OBJECT (marker_info->end_line)); if (marker_info->range_box) gtk_object_destroy (GTK_OBJECT (marker_info->range_box)); } /* we don't want to depend on the existence of mark_info anymore */ ctrl = marker_info->end_ctrl; /* FIXME - What about start_ctrl !!! */ /* clear data in marker info */ memset (marker_info, 0, sizeof (MarkerInfo)); /* add a week references to the marker controls to free the marker_info structures (since we still might receive queued events) */ g_object_weak_ref (G_OBJECT (ctrl), (GWeakNotify)g_free, marker_info); g_object_unref (ctrl); /* -- remove the editor's reference */ } /* start marker control get value method */ static void start_marker_control_get_value_func (SwamiControl *control, GValue *value) { MarkerInfo *marker_info = SWAMI_CONTROL_FUNC_DATA (control); g_value_set_uint (value, marker_info->start_pos); } /* start marker control set value method */ static void start_marker_control_set_value_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { MarkerInfo *marker_info = SWAMI_CONTROL_FUNC_DATA (control); guint u; if (!marker_info->start_line) return; /* in process of being destroyed? */ u = g_value_get_uint (value); if (u != marker_info->start_pos) { marker_info->start_pos = u; marker_control_update (marker_info); } } /* end marker control get value method */ static void end_marker_control_get_value_func (SwamiControl *control, GValue *value) { MarkerInfo *marker_info = SWAMI_CONTROL_FUNC_DATA (control); if (marker_info->flags & SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE) g_value_set_uint (value, marker_info->end_pos - marker_info->start_pos + 1); else g_value_set_uint (value, marker_info->end_pos); } /* end marker control set value method */ static void end_marker_control_set_value_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { MarkerInfo *marker_info = SWAMI_CONTROL_FUNC_DATA (control); guint u; if (!marker_info->end_line) return; /* in process of being destroyed? */ u = g_value_get_uint (value); if (marker_info->flags & SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE) u += marker_info->start_pos - 1; if (u != marker_info->end_pos && u >= marker_info->start_pos) { marker_info->end_pos = u; marker_control_update (marker_info); } } /* update the display of a single marker in the sample view */ static void marker_control_update (MarkerInfo *marker_info) { SwamiguiSampleEditor *editor = marker_info->editor; SwamiguiSampleCanvas *sample_view = NULL; TrackInfo *track_info; GnomeCanvasPoints *points; int startx, endx, sview = -1, eview = -1; int width, height; int visible_count, pos; int y1 = 0, y2 = 0; GList *p; /* can't position marker lines if no sample canvases, exception is if marker is hidden, then we don't need to update the position */ if (!editor->tracks && marker_info->visible) return; /* count number of visible markers and locate visible position of this marker */ for (p = editor->markers, visible_count = 0; p; p = p->next) { if (p->data == marker_info) pos = visible_count; if (((MarkerInfo *)(p->data))->visible) visible_count++; } pos = visible_count - pos - 1; /* adjust for Y-coord order */ if (editor->tracks) { track_info = (TrackInfo *)(editor->tracks->data); sample_view = SWAMIGUI_SAMPLE_CANVAS (track_info->sample_view); } width = GTK_WIDGET (editor->sample_canvas)->allocation.width; height = GTK_WIDGET (editor->sample_canvas)->allocation.height; /* calculate Y positions of range bar */ if (visible_count > 0) { y1 = (pos * (editor->marker_bar_height - 1)) / visible_count; y2 = ((pos+1) * (editor->marker_bar_height - 1)) / visible_count - 1; } points = gnome_canvas_points_new (2); points->coords[1] = y2; points->coords[3] = height; /* update start marker line */ if (sample_view) startx = swamigui_sample_canvas_sample_to_xpos (sample_view, marker_info->start_pos, &sview); if (sample_view && sview == 0 && marker_info->visible) { points->coords[0] = startx; points->coords[2] = startx; g_object_set (marker_info->start_line, "points", points, NULL); gnome_canvas_item_show (marker_info->start_line); } else { gnome_canvas_item_hide (marker_info->start_line); startx = 0; } /* update end marker and range box (if any) */ if (marker_info->end_line) { if (sample_view) endx = swamigui_sample_canvas_sample_to_xpos (sample_view, marker_info->end_pos, &eview); if (sample_view && eview == 0 && marker_info->visible) { points->coords[0] = endx; points->coords[2] = endx; endx++; /* for marker range_box below */ g_object_set (marker_info->end_line, "points", points, NULL); gnome_canvas_item_show (marker_info->end_line); } else { gnome_canvas_item_hide (marker_info->end_line); endx = width; } /* is marker range box in view? */ if (sample_view && (sview == 0 || eview == 0 || (sview == -1 && eview == 1)) && marker_info->visible) { g_object_set (marker_info->range_box, "x1", (double)startx, "x2", (double)endx, "y1", (double)y1, "y2", (double)y2, NULL); gnome_canvas_item_show (marker_info->range_box); } else gnome_canvas_item_hide (marker_info->range_box); } gnome_canvas_points_free (points); } static void marker_control_update_all (SwamiguiSampleEditor *editor) { MarkerInfo *marker_info; GList *p; for (p = editor->markers; p; p = g_list_next (p)) { marker_info = (MarkerInfo *)(p->data); marker_control_update (marker_info); } } /** * swamigui_sample_editor_show_marker: * @editor: Sample editor widget * @marker: Marker number to set visibility of * @show_marker: %TRUE to show marker, %FALSE to hide * * Set the visibility of a marker. */ void swamigui_sample_editor_show_marker (SwamiguiSampleEditor *editor, guint marker, gboolean show_marker) { MarkerInfo *marker_info; GList *p; g_return_if_fail (SWAMIGUI_IS_SAMPLE_EDITOR (editor)); p = g_list_nth (editor->markers, marker); if (!p) return; marker_info = (MarkerInfo *)(p->data); if (marker_info->visible == show_marker) return; marker_info->visible = show_marker; marker_control_update_all (editor); } /** * swamigui_sample_editor_set_loop_types: * @editor: Sample editor object * @types: -1 terminated array of #IpatchSampleLoopType values to show in * loop selector (%NULL to hide loop selector) * @loop_play_btn: If @types is %NULL, setting this to %TRUE will cause a * play loop toggle button to be shown (for items that dont have a loop type * property, but it is desirable for the user to be able to listen to the loop). * * Usually only used by sample editor handlers. Sets the available loop types * in the loop selector menu. */ void swamigui_sample_editor_set_loop_types (SwamiguiSampleEditor *editor, int *types, gboolean loop_play_btn) { GtkTreeIter iter; int i, i2; gtk_list_store_clear (editor->loopsel_store); if (types) { for (i = 0; i < G_N_ELEMENTS (loop_type_info); i++) { for (i2 = 0; types[i2] != -1; i2++) { if (loop_type_info[i].loop_type == types[i2]) { gtk_list_store_append (editor->loopsel_store, &iter); gtk_list_store_set (editor->loopsel_store, &iter, LOOPSEL_COL_LOOP_TYPE, types[i2], LOOPSEL_COL_ICON, loop_type_info[i].icon, LOOPSEL_COL_LABEL, loop_type_info[i].label, LOOPSEL_COL_TOOLTIP, loop_type_info[i].tooltip, -1); } } } gtk_widget_set_sensitive (editor->loopsel, TRUE); } else gtk_widget_set_sensitive (editor->loopsel, FALSE); } /** * swamigui_sample_editor_set_active_loop_type: * @editor: Sample editor widget * @type: #IpatchSampleLoopType or TRUE/FALSE if using loop play button. * * Set the active loop type in the loop type selector or loop play button. * Usually only used by sample editor handlers. */ void swamigui_sample_editor_set_active_loop_type (SwamiguiSampleEditor *editor, int type) { GtkTreeModel *model = GTK_TREE_MODEL (editor->loopsel_store); GtkTreeIter iter; int cmptype; if (!gtk_tree_model_get_iter_first (model, &iter)) return; do { gtk_tree_model_get (model, &iter, LOOPSEL_COL_LOOP_TYPE, &cmptype, -1); if (cmptype == type) { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (editor->loopsel), &iter); break; } } while (gtk_tree_model_iter_next (model, &iter)); } /* Default IpatchSample interface sample editor handler */ static gboolean swamigui_sample_editor_default_handler (SwamiguiSampleEditor *editor) { IpatchSample *sample; IpatchSampleData *sampledata; SwamiControl *loop_view_start, *loop_view_end; SwamiControl *mark_loop_start, *mark_loop_end; SwamiControl *loop_start_prop, *loop_end_prop, *loop_type_prop; int *loop_types; GObject *obj; /* check if selection is handled (single item with IpatchSample interface) */ if (editor->status == SWAMIGUI_SAMPLE_EDITOR_INIT || editor->status == SWAMIGUI_SAMPLE_EDITOR_UPDATE) { /* only handle single item with IpatchSample interface */ if (!editor->selection || !editor->selection->items || editor->selection->items->next) return (FALSE); obj = editor->selection->items->data; if (!IPATCH_IS_SAMPLE (obj)) return (FALSE); sample = IPATCH_SAMPLE (obj); /* same item already selected? */ if ((gpointer)sample == (gpointer)(editor->handler_data)) return (TRUE); sampledata = ipatch_sample_get_sample_data (sample); /* ++ ref sample data */ /* clear all tracks and markers if updating and a new item is selected */ if (editor->status == SWAMIGUI_SAMPLE_EDITOR_UPDATE) swamigui_sample_editor_reset (editor); editor->handler_data = sample; /* set handler data to selected item */ /* clear loop finder results */ swamigui_loop_finder_clear_results (editor->loop_finder_gui); /* stereo sample data?? */ if (IPATCH_SAMPLE_FORMAT_GET_CHANNELS (ipatch_sample_get_format (sample)) == IPATCH_SAMPLE_STEREO) { swamigui_sample_editor_add_track (editor, sampledata, FALSE); swamigui_sample_editor_add_track (editor, sampledata, TRUE); } else /* mono data */ swamigui_sample_editor_add_track (editor, sampledata, FALSE); /* set sample of loop finder */ g_object_set (editor->loop_finder_gui->loop_finder, "sample", sample, NULL); /* create loop range marker */ swamigui_sample_editor_add_marker (editor, 0, &mark_loop_start, &mark_loop_end); /* ++ ref sample loop property controls */ loop_start_prop = swami_get_control_prop_by_name (G_OBJECT (sample), "loop-start"); loop_end_prop = swami_get_control_prop_by_name (G_OBJECT (sample), "loop-end"); /* connect sample properties to loop markers */ swami_control_connect (loop_start_prop, mark_loop_start, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); swami_control_connect (loop_end_prop, mark_loop_end, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); /* connect sample properties to spin buttons */ swami_control_connect (loop_start_prop, editor->spinbtn_start_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); swami_control_connect (loop_end_prop, editor->spinbtn_end_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); /* get loop view controls */ swamigui_sample_editor_get_loop_controls (editor, &loop_view_start, &loop_view_end); /* connect sample properties to loop view controls */ swami_control_connect (loop_start_prop, loop_view_start, SWAMI_CONTROL_CONN_INIT); swami_control_connect (loop_end_prop, loop_view_end, SWAMI_CONTROL_CONN_INIT); /* unref sample property controls */ g_object_unref (loop_start_prop); g_object_unref (loop_end_prop); /* set the available loop types of the loop selector */ loop_types = ipatch_sample_get_loop_types (sample); swamigui_sample_editor_set_loop_types (editor, loop_types, FALSE); if (loop_types) { /* ++ ref new prop control */ loop_type_prop = swami_get_control_prop_by_name (G_OBJECT (sample), "loop-type"); swami_control_connect (loop_type_prop, editor->loopsel_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); g_object_unref (loop_type_prop); /* -- unref prop control */ } /* show finder markers (if finder enabled) */ if (editor->loop_finder_active) { swamigui_sample_editor_show_marker (editor, SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_START, TRUE); swamigui_sample_editor_show_marker (editor, SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_END, TRUE); } g_object_unref (sampledata); /* -- unref sample data */ } /* if INIT or UPDATE */ return (TRUE); } static gboolean swamigui_sample_editor_default_handler_check_func (IpatchList *selection, GType *selection_types) { IpatchSampleData *sampledata; /* only handle single item with IpatchSample interface which returns sample data */ if (selection->items->next || !g_type_is_a (*selection_types, IPATCH_TYPE_SAMPLE) || !(sampledata = ipatch_sample_get_sample_data /* ++ ref sample data */ ((IpatchSample *)(selection->items->data)))) return (FALSE); g_object_unref (sampledata); /* -- unref sample data */ return (TRUE); } /* get value function for loopsel_ctrl */ static void swamigui_sample_editor_loopsel_ctrl_get (SwamiControl *control, GValue *value) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (SWAMI_CONTROL_FUNC_DATA (control)); GtkTreeIter iter; int loop_type = 0; /* get active loop type or failing that get first loop type */ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (editor->loopsel), &iter) || gtk_tree_model_get_iter_first (GTK_TREE_MODEL (editor->loopsel_store), &iter)) { gtk_tree_model_get (GTK_TREE_MODEL (editor->loopsel_store), &iter, LOOPSEL_COL_LOOP_TYPE, &loop_type, -1); } g_value_set_enum (value, loop_type); } /* set value function for loopsel_ctrl */ static void swamigui_sample_editor_loopsel_ctrl_set (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (SWAMI_CONTROL_FUNC_DATA (control)); int loop_type; loop_type = g_value_get_enum (value); /* block changed signal while setting the combo box selection */ g_signal_handlers_block_by_func (editor->loopsel, swamigui_sample_editor_loopsel_cb_changed, editor); swamigui_sample_editor_set_active_loop_type (editor, loop_type); g_signal_handlers_unblock_by_func (editor->loopsel, swamigui_sample_editor_loopsel_cb_changed, editor); } /* callback for when the loop selector combo box is changed */ static void swamigui_sample_editor_loopsel_cb_changed (GtkComboBox *widget, gpointer user_data) { SwamiguiSampleEditor *editor = SWAMIGUI_SAMPLE_EDITOR (user_data); GtkTreeIter iter; int loop_type; GValue value = { 0 }; if (gtk_combo_box_get_active_iter (widget, &iter)) { gtk_tree_model_get (GTK_TREE_MODEL (editor->loopsel_store), &iter, LOOPSEL_COL_LOOP_TYPE, &loop_type, -1); /* transmit the loop type change */ g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, loop_type); swami_control_transmit_value ((SwamiControl *)editor->loopsel_ctrl, &value); g_value_unset (&value); } } swami-2.2.0/src/swamigui/SwamiguiSampleEditor.h000066400000000000000000000236541361104770400215330ustar00rootroot00000000000000/* * SwamiguiSampleEditor.h - Sample editor widget header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SAMPLE_EDITOR_H__ #define __SWAMIGUI_SAMPLE_EDITOR_H__ #include #include #include #include #include #include #include typedef struct _SwamiguiSampleEditor SwamiguiSampleEditor; typedef struct _SwamiguiSampleEditorClass SwamiguiSampleEditorClass; #define SWAMIGUI_TYPE_SAMPLE_EDITOR (swamigui_sample_editor_get_type ()) #define SWAMIGUI_SAMPLE_EDITOR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_SAMPLE_EDITOR, \ SwamiguiSampleEditor)) #define SWAMIGUI_SAMPLE_EDITOR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SAMPLE_EDITOR, \ SwamiguiSampleEditorClass)) #define SWAMIGUI_IS_SAMPLE_EDITOR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_SAMPLE_EDITOR)) #define SWAMIGUI_IS_SAMPLE_EDITOR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_SAMPLE_EDITOR)) typedef enum { SWAMIGUI_SAMPLE_EDITOR_NORMAL, /* no particular status */ SWAMIGUI_SAMPLE_EDITOR_INIT, /* check selection and initialize */ SWAMIGUI_SAMPLE_EDITOR_UPDATE /* selection changed */ } SwamiguiSampleEditorStatus; /** * SwamiguiSampleEditorHandler: * @editor: Sample editor widget * * This function type is used to handle specific patch item types with * sample data and loop info. The @editor object * status field indicates the current * operation which is one of: * * %SWAMIGUI_SAMPLE_EDITOR_INIT - Check selection and initialize the * sample editor if the selection can be handled. Return %TRUE if * selection was handled, which will activate this handler, %FALSE * otherwise. * * %SWAMIGUI_SAMPLE_EDITOR_UPDATE - Item selection has changed, update * sample editor. Return %TRUE if selection change was handled, %FALSE * otherwise which will de-activate this handler. * * Other useful fields of a #SwamiguiSplits object include * selection which defines the current item * selection. * * Returns: Should return %TRUE if operation was handled, %FALSE otherwise. */ typedef gboolean (*SwamiguiSampleEditorHandler)(SwamiguiSampleEditor *editor); /* Sample editor object */ struct _SwamiguiSampleEditor { GtkHBox parent; /* derived from GtkVBox */ SwamiguiSampleEditorStatus status; /* current status */ IpatchList *selection; /* item selection */ SwamiguiSampleEditorHandler handler; /* active handler or NULL */ gpointer handler_data; /* handler defined pointer */ int marker_bar_height; /* height of top marker/loop meter bar */ GList *tracks; /* info for each sample (see SwamiguiSampleEditor.c) */ GList *markers; /* list of markers (see SwamiguiSampleEditor.c) */ guint sample_size; /* cached sample data length, in frames */ SwamiControl *loop_start_hub; /* SwamiControl hub for loop_view start props */ SwamiControl *loop_end_hub; /* SwamiControl hub for loop_view end props */ gboolean marker_cursor; /* TRUE if mouse cursor set for marker move */ int sel_marker; /* index of select marker or -1 */ int sel_marker_edge; /* selected edge -1 = start, 0 = both, 1 = end */ int move_range_ofs; /* if sel_marker_edge == 0, sample ofs of move */ int sel_state; /* see SelState in .c */ int sel_temp; /* tmp pos in samples when sel_state = SEL_MAYBE */ SwamiguiCanvasMod *sample_mod; /* zoom/scroll sample canvas modulator */ double scroll_acc; /* scroll accumulator (since scrolling is integer only) */ SwamiguiCanvasMod *loop_mod; /* zoom/scroll loop view canvas modulator */ double loop_zoom; /* remember last loop zoom between sample loads */ gboolean zoom_all; /* TRUE if entire sample zoomed (remains on resize) */ GtkWidget *mainvbox; /* vbox for toolbar and sample canvases */ GtkWidget *loop_finder_pane; /* pane for loop finder and mainvbox */ SwamiguiLoopFinder *loop_finder_gui; /* loop finder GUI widget */ gboolean loop_finder_active; /* TRUE if loop finder is currently shown */ GnomeCanvas *sample_canvas; /* sample canvas */ GnomeCanvas *loop_canvas; /* sample loop canvas */ GnomeCanvasItem *sample_border_line; /* sample marker bar horizontal line */ GnomeCanvasItem *loop_border_line; /* loop marker bar horizontal line */ GnomeCanvasItem *xsnap_line; /* X zoom/scroll snap line (vertical) */ GnomeCanvasItem *ysnap_line; /* Y zoom/scroll snap line (horizontal) */ GnomeCanvasItem *loop_line; /* loop view center line */ GnomeCanvasItem *loop_snap_line; /* loop zoom snap line */ GtkWidget *loopsel; /* loop selector GtkComboBox */ SwamiControl *loopsel_ctrl; /* control for loop selector */ GtkListStore *loopsel_store; /* loop selector GtkListStore model */ GtkWidget *spinbtn_start; /* start loop spin button */ GtkWidget *spinbtn_end; /* end loop spin button */ SwamiControl *spinbtn_start_ctrl; /* control for loop start spin button */ SwamiControl *spinbtn_end_ctrl; /* control for loop end spin button */ GtkWidget *hscrollbar; /* horizontal scrollbar widget */ GtkWidget *toolbar; /* sample editor toolbar */ GtkWidget *cut_button; /* sample cut button */ GtkWidget *crop_button; /* sample crop button */ GtkWidget *copy_new_button; /* sample copy to new button */ GtkWidget *finder_button; /* loop finder activation button */ GtkWidget *samplesel_button; /* sample selector button */ GtkWidget *dicer_button; /* sample dicer toggle button */ GtkWidget *new_button; /* create new sample from selection button */ GtkWidget *new_name; /* new sample name entry */ guint center_line_color; /* horizontal center line color */ guint marker_border_color; /* color of marker bar horizontal border lines */ guint snap_line_color; /* zoom/scroll snap line color */ guint loop_line_color; /* loop view center line color */ }; /* Sample editor object class */ struct _SwamiguiSampleEditorClass { GtkHBoxClass parent_class; }; /* Flags for swamigui_sample_editor_add_marker() */ typedef enum { SWAMIGUI_SAMPLE_EDITOR_MARKER_SINGLE = 1 << 0, /* Single value (not range) */ SWAMIGUI_SAMPLE_EDITOR_MARKER_VIEW = 1 << 1, /* view only marker */ SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE = 1 << 2 /* a start/size marker */ } SwamiguiSampleEditorMarkerFlags; /* Builtin markers (always present, although perhaps hidden) */ typedef enum { SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_SELECTION, /* selection marker */ SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_START, /* loop find start window */ SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_END /* loop find end window */ } SwamiguiSampleEditorMarkerId; GType swamigui_sample_editor_get_type (void); GtkWidget *swamigui_sample_editor_new (void); void swamigui_sample_editor_zoom_ofs (SwamiguiSampleEditor *editor, double zoom_amt, double zoom_xpos); void swamigui_sample_editor_scroll_ofs (SwamiguiSampleEditor *editor, int sample_ofs); void swamigui_sample_editor_loop_zoom (SwamiguiSampleEditor *editor, double zoom_amt); void swamigui_sample_editor_set_selection (SwamiguiSampleEditor *editor, IpatchList *items); IpatchList *swamigui_sample_editor_get_selection (SwamiguiSampleEditor *editor); void swamigui_sample_editor_register_handler (SwamiguiSampleEditorHandler handler, SwamiguiPanelCheckFunc check_func); void swamigui_sample_editor_unregister_handler (SwamiguiSampleEditorHandler handler); void swamigui_sample_editor_reset (SwamiguiSampleEditor *editor); void swamigui_sample_editor_get_loop_controls (SwamiguiSampleEditor *editor, SwamiControl **loop_start, SwamiControl **loop_end); int swamigui_sample_editor_add_track (SwamiguiSampleEditor *editor, IpatchSampleData *sample, gboolean right_chan); gboolean swamigui_sample_editor_get_track_info (SwamiguiSampleEditor *editor, guint track, IpatchSampleData **sample, SwamiguiSampleCanvas **sample_view, SwamiguiSampleCanvas **loop_view); void swamigui_sample_editor_remove_track (SwamiguiSampleEditor *editor, guint track); void swamigui_sample_editor_remove_all_tracks (SwamiguiSampleEditor *editor); guint swamigui_sample_editor_add_marker (SwamiguiSampleEditor *editor, guint flags, SwamiControl **start, SwamiControl **end); gboolean swamigui_sample_editor_get_marker_info (SwamiguiSampleEditor *editor, guint marker, guint *flags, GnomeCanvasItem **start_line, GnomeCanvasItem **end_line, SwamiControl **start_ctrl, SwamiControl **end_ctrl); void swamigui_sample_editor_set_marker (SwamiguiSampleEditor *editor, guint marker, guint start, guint end); void swamigui_sample_editor_remove_marker (SwamiguiSampleEditor *editor, guint marker); void swamigui_sample_editor_remove_all_markers (SwamiguiSampleEditor *editor); void swamigui_sample_editor_show_marker (SwamiguiSampleEditor *editor, guint marker, gboolean show_marker); void swamigui_sample_editor_set_loop_types (SwamiguiSampleEditor *editor, int *types, gboolean loop_play_btn); void swamigui_sample_editor_set_active_loop_type (SwamiguiSampleEditor *editor, int type); #endif swami-2.2.0/src/swamigui/SwamiguiSpectrumCanvas.c000066400000000000000000000531351361104770400220710ustar00rootroot00000000000000/* * SwamiguiSpectrumCanvas.c - Spectrum frequency canvas item * A canvas item for displaying frequency spectrum data * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include "SwamiguiSpectrumCanvas.h" #include "util.h" #include "i18n.h" enum { PROP_0, PROP_ADJUSTMENT, /* adjustment control */ PROP_X, /* x position in pixels */ PROP_Y, /* y position in pixels */ PROP_WIDTH, /* width of view in pixels */ PROP_HEIGHT, /* height of view in pixels */ PROP_START, /* spectrum start index */ PROP_ZOOM, /* zoom value */ PROP_ZOOM_AMPL /* amplitude zoom */ }; static void swamigui_spectrum_canvas_class_init (SwamiguiSpectrumCanvasClass *klass); static void swamigui_spectrum_canvas_init (SwamiguiSpectrumCanvas *canvas); static void swamigui_spectrum_canvas_finalize (GObject *object); static void swamigui_spectrum_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_spectrum_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_spectrum_canvas_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags); static void swamigui_spectrum_canvas_realize (GnomeCanvasItem *item); static void swamigui_spectrum_canvas_unrealize (GnomeCanvasItem *item); static void swamigui_spectrum_canvas_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height); static double swamigui_spectrum_canvas_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item); static void swamigui_spectrum_canvas_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2); static void swamigui_spectrum_canvas_cb_adjustment_value_changed (GtkAdjustment *adj, gpointer user_data); static void swamigui_spectrum_canvas_update_adjustment (SwamiguiSpectrumCanvas *canvas); static GObjectClass *parent_class = NULL; GType swamigui_spectrum_canvas_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiSpectrumCanvasClass), NULL, NULL, (GClassInitFunc) swamigui_spectrum_canvas_class_init, NULL, NULL, sizeof (SwamiguiSpectrumCanvas), 0, (GInstanceInitFunc) swamigui_spectrum_canvas_init, }; obj_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "SwamiguiSpectrumCanvas", &obj_info, 0); } return (obj_type); } static void swamigui_spectrum_canvas_class_init (SwamiguiSpectrumCanvasClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_spectrum_canvas_set_property; obj_class->get_property = swamigui_spectrum_canvas_get_property; obj_class->finalize = swamigui_spectrum_canvas_finalize; item_class->update = swamigui_spectrum_canvas_update; item_class->realize = swamigui_spectrum_canvas_realize; item_class->unrealize = swamigui_spectrum_canvas_unrealize; item_class->draw = swamigui_spectrum_canvas_draw; item_class->point = swamigui_spectrum_canvas_point; item_class->bounds = swamigui_spectrum_canvas_bounds; g_object_class_install_property (obj_class, PROP_ADJUSTMENT, g_param_spec_object ("adjustment", _("Adjustment"), _("Adjustment control for scrolling"), GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_X, g_param_spec_int ("x", "X", _("X position in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_Y, g_param_spec_int ("y", "Y", _("Y position in pixels"), 0, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_WIDTH, g_param_spec_int ("width", _("Width"), _("Width in pixels"), 0, G_MAXINT, 1, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_HEIGHT, g_param_spec_int ("height", _("Height"), _("Height in pixels"), 0, G_MAXINT, 1, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_START, g_param_spec_uint ("start", _("View start"), _("Start index of spectrum in view"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ZOOM, g_param_spec_double ("zoom", _("Zoom"), _("Zoom factor in indexes per pixel"), 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_ZOOM_AMPL, g_param_spec_double ("zoom-ampl", _("Zoom Amplitude"), _("Amplitude zoom factor"), 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE)); /* g_object_class_install_property (obj_class, PROP_COLOR, g_param_spec_string ("color", _("Color"), _("Spectrum color"), NULL, G_PARAM_WRITABLE)); g_object_class_install_property (obj_class, PROP_COLOR_RGBA, g_param_spec_uint ("color-rgba", _("Color RGBA"), _("Spectrum color RGBA"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); */ } static void swamigui_spectrum_canvas_init (SwamiguiSpectrumCanvas *canvas) { canvas->adj = (GtkAdjustment *)gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); g_object_ref (canvas->adj); canvas->update_adj = TRUE; g_signal_connect (canvas->adj, "value-changed", G_CALLBACK (swamigui_spectrum_canvas_cb_adjustment_value_changed), canvas); canvas->spectrum = NULL; canvas->max_value = 0.0; canvas->start = 0; canvas->zoom = 1.0; canvas->zoom_ampl = 1.0; canvas->x = 0; canvas->y = 0; canvas->width = 0; canvas->height = 0; } static void swamigui_spectrum_canvas_finalize (GObject *object) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (object); if (canvas->spectrum && canvas->notify) canvas->notify (canvas->spectrum, canvas->spectrum_size); if (canvas->adj) { g_signal_handlers_disconnect_by_func (canvas->adj, G_CALLBACK (swamigui_spectrum_canvas_cb_adjustment_value_changed), canvas); g_object_unref (canvas->adj); } if (canvas->bar_gc) g_object_unref (canvas->bar_gc); if (canvas->min_gc) g_object_unref (canvas->min_gc); if (canvas->max_gc) g_object_unref (canvas->max_gc); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); } static void swamigui_spectrum_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object); SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (object); GtkAdjustment *gtkadj; switch (property_id) { case PROP_ADJUSTMENT: gtkadj = g_value_get_object (value); g_return_if_fail (GTK_IS_ADJUSTMENT (gtkadj)); g_signal_handlers_disconnect_by_func (canvas->adj, G_CALLBACK (swamigui_spectrum_canvas_cb_adjustment_value_changed), canvas); g_object_unref (canvas->adj); canvas->adj = GTK_ADJUSTMENT (g_object_ref (gtkadj)); g_signal_connect (canvas->adj, "value-changed", G_CALLBACK (swamigui_spectrum_canvas_cb_adjustment_value_changed), canvas); /* only initialize adjustment if updates enabled */ if (canvas->update_adj) swamigui_spectrum_canvas_update_adjustment (canvas); break; case PROP_X: canvas->x = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_Y: canvas->y = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_WIDTH: canvas->width = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->page_size = canvas->width * canvas->zoom; gtk_adjustment_changed (canvas->adj); } break; case PROP_HEIGHT: canvas->height = g_value_get_int (value); canvas->need_bbox_update = TRUE; gnome_canvas_item_request_update (item); break; case PROP_START: canvas->start = g_value_get_uint (value); gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->value = canvas->start; g_signal_handlers_block_by_func (canvas->adj, swamigui_spectrum_canvas_cb_adjustment_value_changed, canvas); gtk_adjustment_value_changed (canvas->adj); g_signal_handlers_unblock_by_func (canvas->adj, swamigui_spectrum_canvas_cb_adjustment_value_changed, canvas); } break; case PROP_ZOOM: canvas->zoom = g_value_get_double (value); gnome_canvas_item_request_update (item); /* only update adjustment if updates enabled */ if (canvas->update_adj) { canvas->adj->page_size = canvas->width * canvas->zoom; gtk_adjustment_changed (canvas->adj); } break; case PROP_ZOOM_AMPL: canvas->zoom_ampl = g_value_get_double (value); gnome_canvas_item_request_update (item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); return; /* return, to skip code below */ } } static void swamigui_spectrum_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (object); switch (property_id) { case PROP_ADJUSTMENT: g_value_set_object (value, canvas->adj); break; case PROP_X: g_value_set_int (value, canvas->x); break; case PROP_Y: g_value_set_int (value, canvas->y); break; case PROP_WIDTH: g_value_set_int (value, canvas->width); break; case PROP_HEIGHT: g_value_set_int (value, canvas->height); break; case PROP_START: g_value_set_uint (value, canvas->start); break; case PROP_ZOOM: g_value_set_double (value, canvas->zoom); break; case PROP_ZOOM_AMPL: g_value_set_double (value, canvas->zoom_ampl); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* Gnome canvas item update handler */ static void swamigui_spectrum_canvas_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY) && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE)) || (flags & GNOME_CANVAS_UPDATE_AFFINE) || canvas->need_bbox_update) { canvas->need_bbox_update = FALSE; gnome_canvas_update_bbox (item, canvas->x, canvas->y, canvas->x + canvas->width, canvas->y + canvas->height); } else gnome_canvas_request_redraw (item->canvas, canvas->x, canvas->y, canvas->x + canvas->width, canvas->y + canvas->height); if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) GNOME_CANVAS_ITEM_CLASS (parent_class)->update (item, affine, clip_path, flags); } static void swamigui_spectrum_canvas_realize (GnomeCanvasItem *item) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); GdkColor bar_color = { 0x0, 0xFFFF, 0, 0 }; GdkColor min_color = { 0x0, 0, 0, 0xFFFF }; GdkColor max_color = { 0x0, 0, 0xFFFF, 0 }; if (GNOME_CANVAS_ITEM_CLASS (parent_class)->realize) (* GNOME_CANVAS_ITEM_CLASS (parent_class)->realize)(item); canvas->bar_gc = gdk_gc_new (item->canvas->layout.bin_window); /* ++ ref */ gdk_gc_set_rgb_fg_color (canvas->bar_gc, &bar_color); canvas->min_gc = gdk_gc_new (item->canvas->layout.bin_window); /* ++ ref */ gdk_gc_set_rgb_fg_color (canvas->min_gc, &min_color); canvas->max_gc = gdk_gc_new (item->canvas->layout.bin_window); /* ++ ref */ gdk_gc_set_rgb_fg_color (canvas->max_gc, &max_color); } static void swamigui_spectrum_canvas_unrealize (GnomeCanvasItem *item) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); if (canvas->bar_gc) gdk_gc_unref (canvas->bar_gc); if (canvas->min_gc) gdk_gc_unref (canvas->min_gc); if (canvas->max_gc) gdk_gc_unref (canvas->max_gc); canvas->bar_gc = NULL; canvas->min_gc = NULL; canvas->max_gc = NULL; if (GNOME_CANVAS_ITEM_CLASS (parent_class)->unrealize) GNOME_CANVAS_ITEM_CLASS (parent_class)->unrealize (item); } /* GnomeCanvas draws in 512 pixel squares, allocate some extra for overlap */ #define STATIC_POINTS 544 static void swamigui_spectrum_canvas_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); GdkSegment static_segments[STATIC_POINTS]; GdkSegment *segments; GdkRectangle rect; int size, start, end, index, next_index; int height_1, height_1_ofs; double ampl_mul; double min, max, val; int xpos, ypos, xofs, yofs; if (!canvas->spectrum) return; /* set GC clipping rectangle */ rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; size = canvas->spectrum_size; xofs = x - canvas->x; /* x co-ordinate relative to spectrum pos */ yofs = y - canvas->y; /* y co-ordinate relative to spectrum pos */ /* calculate start index */ start = (int)(canvas->start + xofs * canvas->zoom + 0.5); /* calculate end index */ end = (int)(canvas->start + (xofs + width) * canvas->zoom + 0.5); /* no spectrum in area? */ if (start >= size || end < 0) return; start = CLAMP (start, 0, size - 1); end = CLAMP (end, 0, size - 1); height_1 = canvas->height - 1; /* height - 1 */ height_1_ofs = height_1 - yofs; /* height - 1 corrected to current ofs */ /* spectrum amplitude multiplier */ ampl_mul = height_1 * (canvas->zoom_ampl / canvas->max_value); if (canvas->zoom >= 1.0) { gdk_gc_set_clip_origin (canvas->min_gc, 0, 0); gdk_gc_set_clip_rectangle (canvas->min_gc, &rect); gdk_gc_set_clip_origin (canvas->max_gc, 0, 0); gdk_gc_set_clip_rectangle (canvas->max_gc, &rect); /* use static segment array if there is enough, otherwise fall back on malloc (shouldn't get used, but just in case) */ if (width > STATIC_POINTS) segments = g_new (GdkSegment, width); else segments = static_segments; max = -G_MAXDOUBLE; next_index = (int)(canvas->start + (xofs + 1) * canvas->zoom + 0.5); /* draw maximum lines */ for (xpos = 0, index = start; xpos < width; xpos++) { for (; index < next_index && index < size; index++) { val = canvas->spectrum[index]; if (val > max) max = val; } segments[xpos].x1 = xpos; segments[xpos].x2 = xpos; segments[xpos].y1 = (gint)(height_1_ofs - max * ampl_mul); segments[xpos].y2 = height_1_ofs; if (index >= size) break; next_index = (int)(canvas->start + (xofs + xpos + 2) * canvas->zoom + 0.5); max = -G_MAXDOUBLE; } gdk_draw_segments (drawable, canvas->max_gc, segments, xpos); min = G_MAXDOUBLE; next_index = (int)(canvas->start + (xofs + 1) * canvas->zoom + 0.5); /* draw minimum lines */ for (xpos = 0, index = start; xpos < width; xpos++) { for (; index < next_index && index < size; index++) { val = canvas->spectrum[index]; if (val < min) min = val; } segments[xpos].x1 = xpos; segments[xpos].x2 = xpos; segments[xpos].y1 = (gint)(height_1_ofs - min * ampl_mul); segments[xpos].y2 = height_1_ofs; if (index >= size) break; next_index = (int)(canvas->start + (xofs + xpos + 2) * canvas->zoom + 0.5); min = G_MAXDOUBLE; } gdk_draw_segments (drawable, canvas->min_gc, segments, xpos); if (segments != static_segments) g_free (segments); } else /* zoom < 1.0 (do bars) */ { gdk_gc_set_clip_origin (canvas->bar_gc, 0, 0); gdk_gc_set_clip_rectangle (canvas->bar_gc, &rect); if (start > 0) start--; /* draw previous bar for overlap */ /* first xpos co-ordinate */ xpos = (int)((start - (int)canvas->start) / canvas->zoom - xofs + 0.5); for (index = start; index <= end; index++) { ypos = (int)(height_1_ofs - canvas->spectrum[index] * ampl_mul); val = (index - canvas->start + 1) / canvas->zoom - xofs + 0.5; gdk_draw_rectangle (drawable, canvas->bar_gc, TRUE, xpos, ypos, (gint)(val - xpos + 1), height_1_ofs - ypos + 1); xpos = (int)val; } } } static double swamigui_spectrum_canvas_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); double points[2*4]; points[0] = canvas->x; points[1] = canvas->y; points[2] = canvas->x + canvas->width; points[3] = points[1]; points[4] = points[0]; points[5] = canvas->y + canvas->height; points[6] = points[2]; points[7] = points[5]; *actual_item = item; return (gnome_canvas_polygon_to_point (points, 4, cx, cy)); } static void swamigui_spectrum_canvas_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (item); *x1 = canvas->x; *y1 = canvas->y; *x2 = canvas->x + canvas->width; *y2 = canvas->y + canvas->height; } static void swamigui_spectrum_canvas_cb_adjustment_value_changed (GtkAdjustment *adj, gpointer user_data) { SwamiguiSpectrumCanvas *canvas = SWAMIGUI_SPECTRUM_CANVAS (user_data); guint start; canvas->update_adj = FALSE; /* disable adjustment updates to stop adjustment loop */ start = (guint)adj->value; g_object_set (canvas, "start", start, NULL); canvas->update_adj = TRUE; /* re-enable adjustment updates */ } /** * swamigui_spectrum_canvas_set_data: * @canvas: Spectrum data canvas item * @spectrum: Spectrum data pointer * @size: Size of @spectrum data (in values, not bytes) * @notify: Function callback for freeing @spectrum data when spectrum * canvas doesn't need it anymore. * * Set the spectrum data of a spectrum canvas item. */ void swamigui_spectrum_canvas_set_data (SwamiguiSpectrumCanvas *canvas, double *spectrum, guint size, SwamiguiSpectrumDestroyNotify notify) { double max = 0.0; int i; g_return_if_fail (SWAMIGUI_IS_SPECTRUM_CANVAS (canvas)); g_return_if_fail (!spectrum || size > 0); if (spectrum == canvas->spectrum) return; /* call destroy notify if spectrum and notify is set */ if (canvas->spectrum && canvas->notify) canvas->notify (canvas->spectrum, canvas->spectrum_size); canvas->spectrum = spectrum; canvas->spectrum_size = spectrum ? size : 0; canvas->notify = notify; /* find maximum value of spectrum */ for (i = size - 1; i >= 0; i--) { if (spectrum[i] > max) max = spectrum[i]; } canvas->max_value = max; swamigui_spectrum_canvas_update_adjustment (canvas); gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (canvas)); } static void swamigui_spectrum_canvas_update_adjustment (SwamiguiSpectrumCanvas *canvas) { canvas->adj->lower = 0.0; canvas->adj->upper = canvas->spectrum_size; canvas->adj->value = 0.0; canvas->adj->step_increment = canvas->spectrum_size / 400.0; canvas->adj->page_increment = canvas->spectrum_size / 50.0; canvas->adj->page_size = canvas->spectrum_size; gtk_adjustment_changed (canvas->adj); g_signal_handlers_block_by_func (canvas->adj, swamigui_spectrum_canvas_cb_adjustment_value_changed, canvas); gtk_adjustment_value_changed (canvas->adj); g_signal_handlers_unblock_by_func (canvas->adj, swamigui_spectrum_canvas_cb_adjustment_value_changed, canvas); } /** * swamigui_spectrum_canvas_pos_to_spectrum: * @canvas: Spectrum canvas item * @xpos: X pixel position * * Convert an X pixel position to spectrum index. * * Returns: Spectrum index or -1 if out of range. */ int swamigui_spectrum_canvas_pos_to_spectrum (SwamiguiSpectrumCanvas *canvas, int xpos) { int index, spectrum_size; g_return_val_if_fail (SWAMIGUI_IS_SPECTRUM_CANVAS (canvas), -1); index = (int)(canvas->start + canvas->zoom * xpos); spectrum_size = canvas->spectrum_size; if (index < 0 || index > spectrum_size) return (-1); return (index); } /** * swamigui_spectrum_canvas_spectrum_to_pos: * @canvas: Spectrum canvas item * @index: Spectrum index * * Convert a spectrum index to x pixel position. * * Returns: X position, or -1 if out of view. */ int swamigui_spectrum_canvas_spectrum_to_pos (SwamiguiSpectrumCanvas *canvas, int index) { int xpos, start; g_return_val_if_fail (SWAMIGUI_IS_SPECTRUM_CANVAS (canvas), -1); start = canvas->start; if (index < start) return -1; /* index before view start? */ xpos = (int)((index - start) / canvas->zoom + 0.5); return (xpos < canvas->width) ? xpos : -1; /* return if not after view end */ } swami-2.2.0/src/swamigui/SwamiguiSpectrumCanvas.h000066400000000000000000000072301361104770400220710ustar00rootroot00000000000000/* * SwamiguiSpectrumCanvas.h - Spectrum frequency canvas item * A canvas item for displaying frequency spectrum data * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SPECTRUM_CANVAS_H__ #define __SWAMIGUI_SPECTRUM_CANVAS_H__ #include #include #include typedef struct _SwamiguiSpectrumCanvas SwamiguiSpectrumCanvas; typedef struct _SwamiguiSpectrumCanvasClass SwamiguiSpectrumCanvasClass; #define SWAMIGUI_TYPE_SPECTRUM_CANVAS (swamigui_spectrum_canvas_get_type ()) #define SWAMIGUI_SPECTRUM_CANVAS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_SPECTRUM_CANVAS, \ SwamiguiSpectrumCanvas)) #define SWAMIGUI_SPECTRUM_CANVAS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SPECTRUM_CANVAS, \ SwamiguiSpectrumCanvasClass)) #define SWAMIGUI_IS_SPECTRUM_CANVAS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_SPECTRUM_CANVAS)) #define SWAMIGUI_IS_SPECTRUM_CANVAS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_SPECTRUM_CANVAS)) /** * SwamiguiSpectrumDestroyNotify: * @spectrum: The spectrum data pointer as passed to * swamigui_spectrum_canvas_set_data(). * @size: The number of values in the @spectrum array * * This is a function type that gets called when a spectrum canvas item * is destroyed. This function is responsible for freeing @spectrum. */ typedef void (*SwamiguiSpectrumDestroyNotify)(double *spectrum, guint size); /* Spectrum canvas item */ struct _SwamiguiSpectrumCanvas { GnomeCanvasItem parent_instance; /*< private >*/ double *spectrum; /* spectrum data */ guint spectrum_size; /* number of values in spectrum data */ SwamiguiSpectrumDestroyNotify notify; /* notify function for spectrum data */ double max_value; /* maximum value in spectrum data */ GtkAdjustment *adj; /* adjustment for view */ gboolean update_adj; /* TRUE if adj should be updated (to stop loop) */ guint start; /* start spectrum index */ double zoom; /* zoom factor indexes/pixel */ double zoom_ampl; /* amplitude zoom factor */ int x, y; /* x, y coordinates of spectrum item */ int width, height; /* width and height in pixels */ GdkGC *min_gc; /* GC for drawing minimum lines */ GdkGC *max_gc; /* GC for drawing maximum lines */ GdkGC *bar_gc; /* GC for spectrum bars (zoom < 1.0) */ guint need_bbox_update : 1; /* set if bbox needs to be updated */ }; struct _SwamiguiSpectrumCanvasClass { GnomeCanvasItemClass parent_class; }; GType swamigui_spectrum_canvas_get_type (void); void swamigui_spectrum_canvas_set_data (SwamiguiSpectrumCanvas *canvas, double *spectrum, guint size, SwamiguiSpectrumDestroyNotify notify); int swamigui_spectrum_canvas_pos_to_spectrum (SwamiguiSpectrumCanvas *canvas, int xpos); int swamigui_spectrum_canvas_spectrum_to_pos (SwamiguiSpectrumCanvas *canvas, int index); #endif swami-2.2.0/src/swamigui/SwamiguiSpinScale.c000066400000000000000000000250261361104770400210120ustar00rootroot00000000000000/* * SwamiguiSpinScale.c - A GtkSpinButton/GtkScale combo widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include #include "SwamiguiSpinScale.h" #include "i18n.h" #include "util.h" enum { PROP_0, PROP_ADJUSTMENT, PROP_DIGITS, PROP_VALUE, PROP_SCALE_FIRST /* the order of the widgets */ }; /* Local Prototypes */ static void swamigui_spin_scale_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_spin_scale_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_spin_scale_init (SwamiguiSpinScale *spin_scale); static gboolean swamigui_spin_scale_cb_output (GtkSpinButton *spin_button, gpointer user_data); static gint swamigui_spin_scale_cb_input (GtkSpinButton *spinbutton, gdouble *newval, gpointer user_data); static void swamigui_spin_scale_cb_activate (GtkSpinButton *spin_button, gpointer user_data); static gboolean swamigui_spin_scale_real_set_order (SwamiguiSpinScale *spin_scale, gboolean scale_first); /* define the SwamiguiSpinScale type */ G_DEFINE_TYPE (SwamiguiSpinScale, swamigui_spin_scale, GTK_TYPE_HBOX); static void swamigui_spin_scale_class_init (SwamiguiSpinScaleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = swamigui_spin_scale_set_property; obj_class->get_property = swamigui_spin_scale_get_property; g_object_class_install_property (obj_class, PROP_ADJUSTMENT, g_param_spec_object ("adjustment", "Adjustment", "Adjustment", GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_DIGITS, g_param_spec_uint ("digits", "Digits", "Digits", 0, 20, 0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_VALUE, g_param_spec_double ("value", "Value", "Value", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SCALE_FIRST, g_param_spec_boolean ("scale-first", "Scale first", "Scale first", FALSE, G_PARAM_READWRITE)); } static void swamigui_spin_scale_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiSpinScale *sc = SWAMIGUI_SPIN_SCALE (object); GtkAdjustment *adj; guint digits; double d; switch (property_id) { case PROP_ADJUSTMENT: adj = GTK_ADJUSTMENT (g_value_get_object (value)); gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (sc->spinbtn), adj); gtk_range_set_adjustment (GTK_RANGE (sc->hscale), adj); break; case PROP_DIGITS: digits = g_value_get_uint (value); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (sc->spinbtn), digits); gtk_scale_set_digits (GTK_SCALE (sc->hscale), digits); break; case PROP_VALUE: d = g_value_get_double (value); adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sc->spinbtn)); gtk_adjustment_set_value (adj, d); break; case PROP_SCALE_FIRST: swamigui_spin_scale_real_set_order (sc, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_spin_scale_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiSpinScale *sc = SWAMIGUI_SPIN_SCALE (object); switch (property_id) { case PROP_ADJUSTMENT: g_value_set_object (value, (GObject *)gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sc->spinbtn))); break; case PROP_DIGITS: g_value_set_uint (value, gtk_spin_button_get_digits (GTK_SPIN_BUTTON (sc->spinbtn))); break; case PROP_VALUE: g_value_set_double (value, gtk_spin_button_get_value (GTK_SPIN_BUTTON (sc->spinbtn))); break; case PROP_SCALE_FIRST: g_value_set_boolean (value, sc->scale_first); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_spin_scale_init (SwamiguiSpinScale *spin_scale) { GtkAdjustment *adj; spin_scale->scale_first = FALSE; adj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); spin_scale->spinbtn = gtk_spin_button_new (adj, 1.0, 0); gtk_widget_show (spin_scale->spinbtn); gtk_box_pack_start (GTK_BOX (spin_scale), spin_scale->spinbtn, FALSE, FALSE, 0); /* Because text displayed in "text entry" is expected to have a different representation and different resolution that adjustment value, it is important to keep: 1)"text entry" input disconnected from internal automatic update of adjustment value. 2)"spin button" input disconnected from internal automatic update of "text entry" value. 3)cb output being solely responsable of update "text entry" value converted from adjustment value. To insure (1) and (2) we need to connect callbacks on both signals "activate" and "input". To insure (3) we need to connect a callback on "output" signal. */ g_signal_connect (spin_scale->spinbtn, "output", G_CALLBACK (swamigui_spin_scale_cb_output), spin_scale); g_signal_connect (spin_scale->spinbtn, "input", G_CALLBACK (swamigui_spin_scale_cb_input), spin_scale); g_signal_connect (spin_scale->spinbtn, "activate", G_CALLBACK (swamigui_spin_scale_cb_activate), spin_scale); spin_scale->hscale = gtk_hscale_new (adj); gtk_scale_set_draw_value (GTK_SCALE (spin_scale->hscale), FALSE); gtk_widget_show (spin_scale->hscale); gtk_box_pack_start (GTK_BOX (spin_scale), spin_scale->hscale, TRUE, TRUE, 0); } /* Callback for spin button output update (transform value if transform function set) */ static gboolean swamigui_spin_scale_cb_output (GtkSpinButton *spin_button, gpointer user_data) { SwamiguiSpinScale *spin_scale = SWAMIGUI_SPIN_SCALE (user_data); GValue adjval = { 0 }, dispval = { 0 }; GtkAdjustment *adj; gchar *text; int digits; if (spin_scale->adj_units == IPATCH_UNIT_TYPE_NONE) return (FALSE); adj = gtk_spin_button_get_adjustment (spin_button); g_value_init (&adjval, G_TYPE_DOUBLE); g_value_set_double (&adjval, gtk_adjustment_get_value (adj)); g_value_init (&dispval, G_TYPE_DOUBLE); ipatch_unit_convert (spin_scale->adj_units, spin_scale->disp_units, &adjval, &dispval); digits = gtk_spin_button_get_digits (spin_button); text = g_strdup_printf ("%.*f", digits, g_value_get_double (&dispval)); // ++ alloc gtk_entry_set_text (GTK_ENTRY (spin_button), text); g_free (text); // -- free // Probably not needed - but just for good measure g_value_unset (&adjval); g_value_unset (&dispval); return (TRUE); } /* Callback for spin button input */ static gint swamigui_spin_scale_cb_input (GtkSpinButton *spinbutton, gdouble *newval, gpointer user_data) { GtkAdjustment *adj = gtk_spin_button_get_adjustment (spinbutton); *newval = adj->value; return TRUE; } /* Callback for "text entry" input. Take text entry value and convert it to adjustment value. */ static void swamigui_spin_scale_cb_activate (GtkSpinButton *spin_button, gpointer user_data) { SwamiguiSpinScale *spin_scale = SWAMIGUI_SPIN_SCALE (user_data); GtkAdjustment *adj; GValue adjval = { 0 }, dispval = { 0 }; /* take text coming from "text entry". */ /* The buffer is internal and shouldn't be freed. */ const char *text = gtk_entry_get_text (GTK_ENTRY (spin_button)); if (!text) { return; } /* Convert dispval -> adjval */ g_value_init (&dispval, G_TYPE_DOUBLE); g_value_set_double (&dispval, atof (text)); g_value_init (&adjval, G_TYPE_DOUBLE); ipatch_unit_convert (spin_scale->disp_units, spin_scale->adj_units, &dispval, &adjval); /* set adjustement value */ adj = gtk_spin_button_get_adjustment (spin_button); gtk_adjustment_set_value (adj, g_value_get_double(&adjval) ); return; } /** * swamigui_spin_scale_new: * * Create a new spin button/scale combo widget. * * Returns: New widget. */ GtkWidget * swamigui_spin_scale_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_SPIN_SCALE, NULL))); } /** * swamigui_spin_scale_set_order: * @spin_scale: Spin scale widget * @scale_first: %TRUE if the GtkHScale should be before the GtkSpinButton, * %FALSE otherwise. * * Sets the order that the horizontal scale and spin button widgets appear. */ void swamigui_spin_scale_set_order (SwamiguiSpinScale *spin_scale, gboolean scale_first) { if (swamigui_spin_scale_real_set_order (spin_scale, scale_first)) g_object_notify (G_OBJECT (spin_scale), "scale-first"); } static gboolean swamigui_spin_scale_real_set_order (SwamiguiSpinScale *spin_scale, gboolean scale_first) { g_return_val_if_fail (SWAMIGUI_IS_SPIN_SCALE (spin_scale), FALSE); scale_first = (scale_first != 0); /* force booleanize it */ if (spin_scale->scale_first == scale_first) return (FALSE); spin_scale->scale_first = scale_first; gtk_box_reorder_child (GTK_BOX (spin_scale), spin_scale->hscale, !scale_first); return (TRUE); } /** * swamigui_spin_scale_set_transform: * @spin_scale: Spin scale widget * @adj_units: (type IpatchUnitType): Adjustment control units * @disp_units: (type IpatchUnitType): Spin button display units * * Since: 2.1.0 */ void swamigui_spin_scale_set_transform (SwamiguiSpinScale *spin_scale, guint16 adj_units, guint16 disp_units) { IpatchUnitInfo *unitinfo; g_return_if_fail (SWAMIGUI_IS_SPIN_SCALE (spin_scale)); spin_scale->adj_units = adj_units; spin_scale->disp_units = disp_units; unitinfo = ipatch_unit_lookup (disp_units); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin_scale->spinbtn), unitinfo ? unitinfo->digits : 0); } swami-2.2.0/src/swamigui/SwamiguiSpinScale.h000066400000000000000000000047001361104770400210130ustar00rootroot00000000000000/* * SwamiguiSpinScale.h - A GtkSpinButton/GtkScale combo widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SPIN_SCALE_H__ #define __SWAMIGUI_SPIN_SCALE_H__ #include #include #include typedef struct _SwamiguiSpinScale SwamiguiSpinScale; typedef struct _SwamiguiSpinScaleClass SwamiguiSpinScaleClass; #define SWAMIGUI_TYPE_SPIN_SCALE (swamigui_spin_scale_get_type ()) #define SWAMIGUI_SPIN_SCALE(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_SPIN_SCALE, SwamiguiSpinScale)) #define SWAMIGUI_SPIN_SCALE_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SPIN_SCALE, \ SwamiguiSpinScaleClass)) #define SWAMIGUI_IS_SPIN_SCALE(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_SPIN_SCALE)) #define SWAMIGUI_IS_SPIN_SCALE_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_SPIN_SCALE)) /* Swami SpinScale widget */ struct _SwamiguiSpinScale { GtkHBox parent; GtkWidget *spinbtn; /* spin button widget */ GtkWidget *hscale; /* horizontal scale widget */ gboolean scale_first; /* indicates order of widgets */ guint16 adj_units; // Adjustment units (#IpatchUnitType) guint16 disp_units; // Spin button display units (#IpatchUnitType) }; /* Swami SpinScale widget class */ struct _SwamiguiSpinScaleClass { GtkHBoxClass parent_class; }; GType swamigui_spin_scale_get_type (void); GtkWidget *swamigui_spin_scale_new (void); void swamigui_spin_scale_set_order (SwamiguiSpinScale *spin_scale, gboolean scale_first); void swamigui_spin_scale_set_transform (SwamiguiSpinScale *spin_scale, guint16 adj_units, guint16 disp_units); #endif swami-2.2.0/src/swamigui/SwamiguiSplits.c000066400000000000000000001767501361104770400204220ustar00rootroot00000000000000/* * SwamiguiSplits.c - Key/velocity splits widget * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "SwamiguiSplits.h" #include "SwamiguiControl.h" #include "SwamiguiPiano.h" #include "SwamiguiRoot.h" #include "SwamiguiStatusbar.h" #include "icons.h" #include "i18n.h" #include "util.h" enum { PROP_0, PROP_ITEM_SELECTION, PROP_SPLITS_ITEM, PROP_PIANO }; /* Value used for active_drag field in SwamiguiSplits which indicates the * current drag mode */ typedef enum { ACTIVE_NONE, /* Inactive drag */ ACTIVE_LOW, /* Dragging low handle of span */ ACTIVE_HIGH, /* Dragging upper handle of span */ ACTIVE_UNDECIDED, /* Not yet decided which handle of span to drag */ ACTIVE_MOVE_ROOTNOTES, /* Moving root note(s) */ ACTIVE_MOVE_RANGES, /* Dragging note/velocity range(s) */ ACTIVE_MOVE_BOTH /* Moving both root notes and range(s) */ } ActiveDrag; /* min/max width of piano/splits in pixels */ #define MIN_SPLITS_WIDTH SWAMIGUI_PIANO_DEFAULT_WIDTH #define MAX_SPLITS_WIDTH 2400 #define SPAN_DEFAULT_HEIGHT 12 /* span handle height in pixels */ #define SPAN_DEFAULT_SPACING 3 /* vertical spacing between spans in pixels */ #define MOVEMENT_THRESHOLD 3 /* pixels of mouse movement till threshold */ #define SPLIT_IS_SELECTED(entry) (((entry)->flags & SPLIT_SELECTED) != 0) /* default colors */ #define DEFAULT_BG_COLOR GNOME_CANVAS_COLOR (255, 255, 178) #define DEFAULT_SPAN_COLOR GNOME_CANVAS_COLOR (0, 252, 113) #define DEFAULT_SPAN_OUTLINE_COLOR GNOME_CANVAS_COLOR (0, 0, 0) #define DEFAULT_SPAN_SEL_COLOR GNOME_CANVAS_COLOR (255, 13, 53) #define DEFAULT_SPAN_SEL_OUTLINE_COLOR DEFAULT_SPAN_SEL_COLOR #define DEFAULT_LINE_COLOR DEFAULT_SPAN_OUTLINE_COLOR #define DEFAULT_LINE_SEL_COLOR DEFAULT_SPAN_SEL_OUTLINE_COLOR #define DEFAULT_ROOT_NOTE_COLOR GNOME_CANVAS_COLOR (80, 80, 255) /* some flags for each split */ enum { SPLIT_SELECTED = 1 << 0, /* span is selected? */ }; /* structure for a single split */ struct _SwamiguiSplitsEntry { SwamiguiSplits *splits; /* parent split object */ int index; /* index of this entry in splits->entry_list */ GObject *item; /* item of this split */ IpatchRange range; /* current span range (MIDI note/velocity) */ guint rootnote_val; /* current root note number (if active) */ SwamiControl *span_control; /* span range control for this split or NULL */ SwamiControl *rootnote_control; /* root note control for this split or NULL */ gboolean destroyed; /* set to TRUE when a split has been destroyed */ int refcount; /* refcount of structure (held by span_control and rootnote_control) */ GnomeCanvasItem *span; /* span canvas item (GnomeCanvasRect) */ GnomeCanvasItem *lowline; /* low range endpoint vertical line */ GnomeCanvasItem *highline; /* high range endpoint vertical line */ GnomeCanvasItem *rootnote; /* root note circle indicator (GnomeCanvasEllipse) */ int flags; /* flags */ }; static void swamigui_splits_class_init (SwamiguiSplitsClass *klass); static void splits_cb_mode_btn_clicked (GtkButton *button, gpointer user_data); static void swamigui_splits_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data); static void swamigui_splits_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_splits_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_splits_destroy (GtkObject *object); static void swamigui_splits_init (SwamiguiSplits *splits); static gboolean swamigui_splits_cb_low_canvas_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data); static GList *swamigui_splits_get_split_at_pos (SwamiguiSplits *splits, int x, int y, int *index); static void swamigui_splits_update_status_bar (SwamiguiSplits *splits, int low, int high); static void swamigui_splits_span_control_get_func (SwamiControl *control, GValue *value); static void swamigui_splits_span_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_splits_span_control_destroy_func (SwamiControlFunc *control); static void swamigui_splits_root_note_control_get_func (SwamiControl *control, GValue *value); static void swamigui_splits_root_note_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value); static void swamigui_splits_root_note_control_destroy_func (SwamiControlFunc *control); static void swamigui_splits_deactivate_handler (SwamiguiSplits *splits); static SwamiguiSplitsEntry *swamigui_splits_create_entry (SwamiguiSplits *splits, GObject *item); static void swamigui_splits_destroy_entry (SwamiguiSplitsEntry *entry); static GList *swamigui_splits_lookup_item (SwamiguiSplits *splits, GObject *item); static void swamigui_splits_update_item_sel (SwamiguiSplitsEntry *entry); static void swamigui_splits_update_selection (SwamiguiSplits *splits); static gboolean swamigui_splits_real_set_selection (SwamiguiSplits *splits, IpatchList *items); static void swamigui_splits_update_entries (SwamiguiSplits *splits, GList *startp, gboolean width_change, gboolean height_change); static void swamigui_splits_entry_set_span_control (SwamiguiSplitsEntry *entry, int low, int high); static void swamigui_splits_entry_set_span (SwamiguiSplitsEntry *entry, int low, int high); static void swamigui_splits_entry_set_root_note_control (SwamiguiSplitsEntry *entry, int val); static void swamigui_splits_entry_set_root_note (SwamiguiSplitsEntry *entry, int val); static gboolean swamigui_splits_default_handler (SwamiguiSplits *splits); static GdkPixbuf *swamigui_splits_create_velocity_gradient (void); static void gradient_data_free (guchar *pixels, gpointer data); /* data */ static GObjectClass *parent_class = NULL; /* we lock handlers list since they can be registered outside GUI thread */ G_LOCK_DEFINE_STATIC (handlers); static GList *split_handlers = NULL; /* list of SwamiguiSplitsHandlers */ /* start and end velocity gradient colors */ static guint8 velbar_scolor[3] = { 0, 0, 0 }; static guint8 velbar_ecolor[3] = { 0, 0, 255 }; GType swamigui_splits_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiSplitsClass), NULL, NULL, (GClassInitFunc) swamigui_splits_class_init, NULL, NULL, sizeof (SwamiguiSplits), 0, (GInstanceInitFunc) swamigui_splits_init, }; obj_type = g_type_register_static (GTK_TYPE_VBOX, "SwamiguiSplits", &obj_info, 0); } return (obj_type); } static void swamigui_splits_class_init (SwamiguiSplitsClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GtkObjectClass *gtkobj_class = GTK_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->set_property = swamigui_splits_set_property; obj_class->get_property = swamigui_splits_get_property; gtkobj_class->destroy = swamigui_splits_destroy; g_object_class_install_property (obj_class, PROP_ITEM_SELECTION, g_param_spec_object ("item-selection", "Item selection", "Item selection", IPATCH_TYPE_LIST, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SPLITS_ITEM, g_param_spec_object ("splits-item", "Splits item", "Splits item", IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_PIANO, g_param_spec_object ("piano", "Piano", "Piano", SWAMIGUI_TYPE_PIANO, G_PARAM_READABLE)); } static void swamigui_splits_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (object); IpatchList *items; GObject *obj; switch (property_id) { case PROP_ITEM_SELECTION: items = g_value_get_object (value); swamigui_splits_real_set_selection (splits, items); break; case PROP_SPLITS_ITEM: if (splits->splits_item) g_object_unref (splits->splits_item); obj = g_value_dup_object (value); splits->splits_item = obj ? IPATCH_ITEM (obj) : NULL; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_splits_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (object); switch (property_id) { case PROP_ITEM_SELECTION: g_value_set_object (value, splits->selection); break; case PROP_SPLITS_ITEM: g_value_set_object (value, splits->splits_item); break; case PROP_PIANO: g_value_set_object (value, splits->piano); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_splits_destroy (GtkObject *object) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (object); SwamiguiSplitsEntry *entry; GList *p; /* unref objects in entries (entries are freed in control destroy callback, since control events might still occur and they depend on entry and splits widget) */ p = splits->entry_list; while (p) { entry = (SwamiguiSplitsEntry *)(p->data); entry->destroyed = TRUE; if (entry->item) g_object_unref (entry->item); if (entry->span_control) swami_control_disconnect_unref (entry->span_control); if (entry->rootnote_control) swami_control_disconnect_unref (entry->rootnote_control); p = g_list_delete_link (p, p); } splits->entry_list = NULL; splits->entry_count = 0; /* free the selection */ if (splits->selection) { g_object_unref (splits->selection); splits->selection = NULL; } /* deactivate the handler */ splits->handler = NULL; splits->handler_data = NULL; if (GTK_OBJECT_CLASS (parent_class)->destroy) (*GTK_OBJECT_CLASS (parent_class)->destroy)(object); } static void swamigui_splits_init (SwamiguiSplits *splits) { GtkWidget *gladewidg; GtkWidget *scrollwin; GtkWidget *widg; GtkAdjustment *hadj, *vadj; GtkStyle *style; GdkPixbuf *pixbuf; splits->anchor = -1; /* no split selection anchor set */ splits->active_drag = ACTIVE_NONE; /* no active click drag */ /* set default size values */ splits->height = SPAN_DEFAULT_HEIGHT; splits->width = -1; /* Invalid width, to force update */ splits->vert_lines_width = 1; splits->span_height = SPAN_DEFAULT_HEIGHT; splits->move_threshold = MOVEMENT_THRESHOLD; splits->span_spacing = SPAN_DEFAULT_SPACING; splits->bg_color = DEFAULT_BG_COLOR; splits->span_color = DEFAULT_SPAN_COLOR; splits->span_sel_color = DEFAULT_SPAN_SEL_COLOR; splits->span_outline_color = DEFAULT_SPAN_OUTLINE_COLOR; splits->span_sel_outline_color = DEFAULT_SPAN_SEL_OUTLINE_COLOR; splits->line_color = DEFAULT_LINE_COLOR; splits->line_sel_color = DEFAULT_LINE_SEL_COLOR; splits->root_note_color = DEFAULT_ROOT_NOTE_COLOR; splits->selection = ipatch_list_new (); /* ++ ref new list */ gladewidg = swamigui_util_glade_create ("SwamiguiSplits"); gtk_box_pack_start (GTK_BOX (splits), gladewidg, TRUE, TRUE, 0); splits->gladewidg = gladewidg; splits->notes_btn = swamigui_util_glade_lookup (gladewidg, "BtnNotes"); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splits->notes_btn), TRUE); g_signal_connect (splits->notes_btn, "clicked", G_CALLBACK (splits_cb_mode_btn_clicked), splits); widg = swamigui_util_glade_lookup (gladewidg, "BtnNotesImage"); gtk_image_set_from_stock (GTK_IMAGE (widg), SWAMIGUI_STOCK_PIANO, GTK_ICON_SIZE_SMALL_TOOLBAR); splits->velocity_btn = swamigui_util_glade_lookup (gladewidg, "BtnVelocity"); g_signal_connect (splits->velocity_btn, "clicked", G_CALLBACK (splits_cb_mode_btn_clicked), splits); widg = swamigui_util_glade_lookup (gladewidg, "BtnVelocityImage"); gtk_image_set_from_stock (GTK_IMAGE (widg), SWAMIGUI_STOCK_VELOCITY, GTK_ICON_SIZE_SMALL_TOOLBAR); splits->vertical_scrollbar = swamigui_util_glade_lookup (gladewidg, "SplitsVScrollBar"); vadj = gtk_range_get_adjustment (GTK_RANGE (splits->vertical_scrollbar)); widg = swamigui_util_glade_lookup (gladewidg, "SplitsHScrollBar"); hadj = gtk_range_get_adjustment (GTK_RANGE (widg)); /* Set horizontal adjustment of upper scroll window to the horizontal scrollbar's */ scrollwin = swamigui_util_glade_lookup (gladewidg, "SplitsScrollWinUpper"); gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (scrollwin), hadj); /* setup upper canvas */ splits->top_canvas = gnome_canvas_new (); gtk_widget_show (splits->top_canvas); gtk_container_add (GTK_CONTAINER (scrollwin), splits->top_canvas); gnome_canvas_set_center_scroll_region (GNOME_CANVAS (splits->top_canvas), FALSE); gtk_widget_set_size_request (splits->top_canvas, -1, SWAMIGUI_PIANO_DEFAULT_HEIGHT); g_signal_connect (splits->top_canvas, "size-allocate", G_CALLBACK (swamigui_splits_cb_canvas_size_allocate), splits); /* create piano canvas item */ splits->piano = SWAMIGUI_PIANO (gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (splits->top_canvas)), SWAMIGUI_TYPE_PIANO, NULL)); /* create velocity gradient canvas item */ pixbuf = swamigui_splits_create_velocity_gradient (); splits->velgrad = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (splits->top_canvas)), GNOME_TYPE_CANVAS_PIXBUF, "pixbuf", pixbuf, "x", (double)0.0, "y", (double)0.0, "height", (double)SWAMIGUI_PIANO_DEFAULT_HEIGHT, "height-set", TRUE, "width-set", TRUE, NULL); gnome_canvas_item_hide (splits->velgrad); /* assign adjustments of lower scrolled window */ scrollwin = swamigui_util_glade_lookup (gladewidg, "SplitsScrollWinLower"); gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (scrollwin), hadj); gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrollwin), vadj); /* setup lower canvas */ splits->low_canvas = gnome_canvas_new (); gtk_widget_show (splits->low_canvas); gtk_container_add (GTK_CONTAINER (scrollwin), splits->low_canvas); gnome_canvas_set_center_scroll_region (GNOME_CANVAS (splits->low_canvas), FALSE); /* set background color of canvas to white */ style = gtk_style_copy (gtk_widget_get_style (splits->low_canvas)); style->bg[GTK_STATE_NORMAL] = style->white; gtk_widget_set_style (splits->low_canvas, style); /* create lower background rectangle (to catch events) */ splits->bgrect = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (splits->low_canvas)), GNOME_TYPE_CANVAS_RECT, "fill-color-rgba", splits->bg_color, "x1", (double)0.0, "x2", (double)SWAMIGUI_PIANO_DEFAULT_WIDTH, "y1", (double)0.0, "y2", (double)splits->span_height, NULL); /* create vertical line group */ splits->vline_group = GNOME_CANVAS_GROUP (gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (splits->low_canvas)), GNOME_TYPE_CANVAS_GROUP, NULL)); // g_signal_connect (gnome_canvas_root (GNOME_CANVAS (splits->low_canvas)), g_signal_connect (splits->low_canvas, "event", G_CALLBACK (swamigui_splits_cb_low_canvas_event), splits); } /* callback when Notes or Velocity mode toggle button is clicked */ static void splits_cb_mode_btn_clicked (GtkButton *button, gpointer user_data) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (user_data); g_signal_handlers_block_by_func (splits->notes_btn, splits_cb_mode_btn_clicked, splits); g_signal_handlers_block_by_func (splits->velocity_btn, splits_cb_mode_btn_clicked, splits); if ((GtkWidget *)button == splits->notes_btn) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splits->notes_btn), TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splits->velocity_btn), FALSE); swamigui_splits_set_mode (splits, SWAMIGUI_SPLITS_NOTE); } else { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splits->notes_btn), FALSE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (splits->velocity_btn), TRUE); swamigui_splits_set_mode (splits, SWAMIGUI_SPLITS_VELOCITY); } g_signal_handlers_unblock_by_func (splits->notes_btn, splits_cb_mode_btn_clicked, splits); g_signal_handlers_unblock_by_func (splits->velocity_btn, splits_cb_mode_btn_clicked, splits); } static void swamigui_splits_cb_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (user_data); if (!splits->width_set) { if (allocation->width < SWAMIGUI_PIANO_DEFAULT_WIDTH) allocation->width = SWAMIGUI_PIANO_DEFAULT_WIDTH; swamigui_splits_set_width (splits, allocation->width); } } static gboolean swamigui_splits_cb_low_canvas_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data) { SwamiguiSplits *splits = SWAMIGUI_SPLITS (data); GdkEventButton *bevent; GdkEventMotion *mevent; SwamiguiSplitsEntry *entry, *selsplit; GList *p, *selsplitp; double dlow, dhigh; int index, i, low, high, note, noteofs; gboolean updatesel = FALSE; switch (event->type) { case GDK_SCROLL: /* forward the event to the vertical scroll bar */ gtk_widget_event (splits->vertical_scrollbar, event); return (TRUE); case GDK_BUTTON_PRESS: bevent = (GdkEventButton *)event; if (!(bevent->button >= 1 && bevent->button <= 3) || bevent->y < 0.0) break; p = swamigui_splits_get_split_at_pos (splits, (int)bevent->x, (int)bevent->y, &index); if (!p) return (FALSE); /* no split found? */ selsplit = (SwamiguiSplitsEntry *)(p->data); selsplitp = p; if (bevent->button != 1 && bevent->button != 2) break; /* deselect all spans if CTRL and SHIFT not pressed and Left click or Middle on unselected item */ if (!(bevent->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) && !(swamigui_root_is_middle_click (NULL, bevent) && SPLIT_IS_SELECTED (selsplit))) { for (p = splits->entry_list; p; p = g_list_next (p)) { entry = (SwamiguiSplitsEntry *)(p->data); if (SPLIT_IS_SELECTED (entry)) { entry->flags &= ~SPLIT_SELECTED; swamigui_splits_update_item_sel (entry); updatesel = TRUE; } } } /* no CTRL or SHIFT, single select */ if (!(bevent->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { if (!SPLIT_IS_SELECTED (selsplit)) { selsplit->flags |= SPLIT_SELECTED; swamigui_splits_update_item_sel (selsplit); updatesel = TRUE; } splits->anchor = index; /* not yet decided if to edit handle */ if (bevent->button == 1) { splits->active_drag = ACTIVE_UNDECIDED; splits->active_xpos = bevent->x; splits->active_drag_btn = 1; splits->threshold_value = 0.0; splits->active_split = selsplitp; } } if (swamigui_root_is_middle_click (NULL, bevent)) /* middle click? */ { if (!SPLIT_IS_SELECTED (selsplit)) { selsplit->flags |= SPLIT_SELECTED; swamigui_splits_update_item_sel (selsplit); updatesel = TRUE; } splits->anchor = index; if (updatesel) swamigui_splits_update_selection (splits); note = swamigui_piano_pos_to_note (splits->piano, bevent->x, 0.0, NULL, NULL); if (note == -1) return (FALSE); // CTRL & SHIFT move both root note and ranges, CTRL moves ranges, move root notes otherwise if ((bevent->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && selsplit->rootnote && selsplit->span) splits->active_drag = ACTIVE_MOVE_BOTH; else if ((bevent->state & GDK_CONTROL_MASK) || !selsplit->rootnote) splits->active_drag = ACTIVE_MOVE_RANGES; else { gboolean selected = FALSE; splits->active_drag = ACTIVE_MOVE_ROOTNOTES; // Check for multiple selected items for (p = splits->entry_list; p; p = g_list_next (p)) { if (SPLIT_IS_SELECTED ((SwamiguiSplitsEntry *)(p->data))) { if (selected) break; selected = TRUE; } } // Set relative note offset if multiple selected items, absolute offset otherwise if (p) splits->move_note_ofs = note - selsplit->rootnote_val; else splits->move_note_ofs = 0; } splits->active_drag_btn = bevent->button; splits->active_split = selsplitp; if (splits->active_drag != ACTIVE_MOVE_ROOTNOTES) splits->move_note_ofs = note - selsplit->range.low; /* update statusbar */ swamigui_splits_update_status_bar (splits, selsplit->range.low, selsplit->range.high); return (FALSE); } /* SHIFT key and an anchor? - select range */ if ((bevent->state & GDK_SHIFT_MASK) && splits->anchor != -1) { int select; if (splits->anchor < index) { low = splits->anchor; high = index; } else { low = index; high = splits->anchor; } i = -1; p = splits->entry_list; while (p) { entry = (SwamiguiSplitsEntry *)(p->data); p = g_list_next (p); i++; if (i >= low && i <= high) select = TRUE; else if (!(bevent->state & GDK_CONTROL_MASK)) select = FALSE; else continue; if (SPLIT_IS_SELECTED (entry) != select) { entry->flags ^= SPLIT_SELECTED; swamigui_splits_update_item_sel (entry); updatesel = TRUE; } } } else if (bevent->state & GDK_CONTROL_MASK) /* CTRL key? */ { selsplit->flags ^= SPLIT_SELECTED; /* toggle sel state */ swamigui_splits_update_item_sel (selsplit); updatesel = TRUE; splits->anchor = index; } if (updatesel) swamigui_splits_update_selection (splits); break; case GDK_BUTTON_RELEASE: if (splits->active_drag == ACTIVE_NONE) return (FALSE); /* Same button released as caused the drag? */ if (splits->active_drag_btn == event->button.button) { splits->active_drag = ACTIVE_NONE; /* clear status bar */ swamigui_statusbar_msg_set_label (swamigui_root->statusbar, 0, "Global", NULL); } break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *)event; if (splits->active_drag == ACTIVE_NONE) { p = swamigui_splits_get_split_at_pos (splits, (int)mevent->x, (int)mevent->y, NULL); if (p) { entry = (SwamiguiSplitsEntry *)(p->data); swamigui_splits_update_status_bar (splits, entry->range.low, entry->range.high); } return (FALSE); } entry = (SwamiguiSplitsEntry *)(splits->active_split->data); /* still haven't decided which handle? */ if (splits->active_drag == ACTIVE_UNDECIDED) { /* has cursor moved beyond threshold? */ splits->threshold_value += ABS (mevent->x - splits->active_xpos); if (splits->threshold_value < splits->move_threshold) return (FALSE); /* find the edge closest to the original click */ dlow = swamigui_piano_note_to_pos (splits->piano, entry->range.low, -1, FALSE, NULL); dhigh = swamigui_piano_note_to_pos (splits->piano, entry->range.high, 1, FALSE, NULL); if (ABS (splits->active_xpos - dlow) <= ABS (splits->active_xpos - dhigh)) splits->active_drag = ACTIVE_LOW; /* select lower handle1 */ else splits->active_drag = ACTIVE_HIGH; /* select upper handle2 */ } if (mevent->x < 0.0) note = 0; else if (mevent->x > splits->piano->width) note = 127; else note = swamigui_piano_pos_to_note (splits->piano, mevent->x, 0.0, NULL, NULL); if (note == -1) return (FALSE); /* Handle move separately (could be multiple items) */ if (splits->active_drag >= ACTIVE_MOVE_ROOTNOTES && splits->active_drag <= ACTIVE_MOVE_BOTH) { note -= splits->move_note_ofs; if (note < 0) note = 0; else if (note > 127) note = 127; /* If drag has not changed the current note offset, short cut */ if ((splits->active_drag != ACTIVE_MOVE_ROOTNOTES && entry->range.low == note) || (splits->active_drag == ACTIVE_MOVE_ROOTNOTES && entry->rootnote_val == note)) break; if (splits->active_drag == ACTIVE_MOVE_ROOTNOTES) noteofs = note - entry->rootnote_val; else noteofs = note - entry->range.low; /* note offset to low note range */ /* Check if any spans/root notes would go out of range and clamp accordingly */ for (p = splits->entry_list; p && noteofs != 0; p = p->next) { entry = (SwamiguiSplitsEntry *)(p->data); if (!SPLIT_IS_SELECTED (entry)) continue; if (splits->active_drag != ACTIVE_MOVE_ROOTNOTES && entry->span) { if ((int)(entry->range.low) + noteofs < 0) noteofs = -entry->range.low; if ((int)(entry->range.high) + noteofs > 127) noteofs = 127 - entry->range.high; } if (splits->active_drag != ACTIVE_MOVE_RANGES && entry->rootnote) { if ((int)(entry->rootnote_val) + noteofs < 0) noteofs = -(int)entry->rootnote_val; if ((int)(entry->rootnote_val) + noteofs > 127) noteofs = 127 - entry->rootnote_val; } } if (noteofs == 0) break; /* Move the selected spans and/or root notes */ for (p = splits->entry_list; p; p = p->next) { entry = (SwamiguiSplitsEntry *)(p->data); if (!SPLIT_IS_SELECTED (entry)) continue; if (splits->active_drag != ACTIVE_MOVE_ROOTNOTES && entry->span) { swamigui_splits_entry_set_span_control (entry, entry->range.low + noteofs, entry->range.high + noteofs); if (entry == splits->active_split->data) swamigui_splits_update_status_bar (splits, entry->range.low, entry->range.high); } if (splits->active_drag != ACTIVE_MOVE_RANGES && entry->rootnote) swamigui_splits_entry_set_root_note_control (entry, entry->rootnote_val + noteofs); } break; } low = entry->range.low; high = entry->range.high; switch (splits->active_drag) { case ACTIVE_LOW: /* lower handle? */ /* need to switch controlled handles? */ if (note > entry->range.high) { splits->active_drag = ACTIVE_HIGH; low = entry->range.high; high = note; } else low = note; break; case ACTIVE_HIGH: /* upper handle */ /* need to switch controlled handles? */ if (note < entry->range.low) { splits->active_drag = ACTIVE_LOW; high = entry->range.low; low = note; } else high = note; break; } if (low != entry->range.low || high != entry->range.high) { swamigui_splits_update_status_bar (splits, low, high); swamigui_splits_entry_set_span_control (entry, low, high); } break; default: break; } return (FALSE); } /* find a split at a given position */ static GList * swamigui_splits_get_split_at_pos (SwamiguiSplits *splits, int x, int y, int *index) { GList *p; int d, idx; if (index) *index = 0; /* click is at least greater than upper blank area? */ if (y <= splits->span_height) return (NULL); /* subtract blank area and half of spacing */ d = y - (splits->span_height - splits->span_spacing / 2); /* calculate span index */ idx = d / (splits->span_height + splits->span_spacing); /* calculate pixel offset in span */ d -= idx * (splits->span_height + splits->span_spacing); if (index) *index = idx; if (d < splits->span_height) /* click within span height? */ { p = g_list_nth (splits->entry_list, idx); if (p && ((SwamiguiSplitsEntry *)(p->data))->span_control) return (p); else return (NULL); } else return (NULL); } /* Update status bar message. Use high = -1 for root notes or other non-range * parameters */ static void swamigui_splits_update_status_bar (SwamiguiSplits *splits, int low, int high) { char lstr[5], hstr[5]; char *msg; if (splits->mode == SWAMIGUI_SPLITS_NOTE) { swami_util_midi_note_to_str (low, lstr); if (high != -1) { swami_util_midi_note_to_str (high, hstr); msg = g_strdup_printf (_("Range: %s:%s (%d-%d)"), lstr, hstr, low, high); } else msg = g_strdup_printf (_("Note: %s (%d)"), lstr, low); } else msg = g_strdup_printf (_("Range: %d-%d"), low, high); swamigui_statusbar_msg_set_label (swamigui_root->statusbar, 0, "Global", msg); g_free (msg); } /* SwamiControlFunc callback for getting a splits current range value */ static void swamigui_splits_span_control_get_func (SwamiControl *control, GValue *value) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); if (entry->destroyed) return; ipatch_value_set_range (value, &entry->range); } /* SwamiControlFunc callback for setting a splits current range value */ static void swamigui_splits_span_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); IpatchRange *range; if (entry->destroyed) return; range = ipatch_value_get_range (value); swamigui_splits_entry_set_span (entry, range->low, range->high); } /* SwamiControlFunc destroy callback. Things are tricky here because control events might still occur after a split has been destroyed. Therefore each control holds a reference to the splits widget and frees its entry as well (if no more entry->refcounts). */ static void swamigui_splits_span_control_destroy_func (SwamiControlFunc *control) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); /* -- unref the control's held reference to the splits widget */ g_object_unref (entry->splits); /* free the entry if no more controls referencing it */ if (g_atomic_int_dec_and_test (&entry->refcount)) g_free (entry); } /* SwamiControlFunc callback for getting a root note current value */ static void swamigui_splits_root_note_control_get_func (SwamiControl *control, GValue *value) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); if (entry->destroyed) return; g_value_set_int (value, entry->rootnote_val); } /* SwamiControlFunc callback for setting a root note current value */ static void swamigui_splits_root_note_control_set_func (SwamiControl *control, SwamiControlEvent *event, const GValue *value) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); if (entry->destroyed) return; swamigui_splits_entry_set_root_note (entry, g_value_get_int (value)); } /* SwamiControlFunc destroy callback. Things are tricky here because control events might still occur after a split has been destroyed. Therefore each control holds a reference to the splits widget and frees its entry as well (if no more entry->refcounts). */ static void swamigui_splits_root_note_control_destroy_func (SwamiControlFunc *control) { SwamiguiSplitsEntry *entry = SWAMI_CONTROL_FUNC_DATA (control); /* -- unref the control's held reference to the splits widget */ g_object_unref (entry->splits); /* free the entry if no more controls referencing it */ if (g_atomic_int_dec_and_test (&entry->refcount)) g_free (entry); } /* internal functions */ /* unset any active split handler */ static void swamigui_splits_deactivate_handler (SwamiguiSplits *splits) { swamigui_splits_remove_all (splits); splits->handler = NULL; splits->handler_data = NULL; g_object_set (splits, "splits-item", NULL, NULL); } static SwamiguiSplitsEntry * swamigui_splits_create_entry (SwamiguiSplits *splits, GObject *item) { SwamiguiSplitsEntry *entry; entry = g_new0 (SwamiguiSplitsEntry, 1); entry->splits = splits; entry->index = 0; entry->item = g_object_ref (G_OBJECT (item)); entry->range.low = 0; entry->range.high = 127; entry->destroyed = FALSE; entry->refcount = 0; entry->flags = 0; /* not selected */ return (entry); } /* calls destroy on split widgets and unrefs objects */ static void swamigui_splits_destroy_entry (SwamiguiSplitsEntry *entry) { entry->destroyed = TRUE; if (entry->span) gtk_object_destroy (GTK_OBJECT (entry->span)); if (entry->lowline) gtk_object_destroy (GTK_OBJECT (entry->lowline)); if (entry->highline) gtk_object_destroy (GTK_OBJECT (entry->highline)); if (entry->rootnote) gtk_object_destroy (GTK_OBJECT (entry->rootnote)); if (entry->item) g_object_unref (entry->item); if (entry->span_control) swami_control_disconnect_unref (entry->span_control); if (entry->rootnote_control) swami_control_disconnect_unref (entry->rootnote_control); } /* lookup a entry GList pointer by item */ static GList * swamigui_splits_lookup_item (SwamiguiSplits *splits, GObject *item) { GList *p; p = splits->entry_list; while (p) { if (((SwamiguiSplitsEntry *)(p->data))->item == item) break; p = g_list_next (p); } return (p); } /* visually update a split's selected state */ static void swamigui_splits_update_item_sel (SwamiguiSplitsEntry *entry) { gboolean sel = SPLIT_IS_SELECTED (entry); SwamiguiSplits *splits = entry->splits; guint color; g_object_set (entry->span, "fill-color-rgba", sel ? splits->span_sel_color : splits->span_color, "outline-color-rgba", sel ? splits->span_sel_outline_color : splits->span_outline_color, NULL); if (sel) { gnome_canvas_item_raise_to_top (entry->lowline); gnome_canvas_item_raise_to_top (entry->highline); color = splits->line_sel_color; } else { gnome_canvas_item_lower_to_bottom (entry->lowline); gnome_canvas_item_lower_to_bottom (entry->highline); color = splits->line_color; } g_object_set (entry->lowline, "fill-color-rgba", color, NULL); g_object_set (entry->highline, "fill-color-rgba", color, NULL); } /* updates splits->selection based on currently selected splits and issues a property change notify on "item-selection" */ static void swamigui_splits_update_selection (SwamiguiSplits *splits) { SwamiguiSplitsEntry *entry; IpatchList *listobj; GList *list = NULL, *p; for (p = splits->entry_list; p; p = g_list_next (p)) { entry = (SwamiguiSplitsEntry *)(p->data); if (SPLIT_IS_SELECTED (entry)) { list = g_list_prepend (list, entry->item); g_object_ref (entry->item); /* ++ ref item for list obj */ } } list = g_list_reverse (list); listobj = ipatch_list_new (); /* ++ ref new list object */ listobj->items = list; if (splits->selection) g_object_unref (splits->selection); splits->selection = listobj; /* !! takes over list object reference */ g_object_notify (G_OBJECT (splits), "item-selection"); } /** * swamigui_splits_new: * * Create new note/velocity splits widget. * * Returns: New splits widget. */ GtkWidget * swamigui_splits_new (void) { return (GTK_WIDGET (gtk_type_new (swamigui_splits_get_type ()))); } /** * swamigui_splits_set_mode: * @splits: Splits object * @mode: Velocity or key mode enum * * Set the mode of a splits object. */ void swamigui_splits_set_mode (SwamiguiSplits *splits, SwamiguiSplitsMode mode) { g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); if (mode == splits->mode) return; splits->mode = mode; if (splits->mode == SWAMIGUI_SPLITS_VELOCITY) /* velocity mode? */ { gnome_canvas_item_hide (GNOME_CANVAS_ITEM (splits->piano)); gnome_canvas_item_show (splits->velgrad); } else /* note mode */ { gnome_canvas_item_hide (splits->velgrad); gnome_canvas_item_show (GNOME_CANVAS_ITEM (splits->piano)); } G_LOCK (handlers); if (splits->handler) { splits->status = SWAMIGUI_SPLITS_MODE; if (!(*splits->handler)(splits)) swamigui_splits_deactivate_handler (splits); splits->status = SWAMIGUI_SPLITS_NORMAL; } G_UNLOCK (handlers); } /** * swamigui_splits_set_width: * @splits: Splits object * @width: Width in pixels * * Set the width of the splits widget in pixels. */ void swamigui_splits_set_width (SwamiguiSplits *splits, int width) { if (width == splits->width) return; splits->width = width; /* update piano width */ g_object_set (splits->piano, "width-pixels", width, NULL); /* update velocity width */ g_object_set (splits->velgrad, "width", (double)width, NULL); swamigui_splits_update_entries (splits, splits->entry_list, TRUE, FALSE); } /** * swamigui_splits_set_selection: * @splits: Splits object * @items: List of selected items (selected splits and/or the parent of * split items) or %NULL to unset selection. * * Set the items of a splits widget. The @items list can contain * an item that is a parent of items with split parameters (a * SoundFont #IpatchSF2Preset or IpatchSF2Inst for example) and/or a list * of children split item's with the same parent (for example * SoundFont #IpatchSF2PZone items), any other selection list will de-activate * the splits widget. */ void swamigui_splits_set_selection (SwamiguiSplits *splits, IpatchList *items) { if (swamigui_splits_real_set_selection (splits, items)) g_object_notify (G_OBJECT (splits), "item-selection"); } static gboolean swamigui_splits_real_set_selection (SwamiguiSplits *splits, IpatchList *items) { SwamiguiSplitsHandler hfunc; GList *p; g_return_val_if_fail (SWAMIGUI_IS_SPLITS (splits), FALSE); g_return_val_if_fail (!items || IPATCH_IS_LIST (items), FALSE); if (splits->selection) g_object_unref (splits->selection); /* -- unref old */ if (items) splits->selection = ipatch_list_duplicate (items); /* ++ ref */ else splits->selection = NULL; G_LOCK (handlers); if (splits->handler) /* active handler? */ { splits->status = SWAMIGUI_SPLITS_UPDATE; if (!items || !(*splits->handler)(splits)) swamigui_splits_deactivate_handler (splits); } if (items && !splits->handler) /* re-test in case it was de-activated */ { splits->status = SWAMIGUI_SPLITS_INIT; p = split_handlers; while (p) /* try handlers */ { hfunc = (SwamiguiSplitsHandler)(p->data); if ((*hfunc)(splits)) /* selection handled? */ { splits->handler = hfunc; break; } p = g_list_next (p); } } G_UNLOCK (handlers); if (!splits->handler) /* no handler found? - Try default. */ { if (swamigui_splits_default_handler (splits)) splits->handler = swamigui_splits_default_handler; } splits->status = SWAMIGUI_SPLITS_NORMAL; return (TRUE); } /** * swamigui_splits_get_selection: * @splits: Splits widget * * Get the list of active items in a splits widget (a parent of split items * and/or split items). * * Returns: New list containing splits with a ref count of one which the * caller owns or %NULL if no active splits. */ IpatchList * swamigui_splits_get_selection (SwamiguiSplits *splits) { g_return_val_if_fail (SWAMIGUI_IS_SPLITS (splits), NULL); if (splits->selection) return (ipatch_list_duplicate (splits->selection)); else return (NULL); } /** * swamigui_splits_select_items: * @splits: Splits widget * @items: List of objects to select (%NULL to unselect all) * * Set the list of splits currently selected. Usually only used by * #SwamiguiSplit handlers. */ void swamigui_splits_select_items (SwamiguiSplits *splits, GList *items) { SwamiguiSplitsEntry *entry; GHashTable *hash; gboolean sel; GList *p; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); /* hash the item list for speed in the case of large split lists */ hash = g_hash_table_new (NULL, NULL); for (p = items; p; p = p->next) g_hash_table_insert (hash, p->data, GUINT_TO_POINTER (TRUE)); for (p = splits->entry_list; p; p = g_list_next (p)) { entry = (SwamiguiSplitsEntry *)(p->data); sel = GPOINTER_TO_UINT (g_hash_table_lookup (hash, entry->item)); if (sel != SPLIT_IS_SELECTED (entry)) { if (sel) entry->flags |= SPLIT_SELECTED; else entry->flags &= ~SPLIT_SELECTED; swamigui_splits_update_item_sel (entry); } } } /** * swamigui_splits_select_all: * @splits: Splits widget * * Select all splits in a splits widget. */ void swamigui_splits_select_all (SwamiguiSplits *splits) { SwamiguiSplitsEntry *entry; GList *p; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); for (p = splits->entry_list; p; p = g_list_next (p)) { entry = (SwamiguiSplitsEntry *)(p->data); if (!SPLIT_IS_SELECTED (entry)) { entry->flags |= SPLIT_SELECTED; swamigui_splits_update_item_sel (entry); } } } /** * swamigui_splits_unselect_all: * @splits: Splits widget * * Unselect all splits in a splits widget. */ void swamigui_splits_unselect_all (SwamiguiSplits *splits) { SwamiguiSplitsEntry *entry; GList *p; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); for (p = splits->entry_list; p; p = g_list_next (p)) { entry = (SwamiguiSplitsEntry *)(p->data); if (SPLIT_IS_SELECTED (entry)) { entry->flags &= ~SPLIT_SELECTED; swamigui_splits_update_item_sel (entry); } } } /** * swamigui_splits_item_changed: * @splits: Splits widget * * Called to indicate that the active "splits-item" has changed and the splits * should therefore be updated. */ void swamigui_splits_item_changed (SwamiguiSplits *splits) { g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); if (!splits->handler) return; splits->status = SWAMIGUI_SPLITS_CHANGED; if (!splits->handler (splits)) swamigui_splits_deactivate_handler (splits); } /** * swamigui_splits_register_handler: * @handler: Splits handler function to register * * Registers a new handler for splits widgets. Split handlers interface * patch item's of particular types with note/velocity split parameters and * note pointer controls (such as a root note parameter). * * MT: This function is multi-thread safe and can be called from outside * of the GUI thread. */ void swamigui_splits_register_handler (SwamiguiSplitsHandler handler) { g_return_if_fail (handler != NULL); G_LOCK (handlers); split_handlers = g_list_prepend (split_handlers, handler); G_UNLOCK (handlers); } /** * swamigui_splits_unregister_handler: * @handler: Handler function to unregister * * Unregisters a handler previously registered with * swamigui_splits_register_handler(). * * MT: This function is multi-thread safe and can be called from outside * of the GUI thread. */ void swamigui_splits_unregister_handler (SwamiguiSplitsHandler handler) { g_return_if_fail (handler != NULL); G_LOCK (handlers); split_handlers = g_list_remove (split_handlers, handler); G_UNLOCK (handlers); } /** * swamigui_splits_insert: * @splits: Splits widget * @item: Object for this split * @index: Index in list of existing splits in widget (-1 to append). * * Adds a new entry to a splits widget associated with a given object * @item. An entry is a place holder for a split range (key or velocity) * and/or root note controls. * * Returns: Splits entry which is internal and should only be used with * public accessor functions and should not be modified or freed. */ SwamiguiSplitsEntry * swamigui_splits_insert (SwamiguiSplits *splits, GObject *item, int index) { SwamiguiSplitsEntry *entry; GList *p; int entry_count; g_return_val_if_fail (SWAMIGUI_IS_SPLITS (splits), NULL); g_return_val_if_fail (IPATCH_IS_ITEM (item), NULL); entry = swamigui_splits_create_entry (splits, item); entry_count = splits->entry_count; if (index < 0 || index >= entry_count) /* Append? */ { index = entry_count; splits->entry_list = g_list_append (splits->entry_list, entry); p = NULL; } else { p = g_list_nth (splits->entry_list, index); splits->entry_list = g_list_insert_before (splits->entry_list, p, entry); } entry->index = index; splits->entry_count++; /* increment the split count */ splits->height += splits->span_height + splits->span_spacing; /* update splits total height */ swamigui_splits_update_entries (splits, p, FALSE, TRUE); return (entry); } /* Update geometry of items in relation to entry changes or width change. * Also updates entry->index values */ static void swamigui_splits_update_entries (SwamiguiSplits *splits, GList *startp, gboolean width_change, gboolean height_change) { GnomeCanvasPoints *lpoints; double xpos1, xpos2, ypos1, ypos2, halfwidth; SwamiguiSplitsEntry *entry; int index; GList *p; /* update lower canvas background rectangle */ if (width_change && height_change) g_object_set (splits->bgrect, "x2", (double)splits->width, "y2", (double)splits->height, NULL); else if (width_change) g_object_set (splits->bgrect, "x2", (double)splits->width, NULL); else g_object_set (splits->bgrect, "y2", (double)splits->height, NULL); lpoints = gnome_canvas_points_new (2); /* line points */ if (startp && startp->prev) index = ((SwamiguiSplitsEntry *)(startp->prev->data))->index + 1; else index = 0; /* top of starting span to update */ ypos1 = splits->span_height + (index * (splits->span_height + splits->span_spacing)); /* update splits */ for (p = startp; p; p = p->next, index++) { entry = (SwamiguiSplitsEntry *)(p->data); entry->index = index; if (entry->span) { xpos1 = swamigui_piano_note_to_pos (splits->piano, entry->range.low, -1, FALSE, NULL); xpos2 = swamigui_piano_note_to_pos (splits->piano, entry->range.high, 1, FALSE, NULL); if (width_change && height_change) g_object_set (entry->span, "x1", xpos1, "x2", xpos2, "y1", ypos1, "y2", ypos1 + splits->span_height, NULL); else if (width_change) g_object_set (entry->span, "x1", xpos1, "x2", xpos2, NULL); else g_object_set (entry->span, "y1", ypos1, "y2", ypos1 + splits->span_height, NULL); lpoints->coords[1] = 0.0; lpoints->coords[3] = ypos1 + splits->span_height; /* set the low and high vertical line coordinates */ lpoints->coords[0] = lpoints->coords[2] = xpos1; g_object_set (entry->lowline, "points", lpoints, NULL); lpoints->coords[0] = lpoints->coords[2] = xpos2; g_object_set (entry->highline, "points", lpoints, NULL); } if (entry->rootnote) { xpos1 = swamigui_piano_note_to_pos (splits->piano, entry->rootnote_val, 0, FALSE, NULL); ypos2 = ypos1 + splits->span_height; /* Bottom of span */ halfwidth = splits->span_height / 2.0 - 2.0; g_object_set (entry->rootnote, "x1", xpos1 - halfwidth, "x2", xpos1 + halfwidth, "y1", ypos1 + 2.0, "y2", ypos2 - 2.0, NULL); } ypos1 += splits->span_height + splits->span_spacing; } gnome_canvas_points_free (lpoints); if (width_change) gnome_canvas_set_scroll_region (GNOME_CANVAS (splits->top_canvas), 0, 0, splits->width, SWAMIGUI_PIANO_DEFAULT_HEIGHT); gnome_canvas_set_scroll_region (GNOME_CANVAS (splits->low_canvas), 0, 0, splits->width, splits->height); } /** * swamigui_splits_remove: * @splits: Splits widget * @item: Object of split to remove * * Remove a split from a splits object by its associated object. */ void swamigui_splits_remove (SwamiguiSplits *splits, GObject *item) { SwamiguiSplitsEntry *entry; GList *lookup_item, *p; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); g_return_if_fail (IPATCH_IS_ITEM (item)); /* lookup the entry by item */ lookup_item = swamigui_splits_lookup_item (splits, item); g_return_if_fail (lookup_item != NULL); p = lookup_item->next; /* advance to item after */ entry = (SwamiguiSplitsEntry *)(lookup_item->data); splits->entry_list = g_list_delete_link (splits->entry_list, lookup_item); swamigui_splits_destroy_entry (entry); /* destroy entry */ splits->entry_count--; /* decrement split count */ /* update splits total height */ splits->height -= splits->span_height + splits->span_spacing; swamigui_splits_update_entries (splits, p, FALSE, TRUE); } /** * swamigui_splits_remove_all: * @splits: Splits widget * * Remove all splits from a splits object. */ void swamigui_splits_remove_all (SwamiguiSplits *splits) { GList *p; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); p = splits->entry_list; while (p) { swamigui_splits_destroy_entry ((SwamiguiSplitsEntry *)(p->data)); p = g_list_delete_link (p, p); } splits->entry_list = NULL; splits->entry_count = 0; /* update total split height (just upper blank region now) */ splits->height = splits->span_height; swamigui_splits_update_entries (splits, NULL, FALSE, TRUE); } /** * swamigui_splits_set_span_range: * @splits: Splits object * @item: Item of span to set * @low: Low value of span range * @high: High value of span range * * A convenience function to set a span control range. One could also * set this directly via the control. */ void swamigui_splits_set_span_range (SwamiguiSplits *splits, GObject *item, int low, int high) { SwamiguiSplitsEntry *entry; GList *lookup_item; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); g_return_if_fail (IPATCH_IS_ITEM (item)); g_return_if_fail (low <= high); g_return_if_fail (low >= 0 && high <= 127); lookup_item = swamigui_splits_lookup_item (splits, item); g_return_if_fail (lookup_item != NULL); entry = (SwamiguiSplitsEntry *)(lookup_item->data); swamigui_splits_entry_set_span_control (entry, low, high); } /* sets a split span widget range and transmits the change on its control also */ static void swamigui_splits_entry_set_span_control (SwamiguiSplitsEntry *entry, int low, int high) { IpatchRange range; GValue value = { 0 }; if (low == entry->range.low && high == entry->range.high) return; swamigui_splits_entry_set_span (entry, low, high); /* transmit the change via the range control */ range.low = low; range.high = high; g_value_init (&value, IPATCH_TYPE_RANGE); ipatch_value_set_range (&value, &range); swami_control_transmit_value (entry->span_control, &value); } /* sets a split span widget range */ static void swamigui_splits_entry_set_span (SwamiguiSplitsEntry *entry, int low, int high) { double pos1, pos2, ypos; GnomeCanvasPoints *points; SwamiguiSplits *splits = entry->splits; entry->range.low = low; entry->range.high = high; pos1 = swamigui_piano_note_to_pos (entry->splits->piano, low, -1, FALSE, NULL); pos2 = swamigui_piano_note_to_pos (entry->splits->piano, high, 1, FALSE, NULL); ypos = splits->span_height + (entry->index * (splits->span_height + splits->span_spacing)); g_object_set (entry->span, "x1", pos1, "x2", pos2, NULL); /* set low and high vertical line coordinates */ points = gnome_canvas_points_new (2); points->coords[1] = 0.0; points->coords[3] = ypos; points->coords[0] = points->coords[2] = pos1; g_object_set (entry->lowline, "points", points, NULL); points->coords[0] = points->coords[2] = pos2; g_object_set (entry->highline, "points", points, NULL); gnome_canvas_points_free (points); } /** * swamigui_splits_set_root_note: * @splits: Splits widget * @item: Item of the splits entry to change the root note of * @val: MIDI root note value (0-127) * * A convenience function to set the root note value of a splits entry. */ void swamigui_splits_set_root_note (SwamiguiSplits *splits, GObject *item, int val) { SwamiguiSplitsEntry *entry; GList *lookup_item; g_return_if_fail (SWAMIGUI_IS_SPLITS (splits)); g_return_if_fail (IPATCH_IS_ITEM (item)); g_return_if_fail (val >= 0 && val <= 127); lookup_item = swamigui_splits_lookup_item (splits, item); g_return_if_fail (lookup_item != NULL); entry = (SwamiguiSplitsEntry *)(lookup_item->data); swamigui_splits_entry_set_root_note_control (entry, val); } /* sets a split root note widget range and transmits the change on its control also */ static void swamigui_splits_entry_set_root_note_control (SwamiguiSplitsEntry *entry, int val) { GValue value = { 0 }; if (val == entry->rootnote_val) return; swamigui_splits_entry_set_root_note (entry, val); /* transmit the change via the control */ g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, val); swami_control_transmit_value (entry->rootnote_control, &value); } /* sets a split root note widget value */ static void swamigui_splits_entry_set_root_note (SwamiguiSplitsEntry *entry, int val) { SwamiguiSplits *splits = entry->splits; double xpos, ypos1, ypos2, halfwidth; g_return_if_fail (entry->rootnote != NULL); entry->rootnote_val = val; /* top of starting span to update */ ypos1 = splits->span_height + (entry->index * (splits->span_height + splits->span_spacing)); xpos = swamigui_piano_note_to_pos (splits->piano, val, 0, FALSE, NULL); ypos2 = ypos1 + splits->span_height; /* Bottom of span */ halfwidth = splits->span_height / 2.0 - 2.0; g_object_set (entry->rootnote, "x1", xpos - halfwidth, "x2", xpos + halfwidth, "y1", ypos1 + 2.0, "y2", ypos2 - 2.0, NULL); } /** * swamigui_splits_entry_get_split_control: * @entry: Splits entry pointer * * Get the span control for a given splits entry. The span control is * created if it hasn't already been and is used for controlling a note or * velocity range. * * Returns: The split control for the given @entry. The control has not been * referenced and is only valid while the GUI split exists. */ SwamiControl * swamigui_splits_entry_get_span_control (SwamiguiSplitsEntry *entry) { GnomeCanvasGroup *root; GnomeCanvasPoints *points; SwamiguiSplits *splits; double ypos; g_return_val_if_fail (entry != NULL, NULL); if (entry->span_control) return (entry->span_control); splits = entry->splits; root = gnome_canvas_root (GNOME_CANVAS (entry->splits->low_canvas)); g_object_ref (entry->splits); /* ++ ref the splits object for the control */ g_atomic_int_inc (&entry->refcount); /* ++ ref entry structure for span_control */ /* create control for span range (++ ref new object) */ entry->span_control = swamigui_control_new (SWAMI_TYPE_CONTROL_FUNC); swami_control_set_spec (entry->span_control, g_param_spec_boxed ("value", "value", "value", IPATCH_TYPE_RANGE, G_PARAM_READWRITE)); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (entry->span_control), swamigui_splits_span_control_get_func, swamigui_splits_span_control_set_func, swamigui_splits_span_control_destroy_func, entry); ypos = splits->span_height + (entry->index * (splits->span_height + splits->span_spacing)); entry->span = gnome_canvas_item_new (root, GNOME_TYPE_CANVAS_RECT, "fill-color-rgba", entry->splits->span_color, "outline-color-rgba", entry->splits->span_outline_color, "x1", 0.0, "x2", (double)(entry->splits->piano->width), "y1", ypos, "y2", ypos + splits->span_height, NULL); /* set coordinates of low and high range vertical lines */ points = gnome_canvas_points_new (2); points->coords[1] = 0.0; points->coords[3] = ypos + splits->span_height; points->coords[0] = points->coords[2] = 0.0; entry->lowline = gnome_canvas_item_new (entry->splits->vline_group, GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", entry->splits->line_color, "width-pixels", 1, "points", points, NULL); points->coords[0] = points->coords[2] = splits->piano->width; entry->highline = gnome_canvas_item_new (entry->splits->vline_group, GNOME_TYPE_CANVAS_LINE, "fill-color-rgba", entry->splits->line_color, "width-pixels", 1, "points", points, NULL); gnome_canvas_points_free (points); return (entry->span_control); } /** * swamigui_splits_entry_get_root_note_control: * @entry: Splits entry pointer * * Get the root note control for a given splits entry. The root note control is * created if it hasn't already been. * * Returns: The root note control for the given @entry. The control has not been * referenced and is only valid while the GUI split exists. */ SwamiControl * swamigui_splits_entry_get_root_note_control (SwamiguiSplitsEntry *entry) { GnomeCanvasGroup *root; g_return_val_if_fail (entry != NULL, NULL); if (entry->rootnote_control) return (entry->rootnote_control); g_object_ref (entry->splits); /* ++ ref the splits object for the control */ g_atomic_int_inc (&entry->refcount); /* ++ ref entry structure for rootnote_control */ root = gnome_canvas_root (GNOME_CANVAS (entry->splits->low_canvas)); entry->rootnote_control = swamigui_control_new (SWAMI_TYPE_CONTROL_FUNC); swami_control_set_spec (entry->rootnote_control, g_param_spec_int ("value", "value", "value", 0, 127, 60, G_PARAM_READWRITE)); swami_control_set_value_type (entry->rootnote_control, G_TYPE_INT); swami_control_func_assign_funcs (SWAMI_CONTROL_FUNC (entry->rootnote_control), swamigui_splits_root_note_control_get_func, swamigui_splits_root_note_control_set_func, swamigui_splits_root_note_control_destroy_func, entry); entry->rootnote = gnome_canvas_item_new (root, GNOME_TYPE_CANVAS_ELLIPSE, "fill-color-rgba", entry->splits->root_note_color, NULL); gnome_canvas_item_raise_to_top (entry->rootnote); return (entry->rootnote_control); } /* default splits handler */ static gboolean swamigui_splits_default_handler (SwamiguiSplits *splits) { SwamiguiSplitsEntry *entry; SwamiControl *span_ctrl, *prop_ctrl, *rootnote_ctrl; IpatchItem *splitsobj = NULL; IpatchList *children; IpatchIter iter; GValue value = { 0 }; IpatchRange *range; const GType *types; GObject *obj; GList *sel = NULL; int splits_type; GObjectClass *klass; if (splits->status != SWAMIGUI_SPLITS_INIT && splits->status != SWAMIGUI_SPLITS_UPDATE && splits->status != SWAMIGUI_SPLITS_MODE && splits->status != SWAMIGUI_SPLITS_CHANGED) return (TRUE); if (!splits->selection) return (FALSE); ipatch_list_init_iter (splits->selection, &iter); obj = ipatch_iter_first (&iter); if (!obj) return (FALSE); /* either a single object with its "splits-type" type property set or multiple items with the same parent which has a "splits-type" property are handled */ ipatch_type_object_get (obj, "splits-type", &splits_type, NULL); /* does not have splits-type set? */ if (splits_type == IPATCH_SPLITS_NONE) { if (!IPATCH_IS_ITEM (obj)) return (FALSE); /* not handled if !IpatchItem */ splitsobj = ipatch_item_get_parent (IPATCH_ITEM (obj)); /* ++ ref parent */ if (!splitsobj) return (FALSE); /* no parent, not handled */ /* check if parent type supports splits, unhandled if not */ ipatch_type_object_get (G_OBJECT (splitsobj), "splits-type", &splits_type, NULL); if (splits_type == IPATCH_SPLITS_NONE) { g_object_unref (splitsobj); /* -- unref parent */ return (FALSE); } sel = g_list_prepend (sel, obj); /* add to selection list */ while ((obj = ipatch_iter_next (&iter))) { /* selection unhandled if not IpatchItem or parent not the same */ if (!IPATCH_IS_ITEM (obj) || ipatch_item_peek_parent ((IpatchItem *)obj) != splitsobj) { g_list_free (sel); g_object_unref (splitsobj); /* -- unref parent */ return (FALSE); } sel = g_list_prepend (sel, obj); /* add to selection list */ } } /* item has splits-type set, selection unhandled if there is another item */ else { if (ipatch_iter_next (&iter)) return (FALSE); splitsobj = (IpatchItem *)g_object_ref (obj); /* ++ ref to even things up */ } /* clear and update splits if init, mode change or update with different obj */ if (splits->status != SWAMIGUI_SPLITS_UPDATE || splits->splits_item != splitsobj) { swamigui_splits_remove_all (splits); /* set splits-item */ g_object_set (splits, "splits-item", splitsobj, NULL); types = ipatch_container_get_child_types (IPATCH_CONTAINER (splitsobj)); for (; *types; types++) /* loop over child types */ { /* Verify this child type has velocity or note range property */ if (splits->mode == SWAMIGUI_SPLITS_VELOCITY) { klass = g_type_class_peek (*types); if (!klass || !g_object_class_find_property (klass, "velocity-range")) continue; } else { klass = g_type_class_peek (*types); if (!klass || !g_object_class_find_property (klass, "note-range")) continue; } /* ++ ref new list */ children = ipatch_container_get_children (IPATCH_CONTAINER (splitsobj), *types); ipatch_list_init_iter (children, &iter); obj = ipatch_iter_first (&iter); for (; obj; obj = ipatch_iter_next (&iter)) /* loop over child items */ { g_value_init (&value, IPATCH_TYPE_RANGE); if (splits->mode == SWAMIGUI_SPLITS_VELOCITY) g_object_get_property (obj, "velocity-range", &value); else g_object_get_property (obj, "note-range", &value); range = ipatch_value_get_range (&value); /* skip objects with NULL range */ if (range->low == -1 && range->high == -1) { g_value_unset (&value); continue; } entry = swamigui_splits_add (splits, obj); /* add a new entry */ span_ctrl = swamigui_splits_entry_get_span_control (entry); /* set current value of span, we do this instead of using the SWAMI_CONTROL_CONN_INIT flag, since we already read the value */ swami_control_set_value (span_ctrl, &value); /* ++ ref new key range property control */ if (splits->mode == SWAMIGUI_SPLITS_VELOCITY) prop_ctrl = swami_get_control_prop_by_name (obj, "velocity-range"); else prop_ctrl = swami_get_control_prop_by_name (obj, "note-range"); /* connect the key range property control to the span control */ swami_control_connect (prop_ctrl, span_ctrl, SWAMI_CONTROL_CONN_BIDIR); g_object_unref (prop_ctrl); /* -- unref property control */ g_value_unset (&value); /* Add root note indicator if NOTE splits mode and has root-note property */ if (splits->mode == SWAMIGUI_SPLITS_NOTE && g_object_class_find_property (klass, "root-note")) { rootnote_ctrl = swamigui_splits_entry_get_root_note_control (entry); prop_ctrl = swami_get_control_prop_by_name (obj, "root-note"); /* ++ ref property control */ swami_control_connect (prop_ctrl, rootnote_ctrl, SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT); g_object_unref (prop_ctrl); /* -- unref prop control */ } } /* while (obj) */ g_object_unref (children); /* -- unref children list */ } /* for (; *types; types++) */ } g_object_unref (splitsobj); /* -- unref splits object */ swamigui_splits_select_items (splits, sel); g_list_free (sel); return (TRUE); } static GdkPixbuf * swamigui_splits_create_velocity_gradient (void) { guchar *linebuf; /* generated gradient image data */ float rval, gval, bval; float rinc, ginc, binc; gint i; /* allocate buffer space for one line of velocity bar (128 levels * RGB) */ linebuf = g_new (guchar, 128 * 3); rval = velbar_scolor[0]; gval = velbar_scolor[1]; bval = velbar_scolor[2]; rinc = (float) (velbar_ecolor[0] - rval + 1) / 128; ginc = (float) (velbar_ecolor[1] - gval + 1) / 128; binc = (float) (velbar_ecolor[2] - bval + 1) / 128; /* generate the gradient image data */ for (i = 0; i < 128 * 3;) { linebuf[i++] = (guchar)(rval + 0.5); linebuf[i++] = (guchar)(gval + 0.5); linebuf[i++] = (guchar)(bval + 0.5); rval += rinc; gval += ginc; bval += binc; } return (gdk_pixbuf_new_from_data (linebuf, GDK_COLORSPACE_RGB, FALSE, 8, 128, 1, 128 * 3, gradient_data_free, NULL)); } static void gradient_data_free (guchar *pixels, gpointer data) { g_free (pixels); } swami-2.2.0/src/swamigui/SwamiguiSplits.h000066400000000000000000000172411361104770400204140ustar00rootroot00000000000000/* * SwamiguiSplits.h - Key/velocity splits widget header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SPLITS_H__ #define __SWAMIGUI_SPLITS_H__ #include #include #include typedef struct _SwamiguiSplits SwamiguiSplits; typedef struct _SwamiguiSplitsClass SwamiguiSplitsClass; typedef struct _SwamiguiSplitsEntry SwamiguiSplitsEntry; #include "SwamiguiPiano.h" #define SWAMIGUI_TYPE_SPLITS (swamigui_splits_get_type ()) #define SWAMIGUI_SPLITS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_SPLITS, SwamiguiSplits)) #define SWAMIGUI_SPLITS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_SPLITS, \ SwamiguiSplitsClass)) #define SWAMIGUI_IS_SPLITS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_SPLITS)) #define SWAMIGUI_IS_SPLITS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_SPLITS)) /* number of white keys in MIDI 128 note range */ #define SWAMIGUI_SPLITS_WHITE_KEY_COUNT 75 /* splits mode */ typedef enum { SWAMIGUI_SPLITS_NOTE, SWAMIGUI_SPLITS_VELOCITY } SwamiguiSplitsMode; typedef enum { SWAMIGUI_SPLITS_NORMAL, /* no particular status */ SWAMIGUI_SPLITS_INIT, /* check selection and initialize splits */ SWAMIGUI_SPLITS_MODE, /* note/velocity mode change */ SWAMIGUI_SPLITS_UPDATE, /* selection changed */ SWAMIGUI_SPLITS_CHANGED /* splits-item changed */ } SwamiguiSplitsStatus; /** * SwamiguiSplitsHandler: * @splits: Splits object * * This function type is used to handle specific patch item types with note * or velocity split parameters. The @splits object * status field indicates the current operation * which is one of: * * %SWAMIGUI_SPLITS_INIT - Check selection and install splits and note * pointers if the selection can be handled. Return %TRUE if selection * was handled, which will activate this handler, %FALSE otherwise. * * %SWAMIGUI_SPLITS_MODE - Split mode change (from note to velocity mode for * example). Re-configure splits and note pointers. Return %TRUE if mode * change was handled, %FALSE otherwise which will de-activate this handler. * * %SWAMIGUI_SPLITS_UPDATE - Item selection has changed, update splits and * note pointers. Return %TRUE if selection change was handled, %FALSE * otherwise which will de-activate this handler. * * Other useful fields of a #SwamiguiSplits object include * mode which defines the current mode * (#SwamiguiSplitsMode) and selection which * defines the current item selection. * * Returns: Should return %TRUE if operation was handled, %FALSE otherwise. */ typedef gboolean (*SwamiguiSplitsHandler)(SwamiguiSplits *splits); /* Swami Splits Object */ struct _SwamiguiSplits { GtkVBox parent_instance; /* derived from GtkVBox */ /*< public >*/ SwamiguiSplitsStatus status; /* current status (for handlers) */ int mode; /* current mode (SWAMIGUI_SPLITS_NOTE or SWAMIGUI_SPLITS_VELOCITY) */ IpatchList *selection; /* selected items (parent OR child splits) */ IpatchItem *splits_item; /* active item which contains splits */ SwamiguiSplitsHandler handler; /* active splits handler or NULL */ gpointer handler_data; /* handler defined pointer */ /*< private >*/ GtkWidget *gladewidg; /* The embedded glade widget */ GtkWidget *top_canvas; /* piano/velocity/pointers canvas */ GtkWidget *low_canvas; /* lower spans canvas */ GtkWidget *vertical_scrollbar; /* vertical scroll bar */ GtkWidget *notes_btn; /* Notes toggle button */ GtkWidget *velocity_btn; /* Velocity toggle button */ gboolean width_set; /* TRUE when width set, FALSE to resize to window */ GnomeCanvasGroup *vline_group; /* vertical line group */ SwamiguiPiano *piano; /* piano canvas item */ GnomeCanvasItem *velgrad; /* velocity gradient canvas item */ GnomeCanvasItem *bgrect; /* lower canvas background rectangle */ int flags; /* some flags for optimized updating */ GList *entry_list; /* list of SwamiguiSplitsEntry */ guint entry_count; /* count of entries in entry_list */ int active_drag; /* The active drag mode (see ActiveDrag) */ int active_drag_btn; /* The mouse button which caused the drag */ int anchor; /* last clicked split index or -1 */ double active_xpos; /* X coordinate of active click */ double threshold_value; /* threshold pixel movement value */ GList *active_split; /* split being edited (->data = SwamiguiSplitsEntry) */ int move_note_ofs; /* middle click move note click offset */ int height; /* total height of splits lower canvas */ int width; /* Width of splits canvas */ int span_height; /* height of span handles */ int span_spacing; /* vertical spacing between splits */ int vert_lines_width; /* width of background vertical lines */ int move_threshold; /* movement threshold */ guint bg_color; /* background color */ guint span_color; /* span color */ guint span_sel_color; /* selected span color */ guint span_outline_color; /* span outline color */ guint span_sel_outline_color; /* selected span outline color */ guint line_color; /* line color */ guint line_sel_color; /* selected line color */ guint root_note_color; /* root note indicator color */ }; struct _SwamiguiSplitsClass { GtkVBoxClass parent_class; }; GType swamigui_splits_get_type (void); GtkWidget *swamigui_splits_new (void); void swamigui_splits_set_mode (SwamiguiSplits *splits, SwamiguiSplitsMode mode); void swamigui_splits_set_width (SwamiguiSplits *splits, int width); void swamigui_splits_set_selection (SwamiguiSplits *splits, IpatchList *items); IpatchList *swamigui_splits_get_selection (SwamiguiSplits *splits); void swamigui_splits_select_items (SwamiguiSplits *splits, GList *items); void swamigui_splits_select_all (SwamiguiSplits *splits); void swamigui_splits_unselect_all (SwamiguiSplits *splits); void swamigui_splits_item_changed (SwamiguiSplits *splits); void swamigui_splits_register_handler (SwamiguiSplitsHandler handler); void swamigui_splits_unregister_handler (SwamiguiSplitsHandler handler); #define swamigui_splits_add(splits, item) \ swamigui_splits_insert (splits, item, -1) SwamiguiSplitsEntry *swamigui_splits_insert (SwamiguiSplits *splits, GObject *item, int index); SwamiguiSplitsEntry *swamigui_splits_lookup_entry (SwamiguiSplits *splits, GObject *item); void swamigui_splits_remove (SwamiguiSplits *splits, GObject *item); void swamigui_splits_remove_all (SwamiguiSplits *splits); void swamigui_splits_set_span_range (SwamiguiSplits *splits, GObject *item, int low, int high); void swamigui_splits_set_root_note (SwamiguiSplits *splits, GObject *item, int val); SwamiControl *swamigui_splits_entry_get_span_control (SwamiguiSplitsEntry *entry); SwamiControl *swamigui_splits_entry_get_root_note_control (SwamiguiSplitsEntry *entry); int swamigui_splits_entry_get_index (SwamiguiSplitsEntry *entry); #endif swami-2.2.0/src/swamigui/SwamiguiStatusbar.c000066400000000000000000000376451361104770400211130ustar00rootroot00000000000000/* * SwamiguiStatusbar.c - A statusbar (multiple labels/progresses) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include #include "SwamiguiStatusbar.h" #include "i18n.h" #include "util.h" #include "libswami/swami_priv.h" enum { PROP_0, PROP_DEFAULT_TIMEOUT }; typedef struct { SwamiguiStatusbar *statusbar; /* parent statusbar instance */ guint id; /* unique message ID */ char *group; /* group ID for this label or NULL */ int timeout; /* timeout for this label in milliseconds (0 for none) */ guint timeout_handle; /* main loop timeout handle or 0 if none */ guint8 pos; /* SwamiguiStatusbarPos */ GtkWidget *widg; /* status widget */ GtkWidget *frame; /* frame around status widget */ } StatusItem; /* allocation and release of status item structs */ #define status_item_new() g_slice_new0 (StatusItem) #define status_item_free(item) g_slice_free (StatusItem, item) /* default message timeout value in milliseconds */ #define DEFAULT_TIMEOUT_VALUE 4000 /* Local Prototypes */ static void swamigui_statusbar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_statusbar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_statusbar_init (SwamiguiStatusbar *statusbar); static gboolean swamigui_statusbar_item_timeout (gpointer data); static GList *swamigui_statusbar_find (SwamiguiStatusbar *statusbar, guint id, const char *group); static void swamigui_statusbar_cb_item_close_clicked (GtkButton *button, gpointer data); /* define the SwamiguiStatusbar type */ G_DEFINE_TYPE (SwamiguiStatusbar, swamigui_statusbar, GTK_TYPE_FRAME); static void swamigui_statusbar_class_init (SwamiguiStatusbarClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->set_property = swamigui_statusbar_set_property; obj_class->get_property = swamigui_statusbar_get_property; g_object_class_install_property (obj_class, PROP_DEFAULT_TIMEOUT, g_param_spec_int ("default-timeout", _("Default Timeout"), _("Default timeout in milliseconds"), 0, G_MAXINT, DEFAULT_TIMEOUT_VALUE, G_PARAM_READWRITE)); } static void swamigui_statusbar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiStatusbar *statusbar = SWAMIGUI_STATUSBAR (object); switch (property_id) { case PROP_DEFAULT_TIMEOUT: statusbar->default_timeout = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_statusbar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiStatusbar *statusbar = SWAMIGUI_STATUSBAR (object); switch (property_id) { case PROP_DEFAULT_TIMEOUT: g_value_set_uint (value, statusbar->default_timeout); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_statusbar_init (SwamiguiStatusbar *statusbar) { GtkWidget *widg; statusbar->id_counter = 1; statusbar->default_timeout = DEFAULT_TIMEOUT_VALUE; gtk_frame_set_shadow_type (GTK_FRAME (statusbar), GTK_SHADOW_IN); statusbar->box = gtk_hbox_new (FALSE, 0); gtk_widget_show (statusbar->box); gtk_container_add (GTK_CONTAINER (statusbar), statusbar->box); /* add the Global group status label item */ widg = swamigui_statusbar_msg_label_new ("", SWAMIGUI_STATUSBAR_GLOBAL_MAXLEN); swamigui_statusbar_add (statusbar, "Global", 0, SWAMIGUI_STATUSBAR_POS_RIGHT, widg); } /** * swamigui_statusbar_new: * * Create a new status bar widget. * * Returns: New widget. */ GtkWidget * swamigui_statusbar_new (void) { return (GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_STATUSBAR, NULL))); } /** * swamigui_statusbar_add: * @statusbar: Statusbar widget * @group: Group identifier (existing message with same group is replaced, * NULL for no group) * @timeout: Timeout of statusbar message in milliseconds * (see #SwamiguiStatusbarTimeout for special values including * #SWAMIGUI_STATUSBAR_TIMEOUT_FOREVER (0) for no timeout and * #SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT to use "default-timeout" property * value) * @pos: Position of message (#SwamiguiStatusbarPos, 0 for default - left) * @widg: Status widget to add to status bar * * Add a widget to a status bar. The @widg is usually created with one of the * helper functions, such as swamigui_statusbar_msg_label_new() or * swamigui_statusbar_msg_progress_new(), although an arbitrary widget can * be added. * * Returns: New message unique ID (which can be used to change/remove message) */ guint swamigui_statusbar_add (SwamiguiStatusbar *statusbar, const char *group, int timeout, guint pos, GtkWidget *widg) { StatusItem *item; GList *p; g_return_val_if_fail (SWAMIGUI_IS_STATUSBAR (statusbar), 0); g_return_val_if_fail (GTK_IS_WIDGET (widg), 0); if (timeout == SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT) timeout = statusbar->default_timeout; if (group) /* if group specified, search for existing item group match */ { for (p = statusbar->items; p; p = p->next) { item = (StatusItem *)(p->data); if (item->group && strcmp (item->group, group) == 0) /* group match? */ { /* replace the widget */ gtk_container_remove (GTK_CONTAINER (item->frame), item->widg); gtk_container_add (GTK_CONTAINER (item->frame), widg); item->widg = widg; gtk_widget_show (widg); g_object_set_data (G_OBJECT (widg), "_item", item); if (item->timeout_handle) /* remove old timeout if any */ g_source_remove (item->timeout_handle); item->timeout = timeout; if (timeout) /* add new timeout callback if timeout given */ g_timeout_add (timeout, swamigui_statusbar_item_timeout, item); return (item->id); } } } item = status_item_new (); item->statusbar = statusbar; item->id = statusbar->id_counter++; item->group = g_strdup (group); item->timeout = timeout; item->pos = pos; item->widg = widg; item->frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (item->frame), GTK_SHADOW_OUT); statusbar->items = g_list_prepend (statusbar->items, item); gtk_container_add (GTK_CONTAINER (item->frame), widg); gtk_widget_show_all (item->frame); g_object_set_data (G_OBJECT (widg), "_item", item); /* pack the new item into the statusbar */ if (pos == SWAMIGUI_STATUSBAR_POS_LEFT) gtk_box_pack_start (GTK_BOX (statusbar->box), item->frame, FALSE, FALSE, 2); else gtk_box_pack_end (GTK_BOX (statusbar->box), item->frame, FALSE, FALSE, 2); if (timeout) /* add new timeout callback if timeout given */ g_timeout_add (timeout, swamigui_statusbar_item_timeout, item); return (item->id); } /* timeout callback used to remove statusbar items after a timeout period */ static gboolean swamigui_statusbar_item_timeout (gpointer data) { StatusItem *item = (StatusItem *)data; swamigui_statusbar_remove (item->statusbar, item->id, NULL); return (FALSE); } /** * swamigui_statusbar_remove: * @statusbar: Statusbar widget * @id: Unique ID of message (0 if @group is specified) * @group: Group of message to remove (%NULL if @id is specified) * * Remove a message by @id or @group. */ void swamigui_statusbar_remove (SwamiguiStatusbar *statusbar, guint id, const char *group) { StatusItem *item; GList *p; g_return_if_fail (SWAMIGUI_IS_STATUSBAR (statusbar)); g_return_if_fail (id != 0 || group != NULL); p = swamigui_statusbar_find (statusbar, id, group); if (!p) return; item = (StatusItem *)(p->data); g_free (item->group); if (item->timeout_handle) /* remove old timeout if any */ g_source_remove (item->timeout_handle); gtk_container_remove (GTK_CONTAINER (statusbar->box), item->frame); statusbar->items = g_list_delete_link (statusbar->items, p); status_item_free (item); } /** * swamigui_statusbar_printf: * @statusbar: Statusbar widget * @format: printf() style format string. * @...: Additional arguments for @format string * * A convenience function to display a message label to a statusbar with the * "default-timeout" property value for the timeout, no group and positioned * left. This is commonly used to display an operation that was performed. */ void swamigui_statusbar_printf (SwamiguiStatusbar *statusbar, const char *format, ...) { GtkWidget *label; va_list args; char *s; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); label = swamigui_statusbar_msg_label_new (s, 0); g_free (s); swamigui_statusbar_add (statusbar, NULL, SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT, SWAMIGUI_STATUSBAR_POS_LEFT, label); } /* internal function used to find a statusbar item */ static GList * swamigui_statusbar_find (SwamiguiStatusbar *statusbar, guint id, const char *group) { StatusItem *item; GList *p; for (p = statusbar->items; p; p = p->next) { item = (StatusItem *)(p->data); /* criteria matches? */ if ((id && item->id == id) || (group && item->group && strcmp (item->group, group) == 0)) return (p); } return (NULL); } /** * swamigui_statusbar_msg_label_new: * @label: Label text to assign to new widget * @maxlen: Maximum length of label widget (sets size, 0 to set to width of @label) * * A helper function to create a label widget for use in a statusbar. Doesn't * do a whole lot beyond just creating a regular #GtkLabel and setting its * max length. */ GtkWidget * swamigui_statusbar_msg_label_new (const char *label, guint maxlen) { GtkWidget *widg; widg = gtk_label_new (label); if (maxlen > 0) gtk_label_set_width_chars (GTK_LABEL (widg), maxlen); gtk_misc_set_alignment (GTK_MISC (widg), 0.0, 0.5); gtk_widget_show_all (widg); return (widg); } /** * swamigui_statusbar_msg_progress_new: * @label: Label text to assign to new widget * @close: Close callback function (%NULL to not have a close button) * * A helper function to create a progress status bar item. */ GtkWidget * swamigui_statusbar_msg_progress_new (const char *label, SwamiguiStatusbarCloseFunc close) { GtkWidget *hbox; GtkWidget *progress; GtkWidget *btn; GtkWidget *image; hbox = gtk_hbox_new (FALSE, 0); progress = gtk_progress_bar_new (); if (label) gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), label); gtk_box_pack_start (GTK_BOX (hbox), progress, FALSE, FALSE, 0); if (close) { btn = gtk_button_new (); image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (btn), image); gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (hbox), "_close", close); g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (swamigui_statusbar_cb_item_close_clicked), hbox); } /* used by swamigui_statusbar_msg_set_progress() */ g_object_set_data (G_OBJECT (hbox), "_progress", progress); gtk_widget_show_all (hbox); return (hbox); } /* callback which gets called when an item close button is clicked */ static void swamigui_statusbar_cb_item_close_clicked (GtkButton *button, gpointer data) { GtkWidget *hbox = (GtkWidget *)data; SwamiguiStatusbarCloseFunc closefunc; StatusItem *statusitem; closefunc = g_object_get_data (G_OBJECT (hbox), "_close"); g_return_if_fail (closefunc != NULL); /* "_item" gets set when it is added to the statusbar */ statusitem = g_object_get_data (G_OBJECT (hbox), "_item"); g_return_if_fail (statusitem != NULL); if (closefunc (statusitem->statusbar, hbox)) swamigui_statusbar_remove (statusitem->statusbar, statusitem->id, NULL); } /** * swamigui_statusbar_msg_set_timeout: * @statusbar: Statusbar widget * @id: Unique ID of message (0 if @group is specified) * @group: Group of message (%NULL if @id is specified) * @timeout: New timeout of message in milliseconds * (see #SwamiguiStatusbarTimeout for special values including * #SWAMIGUI_STATUSBAR_TIMEOUT_FOREVER (0) for no timeout and * #SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT to use "default-timeout" property * value) * * Modify the timeout of an existing message in the statusbar. Message is * selected by @id or @group. */ void swamigui_statusbar_msg_set_timeout (SwamiguiStatusbar *statusbar, guint id, const char *group, int timeout) { StatusItem *item; GList *p; g_return_if_fail (SWAMIGUI_IS_STATUSBAR (statusbar)); g_return_if_fail (id != 0 || group != NULL); p = swamigui_statusbar_find (statusbar, id, group); if (!p) return; if (timeout == SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT) timeout = statusbar->default_timeout; item = (StatusItem *)(p->data); if (item->timeout_handle) /* remove old timeout if any */ g_source_remove (item->timeout_handle); item->timeout = timeout; if (timeout) /* add new timeout callback if timeout given */ g_timeout_add (timeout, swamigui_statusbar_item_timeout, item); } /** * swamigui_statusbar_msg_set_label: * @statusbar: Statusbar widget * @id: Unique ID of message (0 if @group is specified) * @group: Group of message (%NULL if @id is specified) * @label: New label text to assign to statusbar item * * Modify the label of an existing message in the statusbar. Message is * selected by @id or @group. This function should only be used for #GtkLabel * widget status items or those created with * swamigui_statusbar_msg_label_new() and swamigui_statusbar_msg_progress_new(). */ void swamigui_statusbar_msg_set_label (SwamiguiStatusbar *statusbar, guint id, const char *group, const char *label) { StatusItem *item; GtkWidget *progress; GList *p; g_return_if_fail (SWAMIGUI_IS_STATUSBAR (statusbar)); g_return_if_fail (id != 0 || group != NULL); p = swamigui_statusbar_find (statusbar, id, group); if (!p) return; item = (StatusItem *)(p->data); progress = g_object_get_data (G_OBJECT (item->widg), "_progress"); g_return_if_fail (GTK_IS_LABEL (item->widg) || progress); if (progress) gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), label); else gtk_label_set_text (GTK_LABEL (item->widg), label); } /** * swamigui_statusbar_msg_set_progress: * @statusbar: Statusbar widget * @id: Unique ID of message (0 if @group is specified) * @group: Group of message (%NULL if @id is specified) * @val: New progress value (0.0 to 1.0) * * Modify the progress indicator of an existing message in the statusbar. * Message is selected by @id or @group. This function should only be used for * widget status items created with swamigui_statusbar_msg_progress_new(). */ void swamigui_statusbar_msg_set_progress (SwamiguiStatusbar *statusbar, guint id, const char *group, double val) { GtkWidget *progress; StatusItem *item; GList *p; g_return_if_fail (SWAMIGUI_IS_STATUSBAR (statusbar)); g_return_if_fail (id != 0 || group != NULL); p = swamigui_statusbar_find (statusbar, id, group); if (!p) return; item = (StatusItem *)(p->data); progress = g_object_get_data (G_OBJECT (item->widg), "_progress"); g_return_if_fail (progress != NULL); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), val); } swami-2.2.0/src/swamigui/SwamiguiStatusbar.h000066400000000000000000000075701361104770400211120ustar00rootroot00000000000000/* * SwamiguiStatusbar.h - A statusbar (multiple labels/progresses) * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_STATUSBAR_H__ #define __SWAMIGUI_STATUSBAR_H__ /* max chars for "Global" group status label item */ #define SWAMIGUI_STATUSBAR_GLOBAL_MAXLEN 24 #include typedef struct _SwamiguiStatusbar SwamiguiStatusbar; typedef struct _SwamiguiStatusbarClass SwamiguiStatusbarClass; #define SWAMIGUI_TYPE_STATUSBAR (swamigui_statusbar_get_type ()) #define SWAMIGUI_STATUSBAR(obj) \ (GTK_CHECK_CAST ((obj), SWAMIGUI_TYPE_STATUSBAR, SwamiguiStatusbar)) #define SWAMIGUI_STATUSBAR_CLASS(klass) \ (GTK_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_STATUSBAR, \ SwamiguiStatusbarClass)) #define SWAMIGUI_IS_STATUSBAR(obj) \ (GTK_CHECK_TYPE ((obj), SWAMIGUI_TYPE_STATUSBAR)) #define SWAMIGUI_IS_STATUSBAR_CLASS(klass) \ (GTK_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_STATUSBAR)) /* Statusbar widget */ struct _SwamiguiStatusbar { GtkFrame parent; /*< private >*/ GtkWidget *box; /* the hbox within the statusbar frame */ GList *items; /* active items (see StatusItem in .c) */ guint id_counter; /* unique status item ID counter */ int default_timeout; /* default timeout value in msecs */ }; /* Statusbar widget class */ struct _SwamiguiStatusbarClass { GtkFrameClass parent_class; }; /** * SwamiguiStatusbarCloseFunc: * @statusbar: The status bar widget * @widg: The message widget * * Callback function prototype which gets called when a close button on a * progress status bar item gets activated. * * Returns: Should return %TRUE to remove the item from the status bar, %FALSE * to keep it (useful if a confirmation dialog is popped for the user, etc). */ typedef gboolean (*SwamiguiStatusbarCloseFunc)(SwamiguiStatusbar *statusbar, GtkWidget *widg); typedef enum { SWAMIGUI_STATUSBAR_POS_LEFT, SWAMIGUI_STATUSBAR_POS_RIGHT } SwamiguiStatusbarPos; /* some special timeout values for statusbar messages */ typedef enum { SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT = -1, /* uses "default-timeout" property */ SWAMIGUI_STATUSBAR_TIMEOUT_FOREVER = 0 /* don't timeout */ } SwamiguiStatusbarTimeout; GType swamigui_statusbar_get_type (void); GtkWidget *swamigui_statusbar_new (void); guint swamigui_statusbar_add (SwamiguiStatusbar *statusbar, const char *group, int timeout, guint pos, GtkWidget *widg); void swamigui_statusbar_remove (SwamiguiStatusbar *statusbar, guint id, const char *group); void swamigui_statusbar_printf (SwamiguiStatusbar *statusbar, const char *format, ...) G_GNUC_PRINTF (2, 3); GtkWidget *swamigui_statusbar_msg_label_new (const char *label, guint maxlen); GtkWidget *swamigui_statusbar_msg_progress_new (const char *label, SwamiguiStatusbarCloseFunc close); void swamigui_statusbar_msg_set_timeout (SwamiguiStatusbar *statusbar, guint id, const char *group, int timeout); void swamigui_statusbar_msg_set_label (SwamiguiStatusbar *statusbar, guint id, const char *group, const char *label); void swamigui_statusbar_msg_set_progress (SwamiguiStatusbar *statusbar, guint id, const char *group, double val); #endif swami-2.2.0/src/swamigui/SwamiguiTree.c000066400000000000000000001472611361104770400200360ustar00rootroot00000000000000/* * SwamiguiTree.c - Swami tabbed tree object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include #include "SwamiguiTree.h" #include "SwamiguiTreeStore.h" #include "SwamiguiItemMenu.h" #include "SwamiguiRoot.h" #include "SwamiguiDnd.h" #include "i18n.h" /* properties */ enum { PROP_0, PROP_SELECTION_SINGLE, PROP_SELECTION, PROP_SELECTED_STORE, /* currently selected store */ PROP_STORE_LIST /* list of tree store objects (multi-tabbed tree) */ }; /* Local Prototypes */ static void swamigui_tree_class_init (SwamiguiTreeClass *klass); static void swamigui_tree_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void swamigui_tree_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void swamigui_tree_init (SwamiguiTree *tree); static void swamigui_tree_cb_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data); static void swamigui_tree_cb_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer data); static void swamigui_tree_cb_drag_data_get (GtkWidget *widget, GdkDragContext *drag_context, GtkSelectionData *data, guint info, guint time, gpointer user_data); static void swamigui_tree_real_set_store (SwamiguiTree *tree, SwamiguiTreeStore *store); static void swamigui_tree_update_selection (SwamiguiTree *tree); static void tree_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static void swamigui_tree_finalize (GObject *object); static gboolean swamigui_tree_widget_popup_menu (GtkWidget *widg); static void swamigui_tree_cb_search_entry_changed (GtkEntry *entry, gpointer user_data); static void swamigui_tree_cb_search_next_clicked (GtkButton *button, gpointer user_data); static void swamigui_tree_cb_search_prev_clicked (GtkButton *button, gpointer user_data); static GtkWidget * swamigui_tree_create_scrolled_tree_view (SwamiguiTree *tree, SwamiguiTreeStore *store, GtkTreeView **out_treeview); static void swamigui_tree_item_icon_cell_data (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data); static void swamigui_tree_item_label_cell_data (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data); static void tree_cb_selection_changed (GtkTreeSelection *selection, SwamiguiTree *tree); static gboolean swamigui_tree_cb_button_press (GtkWidget *widg, GdkEventButton *event, SwamiguiTree *tree); static void swamigui_tree_do_popup_menu (SwamiguiTree *tree, GObject *rclick_item, GdkEventButton *event); static void swamigui_tree_set_selection_real (SwamiguiTree *tree, IpatchList *list, int notify_flags); static void swamigui_tree_real_search_next (SwamiguiTree *tree, gboolean usematch); static void set_search_match_item (SwamiguiTree *tree, GtkTreeIter *iter, GObject *obj, int startpos, const char *search); static void reset_search_match_item (SwamiguiTree *tree, GList **new_ancestry); static int str_index (const char *haystack, const char *needle); static gboolean tree_iter_recursive_next (GtkTreeModel *model, GtkTreeIter *iter); static gboolean tree_iter_recursive_prev (GtkTreeModel *model, GtkTreeIter *iter); static GObjectClass *parent_class = NULL; GType swamigui_tree_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiTreeClass), NULL, NULL, (GClassInitFunc) swamigui_tree_class_init, NULL, NULL, sizeof (SwamiguiTree), 0, (GInstanceInitFunc) swamigui_tree_init, }; obj_type = g_type_register_static (GTK_TYPE_VBOX, "SwamiguiTree", &obj_info, 0); } return (obj_type); } static void swamigui_tree_class_init (SwamiguiTreeClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widg_class = GTK_WIDGET_CLASS (klass); parent_class = g_type_class_peek_parent (klass); widg_class->popup_menu = swamigui_tree_widget_popup_menu; obj_class->set_property = swamigui_tree_set_property; obj_class->get_property = swamigui_tree_get_property; obj_class->finalize = swamigui_tree_finalize; g_object_class_install_property (obj_class, PROP_SELECTION_SINGLE, g_param_spec_object ("selection-single", "Single selection", "Single selected object", G_TYPE_OBJECT, /* FIXME? */ G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SELECTION, g_param_spec_object ("selection", "Selection", "Selection list (static)", IPATCH_TYPE_LIST, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_SELECTED_STORE, g_param_spec_object ("selected-store", "Selection store", "Selected tree store", SWAMIGUI_TYPE_TREE_STORE, G_PARAM_READWRITE)); g_object_class_install_property (obj_class, PROP_STORE_LIST, g_param_spec_object ("store-list", "Store list", "Tree store list", IPATCH_TYPE_LIST, G_PARAM_READWRITE)); } static void swamigui_tree_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SwamiguiTree *tree = SWAMIGUI_TREE (object); SwamiguiTreeStore *store; IpatchList *list = NULL; GObject *item; switch (property_id) { case PROP_SELECTION_SINGLE: item = g_value_get_object (value); if (item) { list = ipatch_list_new (); /* ++ ref new list */ list->items = g_list_append (list->items, g_object_ref (item)); } swamigui_tree_set_selection_real (tree, list, 1); if (list) g_object_unref (list); /* -- unref list */ break; case PROP_SELECTION: list = g_value_get_object (value); swamigui_tree_set_selection_real (tree, list, 2); break; case PROP_SELECTED_STORE: store = SWAMIGUI_TREE_STORE (g_value_get_object (value)); swamigui_tree_set_selected_store (tree, store); break; case PROP_STORE_LIST: list = IPATCH_LIST (g_value_get_object (value)); swamigui_tree_set_store_list (tree, list); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_tree_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SwamiguiTree *tree = SWAMIGUI_TREE (object); GObject *item; IpatchList *list; switch (property_id) { case PROP_SELECTION_SINGLE: item = swamigui_tree_get_selection_single (tree); g_value_set_object (value, item); break; case PROP_SELECTION: /* get tree selection (uses directly without ref!) */ list = swamigui_tree_get_selection (tree); g_value_set_object (value, list); break; case PROP_SELECTED_STORE: g_value_set_object (value, tree->selstore); break; case PROP_STORE_LIST: g_value_set_object (value, tree->stores); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void swamigui_tree_init (SwamiguiTree *tree) { GtkWidget *widg; GtkWidget *image; tree->notebook = GTK_NOTEBOOK (gtk_notebook_new ()); gtk_widget_show (GTK_WIDGET (tree->notebook)); gtk_box_pack_start (GTK_BOX (tree), GTK_WIDGET (tree->notebook), TRUE, TRUE, 0); /* add the search widgets */ tree->search_box = gtk_hbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (tree), tree->search_box, FALSE, FALSE, 2); image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); widg = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (widg), image); gtk_button_set_relief (GTK_BUTTON (widg), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX (tree->search_box), widg, FALSE, FALSE, 0); widg = gtk_label_new (_("Search")); gtk_box_pack_start (GTK_BOX (tree->search_box), widg, FALSE, FALSE, 2); tree->search_entry = GTK_ENTRY (gtk_entry_new ()); g_signal_connect (G_OBJECT (tree->search_entry), "changed", G_CALLBACK (swamigui_tree_cb_search_entry_changed), tree); gtk_box_pack_start (GTK_BOX (tree->search_box), GTK_WIDGET (tree->search_entry), TRUE, TRUE, 0); image = gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU); widg = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (widg), image); gtk_button_set_relief (GTK_BUTTON (widg), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX (tree->search_box), widg, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (widg), "clicked", G_CALLBACK (swamigui_tree_cb_search_prev_clicked), tree); image = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU); widg = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (widg), image); gtk_button_set_relief (GTK_BUTTON (widg), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX (tree->search_box), widg, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (widg), "clicked", G_CALLBACK (swamigui_tree_cb_search_next_clicked), tree); gtk_widget_show_all (tree->search_box); /* attach to the "switch-page" signal to update selection when the current notebook page is changed */ g_signal_connect (tree->notebook, "switch-page", G_CALLBACK (swamigui_tree_cb_switch_page), tree); } /* callback when notebook page changes */ static void swamigui_tree_cb_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) { SwamiguiTree *tree = SWAMIGUI_TREE (user_data); SwamiguiTreeStore *store; if (!tree->stores) return; store = g_list_nth_data (tree->stores->items, page_num); swamigui_tree_real_set_store (tree, store); } static void swamigui_tree_real_set_store (SwamiguiTree *tree, SwamiguiTreeStore *store) { int n; if (!tree->stores) return; n = g_list_index (tree->stores->items, store); if (n == -1) return; if (store != tree->selstore) { tree->selstore = store; tree->seltree = g_list_nth_data (tree->treeviews, n); /* update tree selection to that of the current selected tree view */ swamigui_tree_update_selection (tree); } } /* update the tree selection to the currently selected tree */ static void swamigui_tree_update_selection (SwamiguiTree *tree) { GtkTreeSelection *selection; gboolean new_sel_single; GList *list = NULL; if (!tree->seltree) return; /* shouldn't happen, but just in case */ selection = gtk_tree_view_get_selection (tree->seltree); if (tree->selection) g_object_unref (tree->selection); /* -- unref old sel */ /* convert tree selection to list */ gtk_tree_selection_selected_foreach (selection, tree_selection_foreach_func, &list); tree->selection = ipatch_list_new (); /* ++ ref new list object */ /* set the origin object of the selection, so swamigui_root can report this to those who want to know. */ swami_object_set_origin (G_OBJECT (tree->selection), G_OBJECT (tree)); if (list) { list = g_list_reverse (list); /* prepended, so reverse it */ tree->selection->items = list; } /* notify single selection change if new single selected or was single */ new_sel_single = list && !list->next; if (new_sel_single || tree->sel_single) g_object_notify (G_OBJECT (tree), "selection-single"); tree->sel_single = new_sel_single; g_object_notify (G_OBJECT (tree), "selection"); /* notify selection change */ } static void tree_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { GList **plist = (GList **)data; GObject *obj; gtk_tree_model_get (model, iter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref obj */ -1); if (obj) *plist = g_list_prepend (*plist, obj); /* !! list takes over reference */ } static void swamigui_tree_finalize (GObject *object) { SwamiguiTree *tree = SWAMIGUI_TREE (object); if (tree->stores) g_object_unref (tree->stores); if (tree->treeviews) g_list_free (tree->treeviews); if (tree->selection) g_object_unref (tree->selection); } static gboolean swamigui_tree_widget_popup_menu (GtkWidget *widg) { SwamiguiTree *tree = SWAMIGUI_TREE (widg); GtkTreeModel *model; GtkTreeIter iter; GObject *rclick_item = NULL; GtkTreePath *path; if (!tree->seltree) return (TRUE); /* shouldn't happen, but just in case */ gtk_tree_view_get_cursor (GTK_TREE_VIEW (tree->seltree), &path, NULL); if (path) { model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->seltree)); /* convert path to iter */ if (gtk_tree_model_get_iter (model, &iter, path)) { rclick_item = swamigui_tree_store_node_get_item (SWAMIGUI_TREE_STORE (model), &iter); } gtk_tree_path_free (path); } swamigui_tree_do_popup_menu (tree, rclick_item, NULL); return (TRUE); } /** * swamigui_tree_new: * @stores: List of tree stores to use or %NULL to set later * (see swamigui_tree_set_store_list()). * * Create a new Swami tree object * * Returns: Swami tree object */ GtkWidget * swamigui_tree_new (IpatchList *stores) { GtkWidget *tree; tree = GTK_WIDGET (g_object_new (SWAMIGUI_TYPE_TREE, NULL)); if (stores) swamigui_tree_set_store_list (SWAMIGUI_TREE (tree), stores); return (tree); } /** * swamigui_tree_set_store_list: * @tree: Swami GUI tree view * @list: List of #SwamiguiTreeStore objects, this list is used directly and * should not be modified after calling this function. * * Set the tree stores of a tree view. Each tree store gets its own tab * in the tabbed @tree. */ void swamigui_tree_set_store_list (SwamiguiTree *tree, IpatchList *list) { GList *curlist = NULL, *newlist; GList *p, *p2; int pos, index; GtkWidget *page; GtkWidget *label; GtkTreeView *treeview; char *name; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_return_if_fail (IPATCH_IS_LIST (list)); if (tree->stores) curlist = tree->stores->items; newlist = list->items; /* check if current and new store lists are equivalent */ if (curlist) { for (p = newlist, p2 = curlist; p && p2; p = p->next, p2 = p2->next) if (p->data != p2->data) break; if (!p && !p2) return; /* they have the same stores? - return */ } /* copy current list so we can modify it (part of an IpatchList) */ if (curlist) curlist = g_list_copy (curlist); for (p = newlist, pos = 0; p; p = p->next, pos++) /* loop over new stores */ { /* store already in current list? */ if (curlist && (index = g_list_index (curlist, p->data)) != -1) { if (index != pos) /* does it need to be moved? */ { /* move the item in our copy of curlist */ curlist = g_list_remove (curlist, p->data); curlist = g_list_insert (curlist, p->data, pos); /* move the notebook page */ page = gtk_notebook_get_nth_page (tree->notebook, index); gtk_notebook_reorder_child (tree->notebook, page, pos); /* update the treeviews list too */ treeview = g_list_nth_data (tree->treeviews, index); tree->treeviews = g_list_remove (tree->treeviews, treeview); tree->treeviews = g_list_insert (tree->treeviews, treeview, pos); } continue; } /* keep curlist in sync with what we are doing */ curlist = g_list_insert (curlist, p->data, pos); /* create a new scrolled tree view */ page = swamigui_tree_create_scrolled_tree_view (tree, SWAMIGUI_TREE_STORE (p->data), &treeview); swami_object_get (p->data, "name", &name, NULL); /* create label for tab */ label = gtk_label_new (name); gtk_widget_show (label); g_free (name); /* insert into the notebook */ gtk_notebook_insert_page (tree->notebook, page, label, pos); /* update treeviews list also */ tree->treeviews = g_list_insert (tree->treeviews, treeview, pos); } /* remove any remaining items in curlist (not part of new store list) */ for (p = g_list_nth (curlist, pos); p; p = p->next, pos++) { /* remove notebook page */ gtk_notebook_remove_page (tree->notebook, pos); /* remove the GtkTreeView list node */ tree->treeviews = g_list_remove_link (tree->treeviews, g_list_nth (tree->treeviews, pos)); } if (tree->stores) g_object_unref (tree->stores); /* -- unref old stores */ tree->stores = g_object_ref (list); g_list_free (curlist); /* duplicated list not needed anymore */ /* Select first tree view if none currently selected. */ if (!tree->seltree && tree->stores && tree->stores->items) swamigui_tree_real_set_store (tree, SWAMIGUI_TREE_STORE (tree->stores->items->data)); } static GtkWidget * swamigui_tree_create_scrolled_tree_view (SwamiguiTree *tree, SwamiguiTreeStore *store, GtkTreeView **out_treeview) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkWidget *scrollwin; GtkTreeView *treeview; GtkTargetEntry target_table[] = { { SWAMIGUI_DND_OBJECT_NAME, 0, SWAMIGUI_DND_OBJECT_INFO }, { SWAMIGUI_DND_URI_NAME, 0, SWAMIGUI_DND_URI_INFO } }; scrollwin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show (scrollwin); treeview = GTK_TREE_VIEW (gtk_tree_view_new ()); /* all rows have same height, enable fixed height mode for increased speed FIXME - Breaks scroll bar, why??? */ // g_object_set (treeview, "fixed-height-mode", TRUE, NULL); /* disable interactive search (breaks playing of piano from keyboard) */ g_object_set (treeview, "enable-search", FALSE, NULL); gtk_container_add (GTK_CONTAINER (scrollwin), GTK_WIDGET (treeview)); gtk_widget_show (GTK_WIDGET (treeview)); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); /* add pixbuf column to tree view widget */ renderer = gtk_cell_renderer_pixbuf_new (); column = gtk_tree_view_column_new (); // g_object_set (column, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_cell_data_func (column, renderer, swamigui_tree_item_icon_cell_data, treeview, NULL); /* add label column to tree view widget */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_cell_data_func (column, renderer, swamigui_tree_item_label_cell_data, tree, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* assign the tree store */ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); /* attach selection changed signal handler */ g_signal_connect (selection, "changed", G_CALLBACK (tree_cb_selection_changed), tree); /* for right click menus */ g_signal_connect (treeview, "button-press-event", G_CALLBACK (swamigui_tree_cb_button_press), tree); /* enable tree drag and drop */ gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview), target_table, G_N_ELEMENTS (target_table), GDK_ACTION_COPY); gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview), GDK_BUTTON1_MASK, target_table, G_N_ELEMENTS (target_table), GDK_ACTION_COPY); g_signal_connect (G_OBJECT (treeview), "drag-data-received", G_CALLBACK (swamigui_tree_cb_drag_data_received), tree); g_signal_connect (G_OBJECT (treeview), "drag-data-get", G_CALLBACK (swamigui_tree_cb_drag_data_get), tree); *out_treeview = treeview; return (scrollwin); } /* cell renderer for tree pixmap icons */ static void swamigui_tree_item_icon_cell_data (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { GtkTreeView *treeview = GTK_TREE_VIEW (data); GdkPixbuf *icon = NULL; char *stock_id; gtk_tree_model_get (tree_model, iter, SWAMIGUI_TREE_STORE_ICON_COLUMN, &stock_id, -1); if (stock_id) /* ++ ref new closed pixmap */ icon = gtk_widget_render_icon (GTK_WIDGET (treeview), stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); g_object_set (cell, "pixbuf", icon, NULL); if (icon) g_object_unref (icon); /* -- unref icon */ } /* cell renderer for tree labels (overridden to allow for search highlighting) */ static void swamigui_tree_item_label_cell_data (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { SwamiguiTree *tree = SWAMIGUI_TREE (data); PangoAttrList *alist = NULL; PangoAttribute *attr; char *label; GObject *obj; gtk_tree_model_get (tree_model, iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, &label, /* ++ alloc */ SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref */ -1); /* should this object be highlighted for in progress search? */ if (tree->search_match == obj) { alist = pango_attr_list_new (); attr = pango_attr_background_new (0, 65535, 0); attr->start_index = tree->search_start_pos; attr->end_index = tree->search_end_pos; pango_attr_list_insert (alist, attr); } g_object_set (cell, "text", label, "attributes", alist, NULL); if (alist) pango_attr_list_unref (alist); g_object_unref (obj); /* -- unref */ g_free (label); /* -- free */ } /* a callback for when the tree view selection changes */ static void tree_cb_selection_changed (GtkTreeSelection *selection, SwamiguiTree *tree) { GtkTreeView *treeview; treeview = gtk_tree_selection_get_tree_view (selection); /* update currently selected GtkTreeView and store */ tree->seltree = treeview; tree->selstore = SWAMIGUI_TREE_STORE (gtk_tree_view_get_model (treeview)); /* update the current selection to that of the current selected tree */ swamigui_tree_update_selection (tree); } /* when a tree view gets a button press */ static gboolean swamigui_tree_cb_button_press (GtkWidget *widg, GdkEventButton *event, SwamiguiTree *tree) { GtkTreeSelection *tree_sel; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GObject *rclick_item; int x, y; if (!tree->seltree) return (FALSE); /* Shouldn't happen, but.. */ if (event->button != 3) return (FALSE); x = (int)event->x; /* x and y coordinates are of type double */ y = (int)event->y; /* convert to integer */ /* get the tree path at the given mouse cursor position * ++ alloc path */ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree->seltree), x, y, &path, NULL, NULL, NULL); if (!path) return (FALSE); /* right click */ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->seltree)); /* convert path to iter */ if (!gtk_tree_model_get_iter (model, &iter, path)) { gtk_tree_path_free (path); /* -- free path */ return (FALSE); } gtk_tree_path_free (path); /* -- free path */ /* stop button press event propagation */ gtk_signal_emit_stop_by_name (GTK_OBJECT (widg), "button-press-event"); rclick_item = swamigui_tree_store_node_get_item (SWAMIGUI_TREE_STORE (model), &iter); /* check if right click is not part of current selection */ if (!tree->selection || !g_list_find (tree->selection->items, rclick_item)) { /* click not on selection, clear it and select the single item */ tree_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->seltree)); gtk_tree_selection_unselect_all (tree_sel); gtk_tree_selection_select_iter (tree_sel, &iter); tree_cb_selection_changed (tree_sel, tree); /* update selection */ } /* do the menu popup */ swamigui_tree_do_popup_menu (tree, rclick_item, event); return (TRUE); } static void swamigui_tree_do_popup_menu (SwamiguiTree *tree, GObject *rclick_item, GdkEventButton *event) { SwamiguiItemMenu *menu; int button, event_time; if (!tree->selection) return; /* No selection, no menu */ menu = swamigui_item_menu_new (); g_object_set (menu, "selection", tree->selection, "right-click", rclick_item, "creator", tree, NULL); swamigui_item_menu_generate (menu); if (event) { button = event->button; event_time = event->time; } else { button = 0; event_time = gtk_get_current_event_time (); } gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, event_time); } /* callback for when drag data received */ static void swamigui_tree_cb_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer data) { SwamiguiTree *tree = SWAMIGUI_TREE (data); SwamiguiTreeStore *store; GtkTreeView *treeview = GTK_TREE_VIEW (widget); char *atomname; if (selection_data->format != 8 || selection_data->length == 0) { g_critical ("DND on Swami tree had invalid format (%d) or length (%d)", selection_data->format, selection_data->length); return; } atomname = gdk_atom_name (selection_data->type); /* drag and drop between or within tree? */ if (strcmp (atomname, SWAMIGUI_DND_OBJECT_NAME) == 0) { GtkTreePath *path; GtkTreeIter iter; IpatchItem *destitem; int itemcount, pastecount; IpatchList *objlist; GList *p; objlist = *((IpatchList **)(selection_data->data)); /* make sure source object is an IpatchList (could be another object type) */ if (!IPATCH_IS_LIST (objlist)) return; /* get path to destination drop row */ if (!gtk_tree_view_get_path_at_pos (treeview, x, y, &path, NULL, NULL, NULL)) return; /* return if no row at drop position */ /* get iterator for path */ if (!gtk_tree_model_get_iter (gtk_tree_view_get_model (treeview), &iter, path)) { gtk_tree_path_free (path); return; } gtk_tree_path_free (path); store = swamigui_tree_get_selected_store (tree); destitem = IPATCH_ITEM (swamigui_tree_store_node_get_item (store, &iter)); if (!destitem) return; /* loop over source items */ for (p = objlist->items, itemcount = 0, pastecount = 0; p; p = p->next, itemcount++) { if (IPATCH_ITEM (p->data)) { if (ipatch_simple_paste (destitem, (IpatchItem *)(p->data), NULL)) pastecount++; } } if (itemcount > 0) { if (itemcount != pastecount) swamigui_statusbar_printf (swamigui_root->statusbar, _("Pasted %d of %d item(s)"), pastecount, itemcount); else swamigui_statusbar_printf (swamigui_root->statusbar, _("Pasted %d item(s)"), itemcount); } } /* drop of external file URI on tree */ else if (strcmp (atomname, SWAMIGUI_DND_URI_NAME) == 0) { char *uri_list; char **uris; char *fname; GError *err = NULL; int i; uri_list = g_strndup ((char *)(selection_data->data), selection_data->length); uris = g_strsplit (uri_list, "\r\n", 0); /* loop over URIs and attempt to open any that are file names */ for (i = 0; uris && uris[i]; i++) { fname = g_filename_from_uri (uris[i], NULL, NULL); if (fname) { if (!swami_root_patch_load (swami_root, fname, NULL, &err)) { g_critical (_("Failed to load DnD file '%s': %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); } g_free (fname); } } g_strfreev (uris); g_free (uri_list); } } static void swamigui_tree_cb_drag_data_get (GtkWidget *widget, GdkDragContext *drag_context, GtkSelectionData *data, guint info, guint time, gpointer user_data) { SwamiguiTree *tree = SWAMIGUI_TREE (user_data); GdkAtom atom; #if GTK_CHECK_VERSION (2, 10, 0) atom = gdk_atom_intern_static_string (SWAMIGUI_DND_OBJECT_NAME); #else atom = gdk_atom_intern (SWAMIGUI_DND_OBJECT_NAME, FALSE); #endif gtk_selection_data_set (data, atom, 8, (guint8 *)(&tree->selection), sizeof (IpatchList *)); } /* callback for when tree search entry text changes */ static void swamigui_tree_cb_search_entry_changed (GtkEntry *entry, gpointer user_data) { SwamiguiTree *tree = SWAMIGUI_TREE (user_data); const char *search; search = gtk_entry_get_text (entry); /* get current search string */ swamigui_tree_search_set_text (tree, search); } static void swamigui_tree_cb_search_next_clicked (GtkButton *button, gpointer user_data) { SwamiguiTree *tree = SWAMIGUI_TREE (user_data); swamigui_tree_search_next (tree); } static void swamigui_tree_cb_search_prev_clicked (GtkButton *button, gpointer user_data) { SwamiguiTree *tree = SWAMIGUI_TREE (user_data); swamigui_tree_search_prev (tree); } /** * swamigui_tree_get_store_list: * @tree: Swami GUI tree view * * Gets the tree stores of a tree view. * * Returns: List of #SwamiguiTreeStore objects or %NULL if none, NO * reference is added and so the list should only be used within the * context of the calling function unless referenced or duplicated. */ IpatchList * swamigui_tree_get_store_list (SwamiguiTree *tree) { g_return_val_if_fail (SWAMIGUI_IS_TREE (tree), NULL); return (tree->stores); } /** * swamigui_tree_set_selected_store: * @tree: Swami tree object * @store: Store to select as the active store * * Sets the currently selected store. The notebook tab containing @store will * be selected which will cause the current tree selection to be updated to the * item selection of the GtkTreeView contained therein. */ void swamigui_tree_set_selected_store (SwamiguiTree *tree, SwamiguiTreeStore *store) { int store_index; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (tree->stores != NULL); store_index = g_list_index (tree->stores->items, store); g_return_if_fail (store_index != -1); gtk_notebook_set_current_page (tree->notebook, store_index); } /** * swamigui_tree_get_selected_store: * @tree: Swami tree object * * Get the currently selected tree store (displayed in the current notebook tab). * * Returns: Currently selected tree store or %NULL if none. Not referenced and * so caller should take care to reference it if using outside of calling scope. */ SwamiguiTreeStore * swamigui_tree_get_selected_store (SwamiguiTree *tree) { g_return_val_if_fail (SWAMIGUI_IS_TREE (tree), NULL); return (tree->selstore); } /** * swamigui_tree_get_selection_single: * @tree: Swami tree object * * Get and insure single item selection in Swami tree object * * Returns: The currently selected single item or %NULL if multiple or no items * are selected. A reference is not added so caller should take care to * reference it if using outside of the calling scope. */ GObject * swamigui_tree_get_selection_single (SwamiguiTree *tree) { GList *sel = NULL; g_return_val_if_fail (SWAMIGUI_IS_TREE (tree), NULL); if (tree->selection) sel = tree->selection->items; if (sel && !sel->next) return (G_OBJECT (sel->data)); else return (NULL); } /** * swamigui_tree_get_selection: * @tree: Swami tree object * * Get Swami tree selection. * * Returns: List of items in the tree selection or %NULL if no items * selected. The returned list object is internal and should not be * modified. * * NOTE - The list's reference count is not incremented, so if it is * desirable to use the list beyond the scope of the calling function * it should be duplicated or a reference added. */ IpatchList * swamigui_tree_get_selection (SwamiguiTree *tree) { g_return_val_if_fail (SWAMIGUI_IS_TREE (tree), NULL); return (tree->selection); } /** * swamigui_tree_clear_selection: * @tree: Swami tree object * * Clear tree selection (unselect all items) */ void swamigui_tree_clear_selection (SwamiguiTree *tree) { GtkTreeSelection *sel; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); if (!tree->seltree) return; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->seltree)); gtk_tree_selection_unselect_all (sel); } /** * swamigui_tree_set_selection: * @tree: Swami tree object * @list: List of objects to select * * Set the tree selection. List of objects must be in the same tree store * (notebook tab). If items are in a non selected store it will become * selected. */ void swamigui_tree_set_selection (SwamiguiTree *tree, IpatchList *list) { swamigui_tree_set_selection_real (tree, list, 3); } /* the real selection set function, notify_flags 1 << 0 = "selection" and 1 << 1 = "selection-single" */ static void swamigui_tree_set_selection_real (SwamiguiTree *tree, IpatchList *list, int notify_flags) { GtkTreeModel *model; GtkTreeSelection *selection; GtkTreePath *path, *firstpath = NULL; GtkTreeView *seltree; GtkTreeIter treeiter, parent; gboolean new_sel_single; GObject *item; GList *p, *foundstore; int pos; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_return_if_fail (!list || IPATCH_IS_LIST (list)); if (!tree->stores) return; /* no point in selecting nothing, but.. */ /* list contains items? */ if (list && list->items) { /* first item shall suffice (all should be present and in same store) */ item = G_OBJECT (list->items->data); /* locate the store containing the first item */ for (p = tree->stores->items, pos = 0; p; p = p->next, pos++) if (swamigui_tree_store_item_get_node (SWAMIGUI_TREE_STORE (p->data), item, NULL)) break; foundstore = p; if (swami_log_if_fail (foundstore)) return; if (tree->selstore != p->data) /* selected store has changed? */ { tree->selstore = SWAMIGUI_TREE_STORE (p->data); tree->seltree = g_list_nth_data (tree->treeviews, pos); /* switch the page without signaling our switch page handler */ g_signal_handlers_block_by_func (tree, swamigui_tree_cb_switch_page, NULL); gtk_notebook_set_current_page (tree->notebook, pos); g_signal_handlers_unblock_by_func (tree, swamigui_tree_cb_switch_page, NULL); } } /* no items in new list */ else if (!tree->selstore) return; /* no selected store? - return */ if (tree->selection) g_object_unref (tree->selection); /* -- unref old selection */ if (list) tree->selection = ipatch_list_duplicate (list); else tree->selection = ipatch_list_new (); /* set the object's origin so swamigui_root can report this to others */ swami_object_set_origin (G_OBJECT (tree->selection), G_OBJECT (tree)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->seltree)); selection = gtk_tree_view_get_selection (tree->seltree); g_signal_handlers_block_by_func (selection, G_CALLBACK (tree_cb_selection_changed), tree); gtk_tree_selection_unselect_all (selection); seltree = GTK_TREE_VIEW (tree->seltree); /* update the tree view selection and expand all parents of items */ for (p = list ? list->items : NULL; p; p = p->next) { if (swamigui_tree_store_item_get_node (SWAMIGUI_TREE_STORE (model), p->data, &treeiter)) { if (!firstpath) /* ++ alloc path (for first valid item) */ firstpath = gtk_tree_model_get_path (model, &treeiter); /* expand all parents as necessary */ if (gtk_tree_model_iter_parent (model, &parent, &treeiter)) { path = gtk_tree_model_get_path (model, &parent); /* ++ alloc path */ gtk_tree_view_expand_to_path (seltree, path); gtk_tree_path_free (path); /* -- free path */ } /* select item in tree selection */ gtk_tree_selection_select_iter (selection, &treeiter); } } g_signal_handlers_unblock_by_func (selection, G_CALLBACK (tree_cb_selection_changed), tree); /* spotlight the first item in the selection */ if (firstpath) { gtk_tree_view_scroll_to_cell (seltree, firstpath, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (firstpath); /* -- free path */ } /* notify single selection change if old and new single are not NULL */ new_sel_single = list && list->items && !list->items->next; if ((notify_flags & 2) && (new_sel_single || tree->sel_single)) g_object_notify (G_OBJECT (tree), "selection-single"); tree->sel_single = new_sel_single; if (notify_flags & 1) g_object_notify (G_OBJECT (tree), "selection"); } /** * swamigui_tree_spotlight_item: * @tree: Swami tree object * @item: Object in @tree to spotlight * * Spotlights an item in a Swami tree object by recursively expanding all * nodes up the tree from item and moving the view to position item in the * center of the view and then selects it. If the item is part of an unselected * store (i.e., notebook tab), then it will become selected. */ void swamigui_tree_spotlight_item (SwamiguiTree *tree, GObject *item) { GtkTreeModel *model; GtkTreeSelection *sel; GtkTreeIter iter; GtkTreePath *path; GList *p, *foundstore; int pos; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_return_if_fail (G_IS_OBJECT (item)); /* locate the store containing the first item */ for (p = tree->stores->items, pos = 0; p; p = p->next, pos++) if (swamigui_tree_store_item_get_node (SWAMIGUI_TREE_STORE (p->data), item, NULL)) break; foundstore = p; if (swami_log_if_fail (foundstore)) return; /* store is not selected store? */ if ((SwamiguiTreeStore *)(p->data) != tree->selstore) { tree->selstore = SWAMIGUI_TREE_STORE (p->data); tree->seltree = g_list_nth_data (tree->treeviews, pos); /* switch the page without signaling our switch page handler */ g_signal_handlers_block_by_func (tree, swamigui_tree_cb_switch_page, NULL); gtk_notebook_set_current_page (tree->notebook, pos); g_signal_handlers_unblock_by_func (tree, swamigui_tree_cb_switch_page, NULL); } model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->seltree)); if (!swamigui_tree_store_item_get_node (SWAMIGUI_TREE_STORE (model), item, &iter)) return; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->seltree)); /* expand the nodes parents */ path = gtk_tree_model_get_path (model, &iter); if (gtk_tree_path_up (path)) gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree->seltree), path); gtk_tree_path_free (path); /* scroll to the given item */ path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree->seltree), path, NULL, TRUE, 0.5, 0.0); gtk_tree_path_free (path); /* select the item */ gtk_tree_selection_unselect_all (sel); gtk_tree_selection_select_iter (sel, &iter); } /** * swamigui_tree_search_set_start: * @tree: Tree widget * @start: Start object to begin search from (inclusive, %NULL for entire tree) * * Sets the beginning object in tree to start searching from, the passed * object is included in the search (can match). */ void swamigui_tree_search_set_start (SwamiguiTree *tree, GObject *start) { g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_return_if_fail (!start); /* we don't actually care if it is valid */ tree->search_start = start; } /** * swamigui_tree_search_set_text: * @tree: Tree widget * @text: Text to set search to * * Set the text of the tree's search entry and update search selection. */ void swamigui_tree_search_set_text (SwamiguiTree *tree, const char *text) { g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_free (tree->search_text); tree->search_text = g_strdup (text); swamigui_tree_real_search_next (tree, FALSE); } /** * swamigui_tree_search_set_visible: * @tree: Tree widget * @active: TRUE to show search entry, FALSE to hide it * * Shows/hides the search entry below the tree. */ void swamigui_tree_search_set_visible (SwamiguiTree *tree, gboolean visible) { g_return_if_fail (SWAMIGUI_IS_TREE (tree)); if (visible) gtk_widget_show (tree->search_box); else gtk_widget_hide (tree->search_box); } /** * swamigui_tree_search_next: * @tree: Tree widget * * Go to the next matching item for the current search. */ void swamigui_tree_search_next (SwamiguiTree *tree) { swamigui_tree_real_search_next (tree, TRUE); } static void swamigui_tree_real_search_next (SwamiguiTree *tree, gboolean usematch) { GtkTreeModel *model; GtkTreeIter iter; char *label; GObject *obj; int index; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); if (!tree->selstore || !tree->seltree) return; /* FIXME: silently fail? */ model = GTK_TREE_MODEL (tree->selstore); /* if search_match is set and valid, set start iter to the next node thereof */ if (usematch && tree->search_match && swamigui_tree_store_item_get_node (tree->selstore, tree->search_match, &iter)) tree_iter_recursive_next (model, &iter); else /* no search match item (or !usematch), try search start */ { if (!tree->search_start || !swamigui_tree_store_item_get_node (tree->selstore, tree->search_start, &iter)) { /* no search start item, try first item in tree */ if (!gtk_tree_model_get_iter_first (model, &iter)) return; /* empty tree? - return */ gtk_tree_model_get (model, &iter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref */ -1); tree->search_start = obj; g_object_unref (obj); /* -- unref */ } } /* iterate over tree looking for matching item */ do { gtk_tree_model_get (model, &iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, &label, /* ++ alloc */ SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref */ -1); /* search for sub string in row label */ index = str_index (label, tree->search_text); g_free (label); /* -- free */ if (index >= 0) /* matched? */ { set_search_match_item (tree, &iter, obj, index, tree->search_text); g_object_unref (obj); /* -- unref */ return; /* we done */ } g_object_unref (obj); /* -- unref */ } while (tree_iter_recursive_next (model, &iter)); reset_search_match_item (tree, NULL); /* no match, nothing selected */ } /** * swamigui_tree_search_next: * @tree: Tree widget * * Go to the previous matching item for the current search. */ void swamigui_tree_search_prev (SwamiguiTree *tree) { GtkTreeModel *model; GtkTreeIter iter, current; char *label; GObject *obj; int index; g_return_if_fail (SWAMIGUI_IS_TREE (tree)); if (!tree->selstore || !tree->seltree) return; /* FIXME: silently fail? */ model = GTK_TREE_MODEL (tree->selstore); /* if search_match is set and valid, set start iter to the prev node thereof */ if (tree->search_match && swamigui_tree_store_item_get_node (tree->selstore, tree->search_match, &iter)) { if (!tree_iter_recursive_prev (model, &iter)) return; /* FIXME - Wrap around? */ } else /* no search match item, try search start */ { if (!tree->search_start || !swamigui_tree_store_item_get_node (tree->selstore, tree->search_start, &iter)) { /* no search start, find last item of tree */ if (!gtk_tree_model_get_iter_first (model, &iter)) return; /* empty tree? - return */ /* find last child of last sibling of tree */ do { /* find last sibling at this level */ current = iter; while (gtk_tree_model_iter_next (model, ¤t)) iter = current; current = iter; } while (gtk_tree_model_iter_children (model, &iter, ¤t)); iter = current; gtk_tree_model_get (model, &iter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref */ -1); tree->search_start = obj; g_object_unref (obj); /* -- unref */ } } /* iterate over tree looking for matching item */ do { gtk_tree_model_get (model, &iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, &label, /* ++ alloc */ SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &obj, /* ++ ref */ -1); /* search for sub string in row label */ index = str_index (label, tree->search_text); g_free (label); /* -- free */ if (index >= 0) /* matched? */ { set_search_match_item (tree, &iter, obj, index, tree->search_text); g_object_unref (obj); /* -- unref */ return; /* we done */ } g_object_unref (obj); /* -- unref */ } while (tree_iter_recursive_prev (model, &iter)); reset_search_match_item (tree, NULL); /* no match, nothing selected */ } static void set_search_match_item (SwamiguiTree *tree, GtkTreeIter *iter, GObject *obj, int startpos, const char *search) { GtkTreeModel *store; GtkTreePath *path, *parent; GtkTreeIter myiter, piter; GList *new_ancestry = NULL; GObject *pobj; GList *p; if (!tree->selstore || !tree->seltree) return; store = GTK_TREE_MODEL (tree->selstore); if (!iter) /* if only the object was passed, lookup tree iter */ { iter = &myiter; if (!swamigui_tree_store_item_get_node (tree->selstore, obj, iter)) return; } path = gtk_tree_model_get_path (store, iter); /* ++ alloc */ parent = gtk_tree_path_copy (path); /* ++ alloc */ /* create ancestry list of new match item */ while (gtk_tree_path_up (parent) && gtk_tree_path_get_depth (parent) > 0) { gtk_tree_model_get_iter (store, &piter, parent); gtk_tree_model_get (store, &piter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &pobj, /* ++ ref */ -1); new_ancestry = g_list_prepend (new_ancestry, pobj); g_object_unref (pobj); /* -- unref */ } /* previous match and not the same? - reset it */ if (tree->search_match && tree->search_match != obj) reset_search_match_item (tree, &new_ancestry); /* set the search object and start/end text position */ tree->search_match = obj; tree->search_start_pos = startpos; tree->search_end_pos = startpos + strlen (search); /* Tell the model that the row display should be updated. * GtkTreeCellDataFunc will handle the highlight of the label. */ gtk_tree_model_row_changed (store, path, iter); /* loop on remaining ancestry items (that weren't already in search_expanded) */ for (p = new_ancestry; p; p = g_list_delete_link (p, p)) { /* probably won't fail, but.. */ if (swamigui_tree_store_item_get_node (tree->selstore, p->data, &piter)) { parent = gtk_tree_model_get_path (store, &piter); /* ++ alloc */ /* not expanded? - Add to search_expanded list */ if (!gtk_tree_view_row_expanded (tree->seltree, parent)) tree->search_expanded = g_list_prepend (tree->search_expanded, p->data); gtk_tree_path_free (parent); /* -- free */ } } /* expand all ancestry of the matched node */ parent = gtk_tree_path_copy (path); /* ++ alloc */ if (gtk_tree_path_up (parent)) gtk_tree_view_expand_to_path (tree->seltree, parent); gtk_tree_path_free (parent); /* -- free */ /* scroll if needed so the highlighted item is in view */ gtk_tree_view_scroll_to_cell (tree->seltree, path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); /* -- free */ } /* resets the current search match item. * new_ancestry is optional and specifies GObject ancestry of a new item which * will become selected. Nodes shared between the old search_expanded list * and the new_ancestry list are not collapsed, not removed from old list and * removed from new list. Nodes in old list which are not in new list are * collapsed and removed from list. Nodes in new list not in old list are * left alone. This allows for branches to remain open which will be part of * a new match item (therefore the scroll to the new item will only be done if * necessary). */ static void reset_search_match_item (SwamiguiTree *tree, GList **new_ancestry) { GtkTreeIter iter; GtkTreePath *path; gboolean retval; GList *p, *tmp, *match; GObject *obj; if (!tree->search_match || !tree->selstore || !tree->seltree) return; /* get the tree node for the current match item */ retval = swamigui_tree_store_item_get_node (tree->selstore, tree->search_match, &iter); tree->search_match = NULL; if (!retval) return; /* can happen if search match gets removed */ /* notify that item row has changed and should be updated (unhighlighted) */ path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree->selstore), &iter); gtk_tree_model_row_changed (GTK_TREE_MODEL (tree->selstore), path, &iter); gtk_tree_path_free (path); /* collapse any parents that were expanded and delete list */ for (p = tree->search_expanded; p; ) { obj = (GObject *)(p->data); if (new_ancestry) /* new ancestry list provided? */ { /* check if object is in new list also */ if ((match = g_list_find (*new_ancestry, obj))) { /* found in new list: remove from new list, leave in old list */ *new_ancestry = g_list_delete_link (*new_ancestry, match); p = p->next; continue; } } /* delete from old list */ tmp = p; p = p->next; tree->search_expanded = g_list_delete_link (tree->search_expanded, tmp); if (!swamigui_tree_store_item_get_node (tree->selstore, obj, &iter)) continue; path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree->selstore), &iter); gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree->seltree), path); gtk_tree_path_free (path); } } /* search for substring case insensitively and return the index of the match * or -1 on no match */ static int str_index (const char *haystack, const char *needle) { const char *s1, *s2; int i; if (!haystack || !needle) return -1; for (i = 0; *haystack; haystack++, i++) { for (s1 = haystack, s2 = needle; toupper (*s1) == toupper (*s2) && *s1 && *s2; s1++, s2++); if (!*s2) return (i); } return (-1); } /* For recursing forwards through tree one node at a time */ static gboolean tree_iter_recursive_next (GtkTreeModel *model, GtkTreeIter *iter) { GtkTreeIter current = *iter; GtkTreeIter parent; /* attempt to go to first child of current node */ if (gtk_tree_model_iter_children (model, iter, ¤t)) return (TRUE); /* attempt to go to next sibling of current node */ *iter = current; if (gtk_tree_model_iter_next (model, iter)) return (TRUE); /* attempt to go to next possible sibling of the closest parent */ while (TRUE) { if (!gtk_tree_model_iter_parent (model, &parent, ¤t)) return (FALSE); *iter = parent; if (gtk_tree_model_iter_next (model, iter)) return (TRUE); current = parent; } } /* For recursing backwards through tree one node at a time */ static gboolean tree_iter_recursive_prev (GtkTreeModel *model, GtkTreeIter *iter) { GtkTreeIter current; GtkTreePath *path; /* get a path, since it is easier to work with for some operations (such a prev) */ path = gtk_tree_model_get_path (model, iter); /* ++ alloc */ /* attempt to go to previous sibling */ if (gtk_tree_path_prev (path)) { gtk_tree_model_get_iter (model, iter, path); /* shouldn't fail */ gtk_tree_path_free (path); /* -- free */ /* attempt to recursively go to last child */ while (gtk_tree_model_iter_children (model, ¤t, iter)) { /* find last sibling */ *iter = current; while (gtk_tree_model_iter_next (model, ¤t)) *iter = current; } /* at this point, its either previous sibling with no children or * deepest last child of previous sibling */ return (TRUE); } gtk_tree_path_free (path); /* -- free */ /* attempt to go to parent */ current = *iter; if (gtk_tree_model_iter_parent (model, iter, ¤t)) return (TRUE); return (FALSE); /* that was the topmost node */ } swami-2.2.0/src/swamigui/SwamiguiTree.h000066400000000000000000000073721361104770400200410ustar00rootroot00000000000000/* * SwamiguiTree.h - Swami tabbed tree object header file * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_TREE_H__ #define __SWAMIGUI_TREE_H__ #include #include typedef struct _SwamiguiTree SwamiguiTree; typedef struct _SwamiguiTreeClass SwamiguiTreeClass; #define SWAMIGUI_TYPE_TREE (swamigui_tree_get_type ()) #define SWAMIGUI_TREE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_TREE, SwamiguiTree)) #define SWAMIGUI_TREE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_TREE, SwamiguiTreeClass)) #define SWAMIGUI_IS_TREE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_TREE)) #define SWAMIGUI_IS_TREE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_TREE)) /* Swami Tree Object (all fields private) */ struct _SwamiguiTree { GtkVBox parent_instance; /*< private >*/ GtkNotebook *notebook; /* notebook widget */ GtkWidget *search_box; /* the box containing the search widgets */ GtkEntry *search_entry; /* the search entry */ IpatchList *stores; /* list of SwamiguiTreeStore objects for each tab */ GList *treeviews; /* list of GtkTreeView widgets for each tab */ SwamiguiTreeStore *selstore; /* currently selected tree store (not ref'd) */ GtkTreeView *seltree; /* currently selected tree (not ref'd) */ IpatchList *selection; /* current selection of GObjects */ gboolean sel_single; /* TRUE if single item selected */ char *search_text; /* current search text */ GObject *search_start; /* search start item (item in tree, not ref'd) */ GObject *search_match; /* current matching search item (not ref'd) */ int search_start_pos; /* start char pos in search_match label */ int search_end_pos; /* start char pos in search_match label */ GList *search_expanded; /* branches which should be collapsed (items) */ }; /* Swami Tree Object class (all fields private) */ struct _SwamiguiTreeClass { GtkVBoxClass parent_class; }; GType swamigui_tree_get_type (void); GtkWidget *swamigui_tree_new (IpatchList *stores); void swamigui_tree_set_store_list (SwamiguiTree *tree, IpatchList *list); IpatchList *swamigui_tree_get_store_list (SwamiguiTree *tree); void swamigui_tree_set_selected_store (SwamiguiTree *tree, SwamiguiTreeStore *store); SwamiguiTreeStore *swamigui_tree_get_selected_store (SwamiguiTree *tree); GObject *swamigui_tree_get_selection_single (SwamiguiTree *tree); IpatchList *swamigui_tree_get_selection (SwamiguiTree *tree); void swamigui_tree_clear_selection (SwamiguiTree *tree); void swamigui_tree_set_selection (SwamiguiTree *tree, IpatchList *list); void swamigui_tree_spotlight_item (SwamiguiTree *tree, GObject *item); void swamigui_tree_search_set_start (SwamiguiTree *tree, GObject *start); void swamigui_tree_search_set_text (SwamiguiTree *tree, const char *text); void swamigui_tree_search_set_visible (SwamiguiTree *tree, gboolean visible); void swamigui_tree_search_next (SwamiguiTree *tree); void swamigui_tree_search_prev (SwamiguiTree *tree); #endif swami-2.2.0/src/swamigui/SwamiguiTreeStore.c000066400000000000000000000363551361104770400210540ustar00rootroot00000000000000/* * SwamiguiTreeStore.c - Swami item tree store object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiTree.h" #include "SwamiguiRoot.h" #include "icons.h" #include "i18n.h" /* Local Prototypes */ static void swamigui_tree_store_class_init (SwamiguiTreeStoreClass *klass); static void swamigui_tree_store_init (SwamiguiTreeStore *store); static void swamigui_tree_store_finalize (GObject *object); static inline void swamigui_tree_finish_insert (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *iter); static void tree_store_recursive_remove (SwamiguiTreeStore *store, GtkTreeIter *iter); static GObjectClass *parent_class = NULL; /* a cache of icon names (conserve memory, no need to store for every item) */ static GHashTable *icon_name_cache = NULL; GType swamigui_tree_store_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiTreeStoreClass), NULL, NULL, (GClassInitFunc) swamigui_tree_store_class_init, NULL, NULL, sizeof (SwamiguiTreeStore), 0, (GInstanceInitFunc) swamigui_tree_store_init, }; obj_type = g_type_register_static (GTK_TYPE_TREE_STORE, "SwamiguiTreeStore", &obj_info, G_TYPE_FLAG_ABSTRACT); } return (obj_type); } static void swamigui_tree_store_class_init (SwamiguiTreeStoreClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); obj_class->finalize = swamigui_tree_store_finalize; icon_name_cache = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)g_free, NULL); } static void swamigui_tree_store_init (SwamiguiTreeStore *store) { GType types[SWAMIGUI_TREE_STORE_NUM_COLUMNS] = { G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT }; /* we use pointer type for icon since its a static string and we don't want it getting duplicated */ /* set up the tree store */ gtk_tree_store_set_column_types (GTK_TREE_STORE (store), SWAMIGUI_TREE_STORE_NUM_COLUMNS, types); /* create a item->tree_node hash with a destroy notify on the item key to unref it, and a destroy on the tree iterator to free it */ store->item_hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)g_object_unref, (GDestroyNotify)gtk_tree_iter_free); } static void swamigui_tree_store_finalize (GObject *object) { SwamiguiTreeStore *store = SWAMIGUI_TREE_STORE (object); g_hash_table_destroy (store->item_hash); if (parent_class->finalize) parent_class->finalize (object); } /** * swamigui_tree_store_insert: * @store: Swami tree store to insert tree node into * @item: Object to link to tree node or %NULL * @label: Label for node or %NULL to try and obtain it other ways. * @icon: Stock ID of icon (%NULL to use "icon" type property or category icon) * @parent: Pointer to parent tree node or %NULL if inserting a toplevel node * @pos: Position to insert new node at (0 = prepend, > sibling list to append) * @out_iter: Pointer to a user supplied GtkTreeIter structure to store the * new node in or %NULL to ignore. * * Creates a new tree node and inserts it at the position given by * @parent and @pos, returning the created node in @out_iter (if * supplied). @item is an object to link with the created node. * It is also used if either @label or @icons is %NULL in which case * it obtains the information via other methods. */ void swamigui_tree_store_insert (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, int pos, GtkTreeIter *out_iter) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (!item || G_IS_OBJECT (item)); gtk_tree_store_insert (GTK_TREE_STORE (store), &iter, parent, pos); swamigui_tree_finish_insert (store, item, label, icon, &iter); if (out_iter) *out_iter = iter; } /** * swamigui_tree_store_insert_before: * @store: Swami tree store to insert tree node into * @item: Object to link to tree node or %NULL * @label: Label for node or %NULL to try and obtain it other ways. * @icon: Stock ID of icon (%NULL to use "icon" type property or category icon) * @parent: Pointer to parent tree node or %NULL if inserting a toplevel node * @sibling: A sibling node to insert the new node before or %NULL to append * @out_iter: Pointer to a user supplied GtkTreeIter structure to store the * new node in or %NULL to ignore. * * Like swamigui_tree_store_insert() but inserts the node before the * specified @sibling instead. */ void swamigui_tree_store_insert_before (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, GtkTreeIter *sibling, GtkTreeIter *out_iter) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (!item || G_IS_OBJECT (item)); gtk_tree_store_insert_before (GTK_TREE_STORE (store), &iter, parent, sibling); swamigui_tree_finish_insert (store, item, label, icon, &iter); if (out_iter) *out_iter = iter; } /** * swamigui_tree_store_insert_after: * @store: Swami tree store to insert tree node into * @item: Object to link to tree node or %NULL * @label: Label for node or %NULL to try and obtain it other ways. * @icon: Stock ID of icon (%NULL to use "icon" type property or category icon) * @parent: Pointer to parent tree node or %NULL if inserting a toplevel node * @sibling: A sibling node to insert the new node after or %NULL to prepend * @out_iter: Pointer to a user supplied GtkTreeIter structure to store the * new node in or %NULL to ignore. * * Like swamigui_tree_store_insert() but inserts the node after the * specified @sibling instead. */ void swamigui_tree_store_insert_after (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, GtkTreeIter *sibling, GtkTreeIter *out_iter) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (!item || G_IS_OBJECT (item)); gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, parent, sibling); swamigui_tree_finish_insert (store, item, label, icon, &iter); if (out_iter) *out_iter = iter; } static inline void swamigui_tree_finish_insert (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *iter) { char *item_label = NULL; char *item_icon = NULL; GtkTreeIter *copy; gint category; if (!label) { if (IPATCH_IS_ITEM (item)) g_object_get (item, "title", &item_label, NULL); else swami_object_get (item, "name", &item_label, NULL); if (!item_label) ipatch_type_object_get (item, "name", &item_label, NULL); if (!item_label) item_label = g_strdup (_("Untitled")); label = item_label; } if (!icon) { /* get item icon from type property */ ipatch_type_object_get (G_OBJECT (item), "icon", &item_icon, "category", &category, NULL); if (item_icon) { /* lookup icon name in hash (no need to store name for every icon) */ icon = g_hash_table_lookup (icon_name_cache, item_icon); if (!icon) { icon = g_strdup (item_icon); g_hash_table_insert (icon_name_cache, icon, icon); } g_free (item_icon); } /* if no type icon get category icon, gets default if category not set */ if (!icon) icon = swamigui_icon_get_category_icon (category); } gtk_tree_store_set (GTK_TREE_STORE (store), iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, label, SWAMIGUI_TREE_STORE_ICON_COLUMN, icon, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, item, -1); if (item) { copy = gtk_tree_iter_copy (iter); g_object_ref (item); /* ++ ref item for item_hash */ g_hash_table_insert (store->item_hash, item, copy); } if (item_label) g_free (item_label); } /** * swamigui_tree_store_change: * @store: Swami tree store * @item: Object in @store to update * @label: New label or %NULL to keep old * @icon: GdkPixbuf icon or %NULL to keep old * * Changes a row in a Swami tree store. */ void swamigui_tree_store_change (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); if (!swamigui_tree_store_item_get_node (store, item, &iter)) return; if (label) gtk_tree_store_set (GTK_TREE_STORE (store), &iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, label, -1); if (icon) gtk_tree_store_set (GTK_TREE_STORE (store), &iter, SWAMIGUI_TREE_STORE_ICON_COLUMN, icon, -1); } /** * swamigui_tree_store_remove: * @store: Swami tree store * @item: Object in @store to remove * * Removes a node (and all its children) from a Swami tree store. */ void swamigui_tree_store_remove (SwamiguiTreeStore *store, GObject *item) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); if (swamigui_tree_store_item_get_node (store, item, &iter)) { if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter)) tree_store_recursive_remove (store, &iter); else { // !! Remove item from hash before GtkTree to prevent callbacks thinking item still exists g_hash_table_remove (store->item_hash, item); gtk_tree_store_remove (GTK_TREE_STORE (store), &iter); } } } /* recursively remove tree nodes from a Swami tree store and remove the item->node links in item_hash table */ static void tree_store_recursive_remove (SwamiguiTreeStore *store, GtkTreeIter *iter) { GtkTreeIter children, remove; gboolean retval; register GObject *item; /* register to conserve recursive stack */ if (gtk_tree_model_iter_children ((GtkTreeModel *)store, &children, iter)) { do { remove = children; retval = gtk_tree_model_iter_next ((GtkTreeModel *)store, &children); tree_store_recursive_remove (store, &remove); } while (retval); } // !! Remove item from hash before GtkTree to prevent callbacks thinking item still exists item = swamigui_tree_store_node_get_item (store, iter); g_hash_table_remove (store->item_hash, item); gtk_tree_store_remove ((GtkTreeStore *)store, iter); } /** * swamigui_tree_store_move_before: * @store: Swami tree store * @item: Item to move * @position: Location to move item before or %NULL for last position * * Move an item from its current location to before @position. @position * must be at the same level as @item in the tree. */ void swamigui_tree_store_move_before (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *position) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); if (swamigui_tree_store_item_get_node (store, item, &iter)) gtk_tree_store_move_before (GTK_TREE_STORE (store), &iter, position); } /** * swamigui_tree_store_move_after: * @store: Swami tree store * @item: Item to move * @position: Location to move item after or %NULL for first position * * Move an item from its current location to after @position. @position * must be at the same level as @item in the tree. */ void swamigui_tree_store_move_after (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *position) { GtkTreeIter iter; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); if (swamigui_tree_store_item_get_node (store, item, &iter)) gtk_tree_store_move_after (GTK_TREE_STORE (store), &iter, position); } /** * swamigui_tree_store_item_get_node: * @store: Swami tree store * @item: Item that is in @store * @iter: Pointer to a GtkTreeIter structure to store the linked tree node * * Gets a tree node for @item in @store. The tree node is stored in @iter which * should be a pointer to a user supplied GtkTreeIter structure. * * Returns: %TRUE if @iter was set (@item has a linked node in @store), %FALSE * otherwise */ gboolean swamigui_tree_store_item_get_node (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *iter) { GtkTreeIter *lookup_iter; g_return_val_if_fail (SWAMIGUI_IS_TREE_STORE (store), FALSE); g_return_val_if_fail (G_IS_OBJECT (item), FALSE); lookup_iter = g_hash_table_lookup (store->item_hash, item); if (!lookup_iter) return (FALSE); if (iter) *iter = *lookup_iter; return (TRUE); } /** * swamigui_tree_store_node_get_item: * @store: Swami tree store * @iter: Node in @store to find linked item of * * Gets the linked item object for @iter node in @store. The returned item * is not referenced but can be safely used as long as the tree model isn't * changed (possibly causing item to be destroyed). The item should be ref'd * if used for extended periods. * * Returns: The item object linked with @iter node or %NULL if not found. * Item has NOT been referenced, see note above. */ GObject * swamigui_tree_store_node_get_item (SwamiguiTreeStore *store, GtkTreeIter *iter) { GObject *item; g_return_val_if_fail (SWAMIGUI_IS_TREE_STORE (store), NULL); g_return_val_if_fail (iter != NULL, NULL); gtk_tree_model_get (GTK_TREE_MODEL (store), iter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &item, // ++ ref item -1); if (item) g_object_unref (item); // -- unref item return (item); } /** * swamigui_tree_store_add: * @store: Swami tree store * @item: Item to add * * Add an item to a tree store using the item_add class method (specific to * tree store types). */ void swamigui_tree_store_add (SwamiguiTreeStore *store, GObject *item) { SwamiguiTreeStoreClass *klass; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); klass = SWAMIGUI_TREE_STORE_GET_CLASS (store); g_return_if_fail (klass->item_add != NULL); klass->item_add (store, item); } /** * swamigui_tree_store_changed: * @store: Swami tree store * @item: Item that changed * * This function updates the title and sorting of an item that changed using * the item_changed class method (specific to tree store types). * Note that re-ordering of the changed item may occur in * a delayed fashion, to prevent delays while user is typing a name for example. */ void swamigui_tree_store_changed (SwamiguiTreeStore *store, GObject *item) { SwamiguiTreeStoreClass *klass; g_return_if_fail (SWAMIGUI_IS_TREE_STORE (store)); g_return_if_fail (G_IS_OBJECT (item)); klass = SWAMIGUI_TREE_STORE_GET_CLASS (store); g_return_if_fail (klass->item_changed != NULL); klass->item_changed (store, item); } swami-2.2.0/src/swamigui/SwamiguiTreeStore.h000066400000000000000000000100221361104770400210400ustar00rootroot00000000000000/* * SwamiguiTreeStore.h - Swami item tree store object * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_TREE_STORE_H__ #define __SWAMIGUI_TREE_STORE_H__ typedef struct _SwamiguiTreeStore SwamiguiTreeStore; typedef struct _SwamiguiTreeStoreClass SwamiguiTreeStoreClass; #include #define SWAMIGUI_TYPE_TREE_STORE (swamigui_tree_store_get_type ()) #define SWAMIGUI_TREE_STORE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_TREE_STORE, \ SwamiguiTreeStore)) #define SWAMIGUI_TREE_STORE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_TREE_STORE, \ SwamiguiTreeStoreClass)) #define SWAMIGUI_IS_TREE_STORE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_TREE_STORE)) #define SWAMIGUI_IS_TREE_STORE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_TREE_STORE)) #define SWAMIGUI_TREE_STORE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), SWAMIGUI_TYPE_TREE_STORE, \ SwamiguiTreeStoreClass)) /* Swami GUI tree store object */ struct _SwamiguiTreeStore { GtkTreeStore parent_instance; /* derived from GtkTreeStore */ GHashTable *item_hash; /* hash of GObject -> GtkTreeIter* */ }; /* Swami GUI tree store class */ struct _SwamiguiTreeStoreClass { GtkTreeStoreClass parent_class; void (*item_add)(SwamiguiTreeStore *store, GObject *item); void (*item_changed)(SwamiguiTreeStore *store, GObject *item); }; /* GtkTreeStore columns */ enum { SWAMIGUI_TREE_STORE_LABEL_COLUMN, /* label column */ SWAMIGUI_TREE_STORE_ICON_COLUMN, /* pointer (static string) */ SWAMIGUI_TREE_STORE_OBJECT_COLUMN, /* pointer to patch item (invisible) */ SWAMIGUI_TREE_STORE_NUM_COLUMNS }; /* some developer targeted error messages */ #define SWAMIGUI_TREE_ERRMSG_PARENT_NOT_IN_TREE "Parent not in tree store" #define SWAMIGUI_TREE_ERRMSG_ITEM_NOT_IN_TREE "Item not in tree store" GType swamigui_tree_store_get_type (void); void swamigui_tree_store_insert (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, int pos, GtkTreeIter *out_iter); void swamigui_tree_store_insert_before (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, GtkTreeIter *sibling, GtkTreeIter *out_iter); void swamigui_tree_store_insert_after (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon, GtkTreeIter *parent, GtkTreeIter *sibling, GtkTreeIter *out_iter); void swamigui_tree_store_change (SwamiguiTreeStore *store, GObject *item, const char *label, char *icon); void swamigui_tree_store_remove (SwamiguiTreeStore *store, GObject *item); void swamigui_tree_store_move_before (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *position); void swamigui_tree_store_move_after (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *position); gboolean swamigui_tree_store_item_get_node (SwamiguiTreeStore *store, GObject *item, GtkTreeIter *iter); GObject *swamigui_tree_store_node_get_item (SwamiguiTreeStore *store, GtkTreeIter *iter); void swamigui_tree_store_add (SwamiguiTreeStore *store, GObject *item); void swamigui_tree_store_changed (SwamiguiTreeStore *store, GObject *item); #endif swami-2.2.0/src/swamigui/SwamiguiTreeStoreConfig.c000066400000000000000000000052771361104770400222010ustar00rootroot00000000000000/* * SwamiguiTreeStoreConfig.c - Config tree store (for instruments). * * Swami * Copyright (C) 1999-2012 Joshua "Element" Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiTreeStoreConfig.h" #include "i18n.h" static void swamigui_tree_store_config_item_add (SwamiguiTreeStore *store, GObject *item); static void swamigui_tree_store_config_item_changed (SwamiguiTreeStore *store, GObject *item); G_DEFINE_TYPE (SwamiguiTreeStoreConfig, swamigui_tree_store_config, SWAMIGUI_TYPE_TREE_STORE); static void swamigui_tree_store_config_class_init (SwamiguiTreeStoreConfigClass *klass) { SwamiguiTreeStoreClass *store_class = SWAMIGUI_TREE_STORE_CLASS (klass); store_class->item_add = swamigui_tree_store_config_item_add; store_class->item_changed = swamigui_tree_store_config_item_changed; } static void swamigui_tree_store_config_init (SwamiguiTreeStoreConfig *store) { } /** * swamigui_tree_store_config_new: * * Create a new config tree store for preferences and configuration. * * Returns: New config tree store object with a ref count of 1. */ SwamiguiTreeStore * swamigui_tree_store_config_new (void) { return (SWAMIGUI_TREE_STORE (g_object_new (SWAMIGUI_TYPE_TREE_STORE_CONFIG, NULL))); } static void swamigui_tree_store_config_item_add (SwamiguiTreeStore *store, GObject *item) { char *name; g_return_if_fail (SWAMIGUI_IS_TREE_STORE_CONFIG (store)); g_return_if_fail (G_IS_OBJECT (item)); swami_object_get (item, "name", &name, NULL); if (!name) name = g_strdup (_("Untitled")); swamigui_tree_store_insert_before (store, item, name, NULL, NULL, NULL, NULL); g_free (name); } static void swamigui_tree_store_config_item_changed (SwamiguiTreeStore *store, GObject *item) { char *title; /* get title of item */ g_object_get (item, "title", &title, NULL); g_return_if_fail (title != NULL); swamigui_tree_store_change (store, item, title, NULL); g_free (title); } swami-2.2.0/src/swamigui/SwamiguiTreeStoreConfig.h000066400000000000000000000042341361104770400221760ustar00rootroot00000000000000/* * SwamiguiTreeStoreConfig.h - Config tree store (Drivers/Layouts/Plugins/etc) * * Swami * Copyright (C) 1999-2012 Joshua "Element" Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_TREE_STORE_CONFIG_H__ #define __SWAMIGUI_TREE_STORE_CONFIG_H__ typedef struct _SwamiguiTreeStoreConfig SwamiguiTreeStoreConfig; typedef struct _SwamiguiTreeStoreConfigClass SwamiguiTreeStoreConfigClass; #include #include #define SWAMIGUI_TYPE_TREE_STORE_CONFIG (swamigui_tree_store_config_get_type ()) #define SWAMIGUI_TREE_STORE_CONFIG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_TREE_STORE_CONFIG, \ SwamiguiTreeStoreConfig)) #define SWAMIGUI_TREE_STORE_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_TREE_STORE_CONFIG, \ SwamiguiTreeStoreConfigClass)) #define SWAMIGUI_IS_TREE_STORE_CONFIG(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_TREE_STORE_CONFIG)) #define SWAMIGUI_IS_TREE_STORE_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_TREE_STORE_CONFIG)) /* Config tree store object */ struct _SwamiguiTreeStoreConfig { SwamiguiTreeStore parent_instance; /* derived from SwamiguiTreeStore */ }; /* Config tree store class */ struct _SwamiguiTreeStoreConfigClass { SwamiguiTreeStoreClass parent_class; }; GType swamigui_tree_store_config_get_type (void); SwamiguiTreeStore *swamigui_tree_store_config_new (void); #endif swami-2.2.0/src/swamigui/SwamiguiTreeStorePatch.c000066400000000000000000000502771361104770400220330ustar00rootroot00000000000000/* * SwamiguiTreeStorePatch.c - Patch tree store (for instruments). * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "SwamiguiTreeStorePatch.h" #include "SwamiguiRoot.h" #include /* used by swamigui_tree_store_real_item_add to qsort child items by title */ typedef struct { GObject *tree_parent; /* tree parent object (primary sort field) */ char *title; /* text title (secondary sort field, for sorted types) */ GObject *item; } ChildSortBag; /* Local Prototypes */ static void swamigui_tree_store_patch_class_init (SwamiguiTreeStorePatchClass *klass); static IpatchItem *swamigui_tree_store_create_virtual_child (IpatchItem *container, GType virtual_child_type); static IpatchItem *swamigui_tree_store_lookup_virtual_child (IpatchItem *container, GType virtual_child_type); static void swamigui_tree_store_patch_real_item_add ( SwamiguiTreeStore *store, GObject *item, GtkTreeIter *sibling, GtkTreeIter *out_iter, GtkTreeIter *in_parent_iter, ChildSortBag *inbag); static int title_sort_compar_func (const void *a, const void *b); static gboolean get_item_sort_info (SwamiguiTreeStore *store, GObject *item, GType item_type, GObject *parent, GObject **out_tree_parent, GtkTreeIter *out_parent_iter); static gboolean find_sibling_title_sort (SwamiguiTreeStore *store, GObject *item, char *title, GtkTreeIter *parent_iter, GtkTreeIter *sibling_iter); static gboolean find_sibling_container_sort (SwamiguiTreeStore *store, GObject *item, GObject *parent, GtkTreeIter *parent_iter, GtkTreeIter *sibling_iter); GType swamigui_tree_store_patch_get_type (void) { static GType obj_type = 0; if (!obj_type) { static const GTypeInfo obj_info = { sizeof (SwamiguiTreeStorePatchClass), NULL, NULL, (GClassInitFunc) swamigui_tree_store_patch_class_init, NULL, NULL, sizeof (SwamiguiTreeStorePatch), 0, (GInstanceInitFunc) NULL, }; obj_type = g_type_register_static (SWAMIGUI_TYPE_TREE_STORE, "SwamiguiTreeStorePatch", &obj_info, 0); } return (obj_type); } static void swamigui_tree_store_patch_class_init (SwamiguiTreeStorePatchClass *klass) { SwamiguiTreeStoreClass *store_class = SWAMIGUI_TREE_STORE_CLASS (klass); store_class->item_add = swamigui_tree_store_patch_item_add; store_class->item_changed = swamigui_tree_store_patch_item_changed; } /** * swamigui_tree_store_patch_new: * * Create a new patch tree store for instruments. * * Returns: New patch tree store object with a ref count of 1. */ SwamiguiTreeStorePatch * swamigui_tree_store_patch_new (void) { return (SWAMIGUI_TREE_STORE_PATCH (g_object_new (SWAMIGUI_TYPE_TREE_STORE_PATCH, NULL))); } /* * swamigui_tree_store_create_virtual_child: * @container: Item to create virtual container child instance in * @virtual_child_type: The virtual child type * * Create a virtual container instance in a container. If @container already * has the specified @virtual_child_type it is simply returned. * * Returns: Virtual container instance */ static IpatchItem * swamigui_tree_store_create_virtual_child (IpatchItem *container, GType virtual_child_type) { IpatchItem *virt_container; gpointer data; const char *keyname; g_return_val_if_fail (IPATCH_IS_ITEM (container), NULL); g_return_val_if_fail (g_type_is_a (virtual_child_type, IPATCH_TYPE_VIRTUAL_CONTAINER), NULL); /* use the virtual type name as the GObject data key */ keyname = g_type_name (virtual_child_type); data = g_object_get_data (G_OBJECT (container), keyname); if (data) return (IPATCH_ITEM (data)); virt_container = g_object_new (virtual_child_type, NULL); /* ++ ref */ ipatch_item_set_parent (IPATCH_ITEM (virt_container),IPATCH_ITEM (container)); g_object_set_data (G_OBJECT (container), keyname, virt_container); /* !! Reference is held until @container is removed from tree */ return (virt_container); } /* * swamigui_tree_store_lookup_virtual_child: * @container: Container item to look up a virtual child instance in * @virtual_child_type: The virtual child type * * Lookup a virtual container child instance in a container. * * Returns: Virtual container instance or %NULL if no virtual child of * requested type. */ static IpatchItem * swamigui_tree_store_lookup_virtual_child (IpatchItem *container, GType virtual_child_type) { gpointer data; const char *keyname; g_return_val_if_fail (IPATCH_IS_ITEM (container), NULL); g_return_val_if_fail (g_type_is_a (virtual_child_type, IPATCH_TYPE_VIRTUAL_CONTAINER), NULL); /* use the virtual type name as the GObject data key */ keyname = g_type_name (virtual_child_type); data = g_object_get_data (G_OBJECT (container), keyname); if (data) return (IPATCH_ITEM (data)); else return (NULL); } /** * swamigui_tree_store_patch_item_add: * @store: Patch tree store * @item: Item to add * * Function used as item_add method of #SwamiguiTreeStorePatchClass. * Might be useful to other tree store types. */ void swamigui_tree_store_patch_item_add (SwamiguiTreeStore *store, GObject *item) { swamigui_tree_store_patch_real_item_add (store, item, NULL, NULL, NULL, NULL); } /* some tricks are done to speed up adding a container item (children are pre-sorted to decrease exponential list iterations). Yeah looks pretty ugly, but in theory it should provide a bit of a speedup. */ static void swamigui_tree_store_patch_real_item_add ( SwamiguiTreeStore *store, GObject *item, GtkTreeIter *sibling, GtkTreeIter *out_iter, GtkTreeIter *in_parent_iter, ChildSortBag *inbag) { GtkTreeIter *parent_iter = NULL, real_parent_iter; GtkTreeIter sibling_iter, item_iter, *pitem_iter, tmp_iter; GObject *parent, *childitem; GObject *tree_parent; IpatchList *list; ChildSortBag bag, *bagp; char *alloc_title = NULL, *title; gboolean sort = FALSE; GArray *title_sort_array = NULL; GHashTable *prev_child_hash = NULL; GList *p; guint i; /* ++ ref parent */ parent = (GObject *)ipatch_item_get_parent (IPATCH_ITEM (item)); g_return_if_fail (parent != NULL); if (inbag) /* recursive call? - Use already fetched values. */ { title = inbag->title; tree_parent = inbag->tree_parent; parent_iter = in_parent_iter; } else /* not a recursive call, no pre-cached values */ { gboolean sibling_set; /* get title of item (if not supplied to function) */ g_object_get (item, "title", &title, NULL); if (swami_log_if_fail (title != NULL)) goto ret; alloc_title = title; /* get sort boolean, tree parent object and node */ sort = get_item_sort_info (store, item, 0, parent, &tree_parent, &real_parent_iter); if (tree_parent) parent_iter = &real_parent_iter; if (sort) sibling_set = find_sibling_title_sort (store, item, title, parent_iter, &sibling_iter); else sibling_set = find_sibling_container_sort (store, item, parent, parent_iter, &sibling_iter); if (sibling_set) sibling = &sibling_iter; } /* else - !inbag */ /* insert the node into the tree */ swamigui_tree_store_insert_after (store, item, title, NULL, parent_iter, sibling, &item_iter); if (out_iter) *out_iter = item_iter; /* is added item a container? */ if (IPATCH_IS_CONTAINER (item)) { const GType *types; types = ipatch_container_get_virtual_types (IPATCH_CONTAINER (item)); if (types) /* any virtual container child types? */ { IpatchItem *virt; for (; *types; types++) /* create virtual containers */ { virt = swamigui_tree_store_create_virtual_child (IPATCH_ITEM (item), *types); swamigui_tree_store_insert_before (store, G_OBJECT (virt), NULL, NULL, &item_iter, NULL, NULL); } } types = ipatch_container_get_child_types (IPATCH_CONTAINER (item)); if (!types) goto ret; /* should always be child types, but.. */ title_sort_array = g_array_new (FALSE, FALSE, sizeof (ChildSortBag)); prev_child_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free); for (; *types; types++) /* loop over container child types */ { gboolean static_parent; list = ipatch_container_get_children (IPATCH_CONTAINER (item), *types); /* ++ ref list */ parent_iter = NULL; /* virtual parent type is static? */ if (!ipatch_type_get_dynamic_func (*types, "virtual-parent-type")) { sort = get_item_sort_info (store, NULL, *types, item, &tree_parent, &real_parent_iter); if (tree_parent) parent_iter = &real_parent_iter; static_parent = TRUE; } else static_parent = FALSE; sibling = NULL; /* loop over child items of the current type */ for (p = list->items; p; p = p->next) { childitem = (GObject *)(p->data); /* dynamic virtual parent type? */ if (!static_parent) { sort = get_item_sort_info (store, childitem, 0, item, &tree_parent, &real_parent_iter); if (tree_parent) parent_iter = &real_parent_iter; } /* store item's values in the bag structure, may or may not get added to the title sort array, depending on if child is title sorted */ bag.tree_parent = tree_parent; g_object_get (childitem, "title", &title, NULL); if (swami_log_if_fail (title != NULL)) { g_object_unref (list); /* -- unref list */ goto ret; } bag.title = title; bag.item = childitem; if (sort) /* item should be sorted by title? */ { g_array_append_val (title_sort_array, bag); } else /* not title sorted, just add in order */ { /* keep track of the last child for each parent */ pitem_iter = g_hash_table_lookup (prev_child_hash, tree_parent); sibling = pitem_iter; /* sibling set if previous item */ if (!pitem_iter) { pitem_iter = g_new (GtkTreeIter, 1); g_hash_table_insert (prev_child_hash, tree_parent, pitem_iter); } swamigui_tree_store_patch_real_item_add (store, G_OBJECT (p->data), sibling, &tmp_iter, parent_iter, &bag); *pitem_iter = tmp_iter; g_free (bag.title); } } /* for .. p in list */ g_object_unref (list); /* -- unref list */ if (title_sort_array->len > 0) { GObject *prev_parent = NULL; /* sort the items by parent object and title */ qsort (title_sort_array->data, title_sort_array->len, sizeof (ChildSortBag), title_sort_compar_func); /* loop over sorted items */ for (i = 0; i < title_sort_array->len; i++) { bagp = &g_array_index (title_sort_array, ChildSortBag, i); /* dynamic virtual parent type? */ if (!static_parent) { sort = get_item_sort_info (store, G_OBJECT (bagp->item), 0, item, &tree_parent, &real_parent_iter); if (tree_parent) parent_iter = &real_parent_iter; } /* previous and current item are in different containers? */ if (prev_parent != tree_parent) sibling = NULL; else sibling = &item_iter; swamigui_tree_store_patch_real_item_add (store, G_OBJECT (bagp->item), sibling, &tmp_iter, parent_iter, bagp); item_iter = tmp_iter; prev_parent = tree_parent; g_free (bagp->title); } g_array_set_size (title_sort_array, 0); /* clear array */ } /* if (title_sort_array->len > 0) */ } /* for (*types) - container child types */ } /* if (IPATCH_IS_CONTAINER (item)) */ ret: g_object_unref (parent); /* -- unref parent */ if (alloc_title) g_free (alloc_title); if (title_sort_array) g_array_free (title_sort_array, TRUE); if (prev_child_hash) g_hash_table_destroy (prev_child_hash); } static int title_sort_compar_func (const void *a, const void *b) { ChildSortBag *baga = (ChildSortBag *)a, *bagb = (ChildSortBag *)b; if (baga->tree_parent == bagb->tree_parent) return (strcmp (baga->title, bagb->title)); /* parent object is the primary sort */ if (baga->tree_parent < bagb->tree_parent) return (1); else return (-1); } /* a helper function to get sort-children property that applies to an @item (can be NULL if item_type is set instead) and can also get the tree parent object and iterator if pointers are supplied. Returns: TRUE if item should be title sorted, FALSE otherwise */ static gboolean get_item_sort_info (SwamiguiTreeStore *store, GObject *item, GType item_type, GObject *parent, GObject **out_tree_parent, GtkTreeIter *out_parent_iter) { GObject *tree_parent; gboolean sort, has_parent_iter; GType type; if (item) ipatch_type_object_get (item, "virtual-parent-type", &type, NULL); else ipatch_type_get (item_type, "virtual-parent-type", &type, NULL); if (out_tree_parent || out_parent_iter) { /* item has a virtual parent type? Get the instance of it. */ if (type != G_TYPE_NONE) tree_parent = (GObject *)swamigui_tree_store_lookup_virtual_child (IPATCH_ITEM (parent), type); else if (SWAMI_IS_CONTAINER (parent)) /* NULL parent to append to root */ tree_parent = NULL; else tree_parent = parent; /* otherwise use real parent */ if (tree_parent) { /* get the tree node for the tree parent item */ has_parent_iter = swamigui_tree_store_item_get_node (store, G_OBJECT (tree_parent), out_parent_iter); if (swami_log_if_fail (has_parent_iter)) return (FALSE); } *out_tree_parent = tree_parent; } /* use real parent type if no virtual type */ if (type == G_TYPE_NONE && item) type = G_OBJECT_TYPE (item); /* children should be sorted? */ if (type != G_TYPE_NONE) ipatch_type_get (type, "sort-children", &sort, NULL); else sort = FALSE; return (sort); } /* find the closest sibling node already in tree store to insert after, sorted * by title. * Returns: TRUE if sibling_iter is set, FALSE if item should be first. */ static gboolean find_sibling_title_sort (SwamiguiTreeStore *store, GObject *item, char *title, GtkTreeIter *parent_iter, GtkTreeIter *sibling_iter) { GtkTreeModel *model = GTK_TREE_MODEL (store); gboolean haveprev = FALSE; GtkTreeIter child; GObject *cmpitem; char *curtitle; /* parent has any children? */ if (!gtk_tree_model_iter_children (model, &child, parent_iter)) return (FALSE); do /* search for sibling to insert before */ { gtk_tree_model_get (model, &child, SWAMIGUI_TREE_STORE_LABEL_COLUMN, &curtitle, /* ++ alloc */ SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &cmpitem, /* ++ ref */ -1); g_object_unref (cmpitem); /* -- unref, we only need the pointer to compare */ /* compare titles and make sure its not the same item (if called to re-sort item) */ if (strcmp (title, curtitle) <= 0 && item != cmpitem) { g_free (curtitle); break; } g_free (curtitle); *sibling_iter = child; haveprev = TRUE; } while (gtk_tree_model_iter_next (model, &child)); return (haveprev == TRUE); } /* find the closest sibling node already in tree store to insert after, * sorted as found in container child list. * Returns: TRUE if sibling_iter is set, FALSE if item should be first. */ static gboolean find_sibling_container_sort (SwamiguiTreeStore *store, GObject *item, GObject *parent, GtkTreeIter *parent_iter, GtkTreeIter *sibling_iter) { GtkTreeModel *model = GTK_TREE_MODEL (store); GtkTreeIter tmpiter; GtkTreePath *parent_path = NULL, *sibparent_path; IpatchList *list; gboolean parent_match; const GType *childtypes; GType type; GList *p; /* Find matching child type which item is a decendent of - or use item's type */ childtypes = ipatch_container_get_child_types (IPATCH_CONTAINER (parent)); type = G_OBJECT_TYPE (item); for (; *childtypes; childtypes++) { if (g_type_is_a (type, *childtypes)) { type = *childtypes; break; } } /* ++ ref list */ list = ipatch_container_get_children (IPATCH_CONTAINER (parent), type); p = g_list_find (list->items, item); /* find item in list */ if (p) { /* search for previous sibling already in tree */ for (p = p->prev; p; p = p->prev) { /* item in tree? */ if (swamigui_tree_store_item_get_node (store, p->data, sibling_iter)) { /* Make sure its the same parent (no parent if child of root) */ if (gtk_tree_model_iter_parent (model, &tmpiter, sibling_iter)) { if (!parent_path) /* get path of parent iterator in tree store */ parent_path = gtk_tree_model_get_path (model, parent_iter); sibparent_path = gtk_tree_model_get_path (model, &tmpiter); /* is item really a sibling (same parent node?) */ parent_match = gtk_tree_path_compare (parent_path, sibparent_path) == 0; gtk_tree_path_free (sibparent_path); if (parent_match) break; } else break; /* no parent in tree (child of root) */ } } } if (parent_path) gtk_tree_path_free (parent_path); g_object_unref (list); /* -- unref list */ return (p != NULL); } /** * swamigui_tree_store_patch_item_changed: * @store: Patch tree store * @item: Item that changed * * Function used as item_changed method of #SwamiguiTreeStorePatchClass. * Might be useful to other tree store types. */ void swamigui_tree_store_patch_item_changed (SwamiguiTreeStore *store, GObject *item) { GtkTreeIter item_iter, parent_iter, curparent_iter, sibling_iter; gboolean found_item_node; gboolean found_parent_node; GObject *parent = NULL, *curparent = NULL, *tree_parent; char *title, *curtitle = NULL; /* get title of item */ g_object_get (item, "title", &title, NULL); /* ++ alloc */ if (!title) title = g_strdup (""); found_item_node = swamigui_tree_store_item_get_node (store, item, &item_iter); if (swami_log_if_fail (found_item_node)) goto ret; gtk_tree_model_get (GTK_TREE_MODEL (store), &item_iter, SWAMIGUI_TREE_STORE_LABEL_COLUMN, &curtitle, /* ++ alloc */ -1); if (strcmp (title, curtitle) == 0) goto ret; /* No change in title? - Return */ swamigui_tree_store_change (store, item, title, NULL); parent = (GObject *)ipatch_item_get_parent (IPATCH_ITEM (item)); /* ++ ref */ if (swami_log_if_fail (parent != NULL)) goto ret; /* Is item under a sorted parent? */ if (get_item_sort_info (store, item, 0, parent, &tree_parent, &parent_iter)) { /* Check if parent node has changed (switched from melodic to percussion for example) */ found_parent_node = gtk_tree_model_iter_parent (GTK_TREE_MODEL (store), &curparent_iter, &item_iter); if (swami_log_if_fail (found_parent_node)) goto ret; gtk_tree_model_get (GTK_TREE_MODEL (store), &curparent_iter, SWAMIGUI_TREE_STORE_OBJECT_COLUMN, &curparent, /* ++ ref */ -1); /* If the parent is the same, we can use the tree move functions */ if (curparent == tree_parent) { if (find_sibling_title_sort (store, item, title, &parent_iter, &sibling_iter)) swamigui_tree_store_move_after (store, item, &sibling_iter); else swamigui_tree_store_move_after (store, item, NULL); } else /* Parent changed, remove and add it back (yeah, PITA!) */ { swamigui_tree_store_remove (store, item); swamigui_tree_store_patch_item_add (store, item); } } ret: g_free (title); /* -- free title */ g_free (curtitle); /* -- free curtitle */ if (parent) g_object_unref (parent); /* -- unref parent */ if (curparent) g_object_unref (curparent); /* -- unref curparent */ } swami-2.2.0/src/swamigui/SwamiguiTreeStorePatch.h000066400000000000000000000044071361104770400220320ustar00rootroot00000000000000/* * SwamiguiTreeStorePatch.h - Patch tree store (for instruments). * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_TREE_STORE_PATCH_H__ #define __SWAMIGUI_TREE_STORE_PATCH_H__ typedef struct _SwamiguiTreeStorePatch SwamiguiTreeStorePatch; typedef struct _SwamiguiTreeStorePatchClass SwamiguiTreeStorePatchClass; #include #define SWAMIGUI_TYPE_TREE_STORE_PATCH (swamigui_tree_store_patch_get_type ()) #define SWAMIGUI_TREE_STORE_PATCH(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMIGUI_TYPE_TREE_STORE_PATCH, \ SwamiguiTreeStorePatch)) #define SWAMIGUI_TREE_STORE_PATCH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMIGUI_TYPE_TREE_STORE_PATCH, \ SwamiguiTreeStorePatchClass)) #define SWAMIGUI_IS_TREE_STORE_PATCH(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMIGUI_TYPE_TREE_STORE_PATCH)) #define SWAMIGUI_IS_TREE_STORE_PATCH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), SWAMIGUI_TYPE_TREE_STORE_PATCH)) /* Patch tree store object */ struct _SwamiguiTreeStorePatch { SwamiguiTreeStore parent_instance; /* derived from SwamiguiTreeStore */ }; /* Patch tree store class */ struct _SwamiguiTreeStorePatchClass { SwamiguiTreeStoreClass parent_class; }; GType swamigui_tree_store_patch_get_type (void); SwamiguiTreeStorePatch *swamigui_tree_store_patch_new (void); void swamigui_tree_store_patch_item_add (SwamiguiTreeStore *store, GObject *item); void swamigui_tree_store_patch_item_changed (SwamiguiTreeStore *store, GObject *item); #endif swami-2.2.0/src/swamigui/builtin_enums.c000066400000000000000000000310431361104770400202740ustar00rootroot00000000000000/* builtin_enums.c This file contains functions registration of type enumeration (or flags) defined in the respectives headers files. Typically this file could be generated automatically at make time (with the help of glib-mkenums). Compiling on Windows: glib-mkenums is a perl script and Perl isn't natively installed. To avoid Perl dependency, the file should manually updated. This shouldn't be a problem when new enumerations are slowly added over time. Please respect the naming conventions. This example assumes an enum definition in a header file: typedef enum { SWAMIGUI_BAR_PTR_POSITION, SWAMIGUI_BAR_PTR_RANGE } SwamiguiBarPtrType; The name of registration function should be: xxx_get_type, with xxx the enum name (e.g word-separated by underscores. (e.g swamigui_bar_ptr_type_get_type When the enum value definitions contain bit-shift operators, this function must call g_flags_register_static()otherwise g_enum_register_static() must be called. First parameter of g_flags_register_static or g_enum_register_static(type name) must be the enum name (e.g "SwamiguiBarPtrType"). Second parameter of of g_flags_register_static() must be GFlagsValue table value. Second parameter of of g_enum_register_static() must be GFEnumValue table value. Each value (GFlagsValue or GFEnumValue) must be {ENUM_VALUE, "VALUE_NAME", "valuenick"}: - ENUM_VALUE, the integer value for the enum value.(e.g SWAMIGUI_BAR_PTR_POSITION). - VALUE_NAME, name with words uppercase and word-separated by underscores (e.g "SWAMIGUI_BAR_PTR_POSITION"). - valuenick, this is usually stripping common prefix words of all the enum values. the words are lowercase and underscores are substituted by a minus (e.g. "position"). */ #include /* enumerations from "SwamiguiBarPtr.h" */ GType swamigui_bar_ptr_type_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_BAR_PTR_POSITION, "SWAMIGUI_BAR_PTR_POSITION", "position" }, { SWAMIGUI_BAR_PTR_RANGE, "SWAMIGUI_BAR_PTR_RANGE", "range" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiBarPtrType", values); } return etype; } /* enumerations from "SwamiguiCanvasMod.h" */ GType swamigui_canvas_mod_type_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_CANVAS_MOD_SNAP_ZOOM, "SWAMIGUI_CANVAS_MOD_SNAP_ZOOM", "snap-zoom" }, { SWAMIGUI_CANVAS_MOD_WHEEL_ZOOM, "SWAMIGUI_CANVAS_MOD_WHEEL_ZOOM", "wheel-zoom" }, { SWAMIGUI_CANVAS_MOD_SNAP_SCROLL, "SWAMIGUI_CANVAS_MOD_SNAP_SCROLL", "snap-scroll" }, { SWAMIGUI_CANVAS_MOD_WHEEL_SCROLL, "SWAMIGUI_CANVAS_MOD_WHEEL_SCROLL", "wheel-scroll" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiCanvasModType", values); } return etype; } GType swamigui_canvas_mod_axis_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_CANVAS_MOD_X, "SWAMIGUI_CANVAS_MOD_X", "x" }, { SWAMIGUI_CANVAS_MOD_Y, "SWAMIGUI_CANVAS_MOD_Y", "y" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiCanvasModAxis", values); } return etype; } GType swamigui_canvas_mod_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_CANVAS_MOD_ENABLED, "SWAMIGUI_CANVAS_MOD_ENABLED", "enabled" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiCanvasModFlags", values); } return etype; } GType swamigui_canvas_mod_actions_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_CANVAS_MOD_ZOOM_X, "SWAMIGUI_CANVAS_MOD_ZOOM_X", "zoom-x" }, { SWAMIGUI_CANVAS_MOD_ZOOM_Y, "SWAMIGUI_CANVAS_MOD_ZOOM_Y", "zoom-y" }, { SWAMIGUI_CANVAS_MOD_SCROLL_X, "SWAMIGUI_CANVAS_MOD_SCROLL_X", "scroll-x" }, { SWAMIGUI_CANVAS_MOD_SCROLL_Y, "SWAMIGUI_CANVAS_MOD_SCROLL_Y", "scroll-y" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiCanvasModActions", values); } return etype; } /* enumerations from "SwamiguiControl.h" */ GType swamigui_control_rank_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_CONTROL_RANK_LOWEST, "SWAMIGUI_CONTROL_RANK_LOWEST", "lowest" }, { SWAMIGUI_CONTROL_RANK_LOW, "SWAMIGUI_CONTROL_RANK_LOW", "low" }, { SWAMIGUI_CONTROL_RANK_NORMAL, "SWAMIGUI_CONTROL_RANK_NORMAL", "normal" }, { SWAMIGUI_CONTROL_RANK_HIGH, "SWAMIGUI_CONTROL_RANK_HIGH", "high" }, { SWAMIGUI_CONTROL_RANK_HIGHEST, "SWAMIGUI_CONTROL_RANK_HIGHEST", "highest" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiControlRank", values); } return etype; } GType swamigui_control_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_CONTROL_CTRL, "SWAMIGUI_CONTROL_CTRL", "ctrl" }, { SWAMIGUI_CONTROL_VIEW, "SWAMIGUI_CONTROL_VIEW", "view" }, { SWAMIGUI_CONTROL_NO_CREATE, "SWAMIGUI_CONTROL_NO_CREATE", "no-create" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiControlFlags", values); } return etype; } GType swamigui_control_object_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_CONTROL_OBJECT_NO_LABELS, "SWAMIGUI_CONTROL_OBJECT_NO_LABELS", "no-labels" }, { SWAMIGUI_CONTROL_OBJECT_NO_SORT, "SWAMIGUI_CONTROL_OBJECT_NO_SORT", "no-sort" }, { SWAMIGUI_CONTROL_OBJECT_PROP_LABELS, "SWAMIGUI_CONTROL_OBJECT_PROP_LABELS", "prop-labels" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiControlObjectFlags", values); } return etype; } /* enumerations from "SwamiguiItemMenu.h" */ GType swamigui_item_menu_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_ITEM_MENU_INACTIVE, "SWAMIGUI_ITEM_MENU_INACTIVE", "inactive" }, { SWAMIGUI_ITEM_MENU_PLUGIN, "SWAMIGUI_ITEM_MENU_PLUGIN", "plugin" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiItemMenuFlags", values); } return etype; } /* enumerations from "SwamiguiMultiSave.h" */ GType swamigui_multi_save_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_MULTI_SAVE_CLOSE_MODE, "SWAMIGUI_MULTI_SAVE_CLOSE_MODE", "mode" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiMultiSaveFlags", values); } return etype; } /* enumerations from "SwamiguiPanelSF2Gen.h" */ GType swamigui_panel_sf2_gen_op_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_PANEL_SF2_GEN_LABEL, "SWAMIGUI_PANEL_SF2_GEN_LABEL", "label" }, { SWAMIGUI_PANEL_SF2_GEN_COLUMN, "SWAMIGUI_PANEL_SF2_GEN_COLUMN", "column" }, { SWAMIGUI_PANEL_SF2_GEN_END, "SWAMIGUI_PANEL_SF2_GEN_END", "end" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiPanelSF2GenOp", values); } return etype; } /* enumerations from "SwamiguiPaste.h" */ GType swamigui_paste_status_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_PASTE_NORMAL, "SWAMIGUI_PASTE_NORMAL", "normal" }, { SWAMIGUI_PASTE_ERROR, "SWAMIGUI_PASTE_ERROR", "error" }, { SWAMIGUI_PASTE_UNHANDLED, "SWAMIGUI_PASTE_UNHANDLED", "unhandled" }, { SWAMIGUI_PASTE_CONFLICT, "SWAMIGUI_PASTE_CONFLICT", "conflict" }, { SWAMIGUI_PASTE_CANCEL, "SWAMIGUI_PASTE_CANCEL", "cancel" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiPasteStatus", values); } return etype; } GType swamigui_paste_decision_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_PASTE_NO_DECISION, "SWAMIGUI_PASTE_NO_DECISION", "no-decision" }, { SWAMIGUI_PASTE_SKIP, "SWAMIGUI_PASTE_SKIP", "skip" }, { SWAMIGUI_PASTE_CHANGED, "SWAMIGUI_PASTE_CHANGED", "changed" }, { SWAMIGUI_PASTE_REPLACE, "SWAMIGUI_PASTE_REPLACE", "replace" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiPasteDecision", values); } return etype; } /* enumerations from "SwamiguiRoot.h" */ GType swamigui_quit_confirm_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_QUIT_CONFIRM_ALWAYS, "SWAMIGUI_QUIT_CONFIRM_ALWAYS", "always" }, { SWAMIGUI_QUIT_CONFIRM_UNSAVED, "SWAMIGUI_QUIT_CONFIRM_UNSAVED", "unsaved" }, { SWAMIGUI_QUIT_CONFIRM_NEVER, "SWAMIGUI_QUIT_CONFIRM_NEVER", "never" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiQuitConfirm", values); } return etype; } /* enumerations from "SwamiguiSampleEditor.h" */ GType swamigui_sample_editor_status_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_SAMPLE_EDITOR_NORMAL, "SWAMIGUI_SAMPLE_EDITOR_NORMAL", "normal" }, { SWAMIGUI_SAMPLE_EDITOR_INIT, "SWAMIGUI_SAMPLE_EDITOR_INIT", "init" }, { SWAMIGUI_SAMPLE_EDITOR_UPDATE, "SWAMIGUI_SAMPLE_EDITOR_UPDATE", "update" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiSampleEditorStatus", values); } return etype; } GType swamigui_sample_editor_marker_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { { SWAMIGUI_SAMPLE_EDITOR_MARKER_SINGLE, "SWAMIGUI_SAMPLE_EDITOR_MARKER_SINGLE", "single" }, { SWAMIGUI_SAMPLE_EDITOR_MARKER_VIEW, "SWAMIGUI_SAMPLE_EDITOR_MARKER_VIEW", "view" }, { SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE, "SWAMIGUI_SAMPLE_EDITOR_MARKER_SIZE", "size" }, { 0, NULL, NULL } }; etype = g_flags_register_static ("SwamiguiSampleEditorMarkerFlags", values); } return etype; } GType swamigui_sample_editor_marker_id_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_SELECTION, "SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_SELECTION", "selection" }, { SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_START, "SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_START", "loop-find-start" }, { SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_END, "SWAMIGUI_SAMPLE_EDITOR_MARKER_ID_LOOP_FIND_END", "loop-find-end" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiSampleEditorMarkerId", values); } return etype; } /* enumerations from "SwamiguiSplits.h" */ GType swamigui_splits_mode_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_SPLITS_NOTE, "SWAMIGUI_SPLITS_NOTE", "note" }, { SWAMIGUI_SPLITS_VELOCITY, "SWAMIGUI_SPLITS_VELOCITY", "velocity" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiSplitsMode", values); } return etype; } GType swamigui_splits_status_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_SPLITS_NORMAL, "SWAMIGUI_SPLITS_NORMAL", "normal" }, { SWAMIGUI_SPLITS_INIT, "SWAMIGUI_SPLITS_INIT", "init" }, { SWAMIGUI_SPLITS_MODE, "SWAMIGUI_SPLITS_MODE", "mode" }, { SWAMIGUI_SPLITS_UPDATE, "SWAMIGUI_SPLITS_UPDATE", "update" }, { SWAMIGUI_SPLITS_CHANGED, "SWAMIGUI_SPLITS_CHANGED", "changed" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiSplitsStatus", values); } return etype; } /* enumerations from "SwamiguiStatusbar.h" */ GType swamigui_statusbar_pos_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_STATUSBAR_POS_LEFT, "SWAMIGUI_STATUSBAR_POS_LEFT", "left" }, { SWAMIGUI_STATUSBAR_POS_RIGHT, "SWAMIGUI_STATUSBAR_POS_RIGHT", "right" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiStatusbarPos", values); } return etype; } GType swamigui_statusbar_timeout_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { { SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT, "SWAMIGUI_STATUSBAR_TIMEOUT_DEFAULT", "default" }, { SWAMIGUI_STATUSBAR_TIMEOUT_FOREVER, "SWAMIGUI_STATUSBAR_TIMEOUT_FOREVER", "forever" }, { 0, NULL, NULL } }; etype = g_enum_register_static ("SwamiguiStatusbarTimeout", values); } return etype; } /* Generated data ends here */ swami-2.2.0/src/swamigui/builtin_enums.h000066400000000000000000000074371361104770400203130ustar00rootroot00000000000000/* builin_enums.h Prototype of registration functions of type enumeration (or flags) defined in the respectives headers files. Typically this file could be generated automatically at make time (with the help of glib-mkenums). Compiling on Windows: glib-mkenums is a perl script and Perl isn't natively installed. To avoid Perl dependency, the file should manually updated. This shouldn't be a problem when new enumerations are slowly added over time. Please read the naming conventions described in builin_enums.c. */ #ifndef __SWAMIGUI_BUILTIN_ENUMS_H__ #define __SWAMIGUI_BUILTIN_ENUMS_H__ #include G_BEGIN_DECLS /* enumerations from "SwamiguiBarPtr.h" */ GType swamigui_bar_ptr_type_get_type (void); #define SWAMIGUI_TYPE_BAR_PTR_TYPE (swamigui_bar_ptr_type_get_type()) /* enumerations from "SwamiguiCanvasMod.h" */ GType swamigui_canvas_mod_type_get_type (void); #define SWAMIGUI_TYPE_CANVAS_MOD_TYPE (swamigui_canvas_mod_type_get_type()) GType swamigui_canvas_mod_axis_get_type (void); #define SWAMIGUI_TYPE_CANVAS_MOD_AXIS (swamigui_canvas_mod_axis_get_type()) GType swamigui_canvas_mod_flags_get_type (void); #define SWAMIGUI_TYPE_CANVAS_MOD_FLAGS (swamigui_canvas_mod_flags_get_type()) GType swamigui_canvas_mod_actions_get_type (void); #define SWAMIGUI_TYPE_CANVAS_MOD_ACTIONS (swamigui_canvas_mod_actions_get_type()) /* enumerations from "SwamiguiControl.h" */ GType swamigui_control_rank_get_type (void); #define SWAMIGUI_TYPE_CONTROL_RANK (swamigui_control_rank_get_type()) GType swamigui_control_flags_get_type (void); #define SWAMIGUI_TYPE_CONTROL_FLAGS (swamigui_control_flags_get_type()) GType swamigui_control_object_flags_get_type (void); #define SWAMIGUI_TYPE_CONTROL_OBJECT_FLAGS (swamigui_control_object_flags_get_type()) /* enumerations from "SwamiguiItemMenu.h" */ GType swamigui_item_menu_flags_get_type (void); #define SWAMIGUI_TYPE_ITEM_MENU_FLAGS (swamigui_item_menu_flags_get_type()) /* enumerations from "SwamiguiMultiSave.h" */ GType swamigui_multi_save_flags_get_type (void); #define SWAMIGUI_TYPE_MULTI_SAVE_FLAGS (swamigui_multi_save_flags_get_type()) /* enumerations from "SwamiguiPanelSF2Gen.h" */ GType swamigui_panel_sf2_gen_op_get_type (void); #define SWAMIGUI_TYPE_PANEL_SF2_GEN_OP (swamigui_panel_sf2_gen_op_get_type()) /* enumerations from "SwamiguiPaste.h" */ GType swamigui_paste_status_get_type (void); #define SWAMIGUI_TYPE_PASTE_STATUS (swamigui_paste_status_get_type()) GType swamigui_paste_decision_get_type (void); #define SWAMIGUI_TYPE_PASTE_DECISION (swamigui_paste_decision_get_type()) /* enumerations from "SwamiguiRoot.h" */ GType swamigui_quit_confirm_get_type (void); #define SWAMIGUI_TYPE_QUIT_CONFIRM (swamigui_quit_confirm_get_type()) /* enumerations from "SwamiguiSampleEditor.h" */ GType swamigui_sample_editor_status_get_type (void); #define SWAMIGUI_TYPE_SAMPLE_EDITOR_STATUS (swamigui_sample_editor_status_get_type()) GType swamigui_sample_editor_marker_flags_get_type (void); #define SWAMIGUI_TYPE_SAMPLE_EDITOR_MARKER_FLAGS (swamigui_sample_editor_marker_flags_get_type()) GType swamigui_sample_editor_marker_id_get_type (void); #define SWAMIGUI_TYPE_SAMPLE_EDITOR_MARKER_ID (swamigui_sample_editor_marker_id_get_type()) /* enumerations from "SwamiguiSplits.h" */ GType swamigui_splits_mode_get_type (void); #define SWAMIGUI_TYPE_SPLITS_MODE (swamigui_splits_mode_get_type()) GType swamigui_splits_status_get_type (void); #define SWAMIGUI_TYPE_SPLITS_STATUS (swamigui_splits_status_get_type()) /* enumerations from "SwamiguiStatusbar.h" */ GType swamigui_statusbar_pos_get_type (void); #define SWAMIGUI_TYPE_STATUSBAR_POS (swamigui_statusbar_pos_get_type()) GType swamigui_statusbar_timeout_get_type (void); #define SWAMIGUI_TYPE_STATUSBAR_TIMEOUT (swamigui_statusbar_timeout_get_type()) G_END_DECLS #endif /* __SWAMIGUI_BUILTIN_ENUMS_H__ */ /* Generated data ends here */ swami-2.2.0/src/swamigui/glade_strings.c000066400000000000000000000143111361104770400202430ustar00rootroot00000000000000/* * Translatable strings file generated by Glade. * Add this file to your project's POTFILES.in. * DO NOT compile it as part of your application. */ gchar *s = N_("Find"); gchar *s = N_("File:"); gchar *s = N_("Type:"); gchar *s = N_("All Files"); gchar *s = N_("Preset"); gchar *s = N_("Instrument"); gchar *s = N_("Sample"); gchar *s = N_("Start from beginning"); gchar *s = N_("Preset:"); gchar *s = N_("Name:"); gchar *s = N_("Sub string"); gchar *s = N_("Starts with"); gchar *s = N_("Bank:"); gchar *s = N_("Search"); gchar *s = N_("Close"); gchar *s = N_("Cancel"); gchar *s = N_("Percussion"); gchar *s = N_("Name:"); gchar *s = N_("Bank:"); gchar *s = N_("Preset:"); gchar *s = N_("Name:"); gchar *s = N_("Name:"); gchar *s = N_("Rate:"); gchar *s = N_("Name of sample"); gchar *s = N_("MIDI note number of original pitch"); gchar *s = N_("-"); gchar *s = N_("Sample rate"); gchar *s = N_("48000"); gchar *s = N_("44100"); gchar *s = N_("22050"); gchar *s = N_("11025"); gchar *s = N_("Tuning:"); gchar *s = N_("Fine tuning in cents"); gchar *s = N_("cents"); gchar *s = N_("Note:"); gchar *s = N_("New Sample"); gchar *s = N_("Name:"); gchar *s = N_("Import loop information"); gchar *s = N_("Raw Sample Parameters"); gchar *s = N_("Rate:"); gchar *s = N_("Custom:"); gchar *s = N_("16 bit"); gchar *s = N_("8 bit"); gchar *s = N_("Mono"); gchar *s = N_("Stereo"); gchar *s = N_("44100"); gchar *s = N_("22050"); gchar *s = N_("11025"); gchar *s = N_("Custom"); gchar *s = N_("Width:"); gchar *s = N_("Channels:"); gchar *s = N_("Endian:"); gchar *s = N_("Little"); gchar *s = N_("Big"); gchar *s = N_("Sign:"); gchar *s = N_("Signed"); gchar *s = N_("Unsigned"); gchar *s = N_("OK"); gchar *s = N_("Cancel"); gchar *s = N_("Swami Tips"); gchar *s = N_("Show tips on start up"); gchar *s = N_("Previous"); gchar *s = N_("Next"); gchar *s = N_("Close"); gchar *s = N_("Preferences"); gchar *s = N_("Patches"); gchar *s = N_("Samples"); gchar *s = N_("Search Path"); gchar *s = N_("Browse"); gchar *s = N_("Browse"); gchar *s = N_("Browse"); gchar *s = N_("Colon separated list of directories to search for"); gchar *s = N_("Default Paths"); gchar *s = N_("Swami Tips"); gchar *s = N_("Splash image"); gchar *s = N_("Restore pane geometry"); gchar *s = N_("Startup Options"); gchar *s = N_("Confirm Quit"); gchar *s = N_("Always"); gchar *s = N_("Unsaved"); gchar *s = N_("Never"); gchar *s = N_("Save window geometry"); gchar *s = N_("Save Now"); gchar *s = N_("Exit Options"); gchar *s = N_("General"); gchar *s = N_("Bank"); gchar *s = N_("Preset"); gchar *s = N_("Temporary Audible"); gchar *s = N_("Text appended to stereo channel names"); gchar *s = N_("Left"); gchar *s = N_("Right"); gchar *s = N_("Max swap file waste"); gchar *s = N_("MB"); gchar *s = N_("Patches"); gchar *s = N_("Lower Octave"); gchar *s = N_("Upper Octave"); gchar *s = N_("Change"); gchar *s = N_("Change All"); gchar *s = N_("Piano"); gchar *s = N_("OK"); gchar *s = N_("Save Preferences"); gchar *s = N_("Cancel"); gchar *s = N_("Paste conflict"); gchar *s = N_("Source"); gchar *s = N_("Modify source item"); gchar *s = N_("Modify"); gchar *s = N_("Destination"); gchar *s = N_("Modify conflict item"); gchar *s = N_("Modify"); gchar *s = N_("For"); gchar *s = N_("Remember action for other items"); gchar *s = N_("This Conflict"); gchar *s = N_("This Type"); gchar *s = N_("All"); gchar *s = N_("Skip this paste item"); gchar *s = N_("Skip"); gchar *s = N_("Replace destination"); gchar *s = N_("Replace"); gchar *s = N_("Cancel entire paste operation"); gchar *s = N_("Cancel"); gchar *s = N_("Undo History"); gchar *s = N_("Backward in state history"); gchar *s = N_("Back"); gchar *s = N_("Forward in state history"); gchar *s = N_("Forward"); gchar *s = N_("Undo previous action"); gchar *s = N_("Undo"); gchar *s = N_("Redo next action"); gchar *s = N_("Redo"); gchar *s = N_("Jump to state of selected item"); gchar *s = N_("Jump"); gchar *s = N_("Close"); gchar *s = N_("Name:"); gchar *s = N_("Author:"); gchar *s = N_("Copyright:"); gchar *s = N_("Product:"); gchar *s = N_("Date:"); gchar *s = N_("Today"); gchar *s = N_("Version:"); gchar *s = N_("Sound Engine:"); gchar *s = N_("ROM:"); gchar *s = N_("Created With:"); gchar *s = N_("Edited With:"); gchar *s = N_("Comment"); gchar *s = N_("Chan"); gchar *s = N_("Preset"); gchar *s = N_("Bank"); gchar *s = N_("New"); gchar *s = N_("Delete"); gchar *s = N_("Source"); gchar *s = N_("Destination"); gchar *s = N_("Amount"); gchar *s = N_("Amount Source"); gchar *s = N_("Root Note"); gchar *s = N_("Exclusive Class"); gchar *s = N_("Fixed Note"); gchar *s = N_("Fixed Velocity"); gchar *s = N_("Set"); gchar *s = N_("Set"); gchar *s = N_("Set"); gchar *s = N_("Set"); gchar *s = N_("File"); gchar *s = N_("Edit"); gchar *s = N_("Plugins"); gchar *s = N_("Help"); gchar *s = N_("Swami Tips"); gchar *s = N_("About"); gchar *s = N_("Swami"); gchar *s = N_("Copyright (C) 1999-2006 Josh Green"); gchar *s = N_("Distributed under the GNU General Public License"); gchar *s = N_("translator-credits"); gchar *s = N_("Loop Finder"); gchar *s = N_("Sample"); gchar *s = N_("Length"); gchar *s = N_("Results"); gchar *s = N_("Min loop size"); gchar *s = N_("Size of analysis window (affects quality of results)"); gchar *s = N_("Minimum length of matching loops"); gchar *s = N_("Maximum loop suggestion results"); gchar *s = N_("Analysis window"); gchar *s = N_("Max results"); gchar *s = N_("Loop end"); gchar *s = N_("Loop start"); gchar *s = N_("Loop end search area position"); gchar *s = N_("Loop start search area position"); gchar *s = N_("Position"); gchar *s = N_("Loop start search area size"); gchar *s = N_("Loop end search area size"); gchar *s = N_("Search entire sample for loop start"); gchar *s = N_("Entire Sample"); gchar *s = N_("Search entire sample for loop end"); gchar *s = N_("Entire sample"); gchar *s = N_("Size"); gchar *s = N_("Search area"); gchar *s = N_("Revert to original loop values"); gchar *s = N_("0.00 secs"); gchar *s = N_("window1"); gchar *s = N_(" Name"); gchar *s = N_("Save current script"); gchar *s = N_("Create new script"); gchar *s = N_("Execute current script"); gchar *s = N_("Execute a line when ENTER is pressed"); gchar *s = N_("Shell mode"); gchar *s = N_("Script Editor"); gchar *s = N_("Python Console"); swami-2.2.0/src/swamigui/help.c000066400000000000000000000222101361104770400163430ustar00rootroot00000000000000/* * help.c - User interface help routines * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "SwamiguiRoot.h" #include "help.h" #include "splash.h" #include "icons.h" #include "i18n.h" #include "util.h" static void swamigui_help_swamitips_set_tip (GtkWidget *tips, gint tipnum); static void swamigui_help_cb_swamitips_next (GtkWidget *btn, GtkWidget *tips); static void swamigui_help_cb_swamitips_previous (GtkWidget *btn, GtkWidget *tips); static void swamigui_help_cb_again_toggled (GtkWidget *btn, gpointer data); static gchar *swamitips_msg[] = { N_("Welcome to Swami!\n\n" "Many operations are performed by Right clicking on the instrument tree." " The type of item clicked on determines the options that are available."), N_("To select multiple items in the instrument tree:\n" "Hold down CTRL to mark individual items or SHIFT to mark a range."), N_("To zoom in the sample viewer:\n" "Middle click and drag the mouse to the left or right in the sample" " viewer. A vertical line will appear to mark the position to zoom into," " and the distance from the marker determines how fast the zoom is" " performed. Moving the mouse to the opposite side of" " the zoom marker will unzoom. The mouse Wheel can also be used to zoom.\n" "SHIFT Middle click and drag will scroll the sample left or right."), N_("The right most view in the Sample Editor assists with making seamless" " loops. The sample points surrounding the start of the loop are shown" " in green while the end sample points are red. They are overlaid on one" " another, where they intersect they become yellow. Zooming can be performed" " in the loop viewer, just like the normal sample view. The more yellow points" " surrounding the middle line, the more seamless the loop!"), N_("In the note range view click and drag on the same line as the range and" " the nearest endpoint will be adjusted. Multiple ranges can be selected" " using CTRL and SHIFT. Clicking and dragging with the Middle mouse button" " will move a range. The \"Move\" drop down selector can be used to set" " if the ranges, root notes or both are moved together. The root notes" " are shown as blue circles on the same line as the range they belong to."), N_("To add samples to instruments:\n" "Select the samples and/or instrument zones you want to add and then Right" " click on the instrument you would like to add to and select" " \"Paste\". If an instrument zone was selected all its parameters" " will be copied into the newly created zone. The same procedure is used" " to add instruments to presets."), N_("The sample loop finder is actived by clicking the Finder icon in the" " Sample Editor. Two additional range selectors will appear above the sample" " and allow for setting the loop start and end search \"windows\". The" " Config tab contains additional settings which control the algorithm." " The \"Window size\" sets the number of sample points which are compared" " around the loop end points, \"Min loop size\" sets a minimum loop size" " for the results and sample groups provide settings for grouping results" " by their proximity and size. Once the parameters are to your liking," " click the \"Find Loops\" button. The parameter settings can drastically" " affect the time it takes. Once complete a list of results will be" " displayed, clicking a result will assign the given loop. Click the" " \"Revert\" button to return to the loop setting prior to executing the" " find loops operation."), N_("The FFTune plugin provides semi-automated tuning of samples. To access it" " click the FFTune panel tab when a sample or instrument zone is selected." " An FFT calculation is performed and the spectrum is displayed in the view." " A list of tuning results is shown in the list. Clicking a tuning result" " will automatically assign it to the selected sample or zone. Results are" " based on interpreting the strongest frequency components as MIDI root" " note values and calculating the fine tine adjustment required to play" " back the matched frequency component at the given root note. Your milage" " may vary, depending on the sample content. The \"Sample data\" dropdown" " allows for setting what portion of the sample the calculation is" " performed on: All for the entire sample and Loop for just the loop."), N_("Adjusting knobs is done by clicking and dragging the mouse up or down." " Hold the SHIFT key to make finer adjustments."), N_("No more tips!") }; #define TIPCOUNT (sizeof(swamitips_msg) / sizeof(swamitips_msg[0])) static gint swamitip_current; /* current swami tip # */ void swamigui_help_about (void) { GtkWidget *boutwin; GdkPixbuf *pixbuf; if (swamigui_util_activate_unique_dialog ("about", 0)) return; boutwin = swamigui_util_glade_create ("About"); swamigui_util_register_unique_dialog (boutwin, "about", 0); gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (boutwin), VERSION); /* ++ ref Swami logo icon */ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), "swami_logo", 160, 0, NULL); gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG (boutwin), pixbuf); if (pixbuf) g_object_unref (pixbuf); /* -- unref pixbuf */ g_signal_connect_swapped (G_OBJECT(boutwin), "response", G_CALLBACK (gtk_widget_destroy), boutwin); gtk_widget_show (boutwin); } /* Create swami tips dialog and load it with current tip */ void swamigui_help_swamitips_create (SwamiguiRoot *root) { GtkWidget *tips; GtkWidget *widg; int i; g_return_if_fail (SWAMIGUI_IS_ROOT (root)); if (swamigui_util_activate_unique_dialog ("tips", 0)) return; tips = swamigui_util_glade_create ("Tips"); swamigui_util_register_unique_dialog (tips, "tips", 0); g_object_set_data (G_OBJECT (tips), "root", root); widg = swamigui_util_glade_lookup (tips, "CHKagain"); /* update check button to state of Tips Enabled on startup config var */ g_object_get (root, "tips-enable", &i, NULL); if (i) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widg), TRUE); g_signal_connect (widg, "toggled", G_CALLBACK (swamigui_help_cb_again_toggled), root); widg = swamigui_util_glade_lookup (tips, "BTNnext"); g_signal_connect (G_OBJECT (widg), "clicked", G_CALLBACK (swamigui_help_cb_swamitips_next), tips); widg = swamigui_util_glade_lookup (tips, "BTNprev"); g_signal_connect (G_OBJECT (widg), "clicked", G_CALLBACK (swamigui_help_cb_swamitips_previous), tips); widg = swamigui_util_glade_lookup (tips, "BTNclose"); g_signal_connect_swapped (G_OBJECT (widg), "clicked", G_CALLBACK (gtk_widget_destroy), tips); g_object_get (root, "tips-position", &i, NULL); swamigui_help_swamitips_set_tip (tips, i); gtk_widget_show (tips); } static void swamigui_help_swamitips_set_tip (GtkWidget *tips, gint tipnum) { SwamiguiRoot *root; GtkWidget *txtview; GtkTextBuffer *buffer; GtkWidget *btn; gchar *msg; tipnum = CLAMP (tipnum, 0, TIPCOUNT - 1); btn = swamigui_util_glade_lookup (tips, "BTNprev"); gtk_widget_set_sensitive (btn, (tipnum != 0)); btn = swamigui_util_glade_lookup (tips, "BTNnext"); gtk_widget_set_sensitive (btn, (tipnum != TIPCOUNT - 1)); txtview = swamigui_util_glade_lookup (tips, "TXTview"); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txtview)); msg = _(swamitips_msg[tipnum]); /* get the tip */ gtk_text_buffer_set_text (buffer, msg, -1); swamitip_current = tipnum; root = g_object_get_data (G_OBJECT (tips), "root"); if (root) { tipnum++; if (tipnum >= TIPCOUNT) tipnum = TIPCOUNT; g_object_set (root, "tips-position", tipnum, NULL); } } /* next tip callback */ static void swamigui_help_cb_swamitips_next (GtkWidget *btn, GtkWidget *tips) { swamigui_help_swamitips_set_tip (tips, swamitip_current + 1); } /* previous tip callback */ static void swamigui_help_cb_swamitips_previous (GtkWidget *btn, GtkWidget *tips) { swamigui_help_swamitips_set_tip (tips, swamitip_current - 1); } static void swamigui_help_cb_again_toggled (GtkWidget *btn, gpointer data) { SwamiguiRoot *root = SWAMIGUI_ROOT (data); g_object_set (root, "tips-enable", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (btn)), NULL); } swami-2.2.0/src/swamigui/help.h000066400000000000000000000020721361104770400163540ustar00rootroot00000000000000/* * help.h - Header for help related user interface functions. * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __HELP_H__ #define __HELP_H__ #include #include "SwamiguiRoot.h" void swamigui_help_about (void); void swamigui_help_swamitips_create (SwamiguiRoot *root); #endif /* __HELP_H__ */ swami-2.2.0/src/swamigui/i18n.h000066400000000000000000000006251361104770400162050ustar00rootroot00000000000000#ifndef __SWAMIGUI_I18N_H__ #define __SWAMIGUI_I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) gettext(x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif /* __SWAMIGUI_I18N_H__ */ swami-2.2.0/src/swamigui/icons.c000066400000000000000000000126671361104770400165450ustar00rootroot00000000000000/* * icons.c - Swami stock icons * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include "config.h" #include "icons.h" #include "util.h" /* icon mappings for IpatchItem categories */ struct { int category; char *icon; } category_icons[] = { { IPATCH_CATEGORY_NONE, GTK_STOCK_DIRECTORY }, { IPATCH_CATEGORY_BASE, GTK_STOCK_DIRECTORY }, { IPATCH_CATEGORY_PROGRAM, SWAMIGUI_STOCK_PRESET }, { IPATCH_CATEGORY_INSTRUMENT, SWAMIGUI_STOCK_INST }, { IPATCH_CATEGORY_INSTRUMENT_REF, SWAMIGUI_STOCK_INST }, { IPATCH_CATEGORY_SAMPLE, SWAMIGUI_STOCK_SAMPLE }, { IPATCH_CATEGORY_SAMPLE_REF, SWAMIGUI_STOCK_SAMPLE } }; /* stores the registered "CustomLarge1" custom icon size enum */ int swamigui_icon_size_custom_large1; void _swamigui_stock_icons_init (void) { GtkIconFactory *factory; GtkIconTheme *theme = gtk_icon_theme_get_default (); int prefix_len = strlen ("swamigui_"); gchar *path; int i; /* !! keep synchronized with icons.h !! */ static const char *items[] = { SWAMIGUI_STOCK_CONCAVE_NEG_BI, SWAMIGUI_STOCK_CONCAVE_NEG_UNI, SWAMIGUI_STOCK_CONCAVE_POS_BI, SWAMIGUI_STOCK_CONCAVE_POS_UNI, SWAMIGUI_STOCK_CONVEX_NEG_BI, SWAMIGUI_STOCK_CONVEX_NEG_UNI, SWAMIGUI_STOCK_CONVEX_POS_BI, SWAMIGUI_STOCK_CONVEX_POS_UNI, SWAMIGUI_STOCK_DLS, SWAMIGUI_STOCK_EFFECT_CONTROL, SWAMIGUI_STOCK_EFFECT_DEFAULT, SWAMIGUI_STOCK_EFFECT_GRAPH, SWAMIGUI_STOCK_EFFECT_SET, SWAMIGUI_STOCK_EFFECT_VIEW, SWAMIGUI_STOCK_GIG, SWAMIGUI_STOCK_GLOBAL_ZONE, SWAMIGUI_STOCK_INST, SWAMIGUI_STOCK_LINEAR_NEG_BI, SWAMIGUI_STOCK_LINEAR_NEG_UNI, SWAMIGUI_STOCK_LINEAR_POS_BI, SWAMIGUI_STOCK_LINEAR_POS_UNI, SWAMIGUI_STOCK_LOOP_NONE, SWAMIGUI_STOCK_LOOP_STANDARD, SWAMIGUI_STOCK_LOOP_RELEASE, SWAMIGUI_STOCK_MODENV, SWAMIGUI_STOCK_MODENV_ATTACK, SWAMIGUI_STOCK_MODENV_DECAY, SWAMIGUI_STOCK_MODENV_DELAY, SWAMIGUI_STOCK_MODENV_HOLD, SWAMIGUI_STOCK_MODENV_RELEASE, SWAMIGUI_STOCK_MODENV_SUSTAIN, SWAMIGUI_STOCK_MODULATOR_EDITOR, SWAMIGUI_STOCK_MODULATOR_JUNCT, SWAMIGUI_STOCK_MUTE, SWAMIGUI_STOCK_PIANO, SWAMIGUI_STOCK_PRESET, SWAMIGUI_STOCK_PYTHON, SWAMIGUI_STOCK_SAMPLE, SWAMIGUI_STOCK_SAMPLE_ROM, SWAMIGUI_STOCK_SAMPLE_VIEWER, SWAMIGUI_STOCK_SLI, SWAMIGUI_STOCK_SOUNDFONT, SWAMIGUI_STOCK_SPLITS, SWAMIGUI_STOCK_SWITCH_NEG_BI, SWAMIGUI_STOCK_SWITCH_NEG_UNI, SWAMIGUI_STOCK_SWITCH_POS_BI, SWAMIGUI_STOCK_SWITCH_POS_UNI, SWAMIGUI_STOCK_TREE, SWAMIGUI_STOCK_TUNING, SWAMIGUI_STOCK_VELOCITY, SWAMIGUI_STOCK_VOLENV, SWAMIGUI_STOCK_VOLENV_ATTACK, SWAMIGUI_STOCK_VOLENV_DECAY, SWAMIGUI_STOCK_VOLENV_DELAY, SWAMIGUI_STOCK_VOLENV_HOLD, SWAMIGUI_STOCK_VOLENV_RELEASE, SWAMIGUI_STOCK_VOLENV_SUSTAIN }; /* ++ alloc path */ path = swamigui_util_get_resource_path (SWAMIGUI_RESOURCE_PATH_IMAGES); gtk_icon_theme_append_search_path (theme, path); factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (factory); for (i = 0; i < (int) G_N_ELEMENTS (items); i++) { GtkIconSet *icon_set; GdkPixbuf *pixbuf; gchar *fn, *s; s = g_strconcat (&items[i][prefix_len], ".png", NULL); fn = g_build_filename (path, s, NULL); g_free (s); pixbuf = gdk_pixbuf_new_from_file (fn, NULL); g_free (fn); if (!pixbuf) continue; if (strcmp (items[i], SWAMIGUI_STOCK_MODULATOR_JUNCT) == 0) { int width, height; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); swamigui_icon_size_custom_large1 = gtk_icon_size_register ("CustomLarge1", width, height); } icon_set = gtk_icon_set_new_from_pixbuf (pixbuf); gtk_icon_factory_add (factory, items[i], icon_set); gtk_icon_set_unref (icon_set); g_object_unref (G_OBJECT (pixbuf)); } g_object_unref (G_OBJECT (factory)); g_free (path); /* -- free allocated path */ /* set the default application icon name */ gtk_window_set_default_icon_name ("swami"); } /** * swamigui_icon_get_category_icon: * @category: IpatchItem type property "category" (IPATCH_CATEGORY_*) * * Get the icon used for the specified IpatchItem type category. * * Returns: Stock icon ID or %NULL if no icon for @category or invalid category. */ char * swamigui_icon_get_category_icon (int category) { int i; for (i = 0; i < G_N_ELEMENTS (category_icons); i++) { if (category_icons[i].category == category) return (category_icons[i].icon); } return NULL; } swami-2.2.0/src/swamigui/icons.h000066400000000000000000000106001361104770400165330ustar00rootroot00000000000000/* * icons.h - Swami stock icons * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_ICONS_H__ #define __SWAMIGUI_ICONS_H__ /* !! keep synchronized with icons.c !! */ #define SWAMIGUI_STOCK_CONCAVE_NEG_BI "swamigui_concave_neg_bi" #define SWAMIGUI_STOCK_CONCAVE_NEG_UNI "swamigui_concave_neg_uni" #define SWAMIGUI_STOCK_CONCAVE_POS_BI "swamigui_concave_pos_bi" #define SWAMIGUI_STOCK_CONCAVE_POS_UNI "swamigui_concave_pos_uni" #define SWAMIGUI_STOCK_CONVEX_NEG_BI "swamigui_convex_neg_bi" #define SWAMIGUI_STOCK_CONVEX_NEG_UNI "swamigui_convex_neg_uni" #define SWAMIGUI_STOCK_CONVEX_POS_BI "swamigui_convex_pos_bi" #define SWAMIGUI_STOCK_CONVEX_POS_UNI "swamigui_convex_pos_uni" #define SWAMIGUI_STOCK_DLS "swamigui_DLS" #define SWAMIGUI_STOCK_EFFECT_CONTROL "swamigui_effect_control" #define SWAMIGUI_STOCK_EFFECT_DEFAULT "swamigui_effect_default" #define SWAMIGUI_STOCK_EFFECT_GRAPH "swamigui_effect_graph" #define SWAMIGUI_STOCK_EFFECT_SET "swamigui_effect_set" #define SWAMIGUI_STOCK_EFFECT_VIEW "swamigui_effect_view" #define SWAMIGUI_STOCK_GIG "swamigui_GIG" #define SWAMIGUI_STOCK_GLOBAL_ZONE "swamigui_global_zone" #define SWAMIGUI_STOCK_INST "swamigui_inst" #define SWAMIGUI_STOCK_LINEAR_NEG_BI "swamigui_linear_neg_bi" #define SWAMIGUI_STOCK_LINEAR_NEG_UNI "swamigui_linear_neg_uni" #define SWAMIGUI_STOCK_LINEAR_POS_BI "swamigui_linear_pos_bi" #define SWAMIGUI_STOCK_LINEAR_POS_UNI "swamigui_linear_pos_uni" #define SWAMIGUI_STOCK_LOOP_NONE "swamigui_loop_none" #define SWAMIGUI_STOCK_LOOP_STANDARD "swamigui_loop_standard" #define SWAMIGUI_STOCK_LOOP_RELEASE "swamigui_loop_release" #define SWAMIGUI_STOCK_MODENV "swamigui_modenv" #define SWAMIGUI_STOCK_MODENV_ATTACK "swamigui_modenv_attack" #define SWAMIGUI_STOCK_MODENV_DECAY "swamigui_modenv_decay" #define SWAMIGUI_STOCK_MODENV_DELAY "swamigui_modenv_delay" #define SWAMIGUI_STOCK_MODENV_HOLD "swamigui_modenv_hold" #define SWAMIGUI_STOCK_MODENV_RELEASE "swamigui_modenv_release" #define SWAMIGUI_STOCK_MODENV_SUSTAIN "swamigui_modenv_sustain" #define SWAMIGUI_STOCK_MODULATOR_EDITOR "swamigui_modulator_editor" #define SWAMIGUI_STOCK_MODULATOR_JUNCT "swamigui_modulator_junct" #define SWAMIGUI_STOCK_MUTE "swamigui_mute" #define SWAMIGUI_STOCK_PIANO "swamigui_piano" #define SWAMIGUI_STOCK_PRESET "swamigui_preset" #define SWAMIGUI_STOCK_PYTHON "swamigui_python" #define SWAMIGUI_STOCK_SAMPLE "swamigui_sample" #define SWAMIGUI_STOCK_SAMPLE_ROM "swamigui_sample_rom" #define SWAMIGUI_STOCK_SAMPLE_VIEWER "swamigui_sample_viewer" #define SWAMIGUI_STOCK_SLI "swamigui_SLI" #define SWAMIGUI_STOCK_SOUNDFONT "swamigui_SoundFont" #define SWAMIGUI_STOCK_SPLITS "swamigui_splits" #define SWAMIGUI_STOCK_SWITCH_NEG_BI "swamigui_switch_neg_bi" #define SWAMIGUI_STOCK_SWITCH_NEG_UNI "swamigui_switch_neg_uni" #define SWAMIGUI_STOCK_SWITCH_POS_BI "swamigui_switch_pos_bi" #define SWAMIGUI_STOCK_SWITCH_POS_UNI "swamigui_switch_pos_uni" #define SWAMIGUI_STOCK_TREE "swamigui_tree" #define SWAMIGUI_STOCK_TUNING "swamigui_tuning" #define SWAMIGUI_STOCK_VELOCITY "swamigui_velocity" #define SWAMIGUI_STOCK_VOLENV "swamigui_volenv" #define SWAMIGUI_STOCK_VOLENV_ATTACK "swamigui_volenv_attack" #define SWAMIGUI_STOCK_VOLENV_DECAY "swamigui_volenv_decay" #define SWAMIGUI_STOCK_VOLENV_DELAY "swamigui_volenv_delay" #define SWAMIGUI_STOCK_VOLENV_HOLD "swamigui_volenv_hold" #define SWAMIGUI_STOCK_VOLENV_RELEASE "swamigui_volenv_release" #define SWAMIGUI_STOCK_VOLENV_SUSTAIN "swamigui_volenv_sustain" #define SWAMIGUI_ICON_SIZE_CUSTOM_LARGE1 swamigui_icon_size_custom_large1 extern int swamigui_icon_size_custom_large1; char *swamigui_icon_get_category_icon (int category); #endif swami-2.2.0/src/swamigui/images/000077500000000000000000000000001361104770400165175ustar00rootroot00000000000000swami-2.2.0/src/swamigui/images/CMakeLists.txt000066400000000000000000000025271361104770400212650ustar00rootroot00000000000000# # Swami # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # install ( FILES DLS.png GIG.png SLI.png SoundFont.png concave_neg_bi.png concave_neg_uni.png concave_pos_bi.png concave_pos_uni.png effect_control.png effect_default.png effect_graph.png effect_set.png effect_view.png convex_neg_bi.png convex_neg_uni.png convex_pos_bi.png convex_pos_uni.png global_zone.png inst.png knob.png linear_neg_bi.png linear_neg_uni.png linear_pos_bi.png linear_pos_uni.png loop_none.png loop_standard.png loop_release.png modenv.png modenv_attack.png modenv_decay.png modenv_delay.png modenv_hold.png modenv_release.png modenv_sustain.png modulator_editor.png modulator_junct.png mute.png piano.png preset.png python.png sample.png sample_rom.png sample_viewer.png splash.png splits.png swami_logo.png switch_neg_bi.png switch_neg_uni.png switch_pos_bi.png switch_pos_uni.png tree.png tuning.png velocity.png volenv.png volenv_attack.png volenv_decay.png volenv_delay.png volenv_hold.png volenv_release.png volenv_sustain.png DESTINATION ${IMAGES_DIR} ) swami-2.2.0/src/swamigui/images/DLS.png000066400000000000000000000013341361104770400176500ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<nIDAT8u]HQ^efj4ghyZ ""[mA H(]Ql+"">Crˋ("LfmZKd~l uhy󜣲;n V`bh>8\Z <VԜl|erN#*$%;@ԯї+/k֧o*aIo6X[U}7c'LJQ|ȎD8wmQl/ yuhm~ {O[l{9Hj +2H N+d~$8D lȡz`͢Z62;7`Hed&0ɓ5 9\U!,.%-ۮXWubnEӝ}.~V<$$JkY`>{K鈍8\/=ʹ{0w?ݾ;?$YT Sb92 CILTDz/>KݩtD**&Š^7~;C |X|0A6Smz9s& -Hi2i9;:Uvd׀;KL%ECϴj6IENDB`swami-2.2.0/src/swamigui/images/GIG.png000066400000000000000000000011031361104770400176260ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<IDAT8}?haorgó*A3""")(TO(Vw] .RA.Y,K : (bbrڴi:b{3gxRJj `HE $i3G4mV?[sӾ HF׸na[$]q`a$_*,MDu~w,Z# :0.XJi$pݱH@:`-mξ0VjCjff^gZeMga]!^tx֞Wb|=c$Oi{/H՟Oi%CJb;< /H=^w1,It8Q,l:2IENDB`swami-2.2.0/src/swamigui/images/SoundFont.png000066400000000000000000000012701361104770400211440ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<JIDAT8}_helCY笳Dlɴ9J?$d7# sqU%^LaXiE305'&&;Usn >oBɼw1H%0 h%t^Bw-jAOkNHksU鏜33#v}՛xK>E}%$“ y(eR>9?(pv}iv.W9Aغ0Ҽpc/CCQ ᘺS:F4/ ? 1 k_pdef:m_?\ݎT'b R xN3|yJ"Yh7[>x6ͭ-/u6`d?D÷'ն|;_Юgj+XS=H=Ԩc:.s_dYy6[oAv,Nݿn[ { >'QƶXl*ၤ_jk}x@[4jy(p'ph {m.]-BMU2_ ݅#suĆ {,9:7ģk0?IENDB`swami-2.2.0/src/swamigui/images/concave_neg_bi.png000066400000000000000000000010171361104770400221450ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͕@FAAlEHoio#lF}|+Bba%vXlX,(f,$wsɄp FG"*R}pA/|CtZk6 h4c1&}}FZp8'x p\.~_Hnp]sb(e:NJR ||4t:ZRJEWy{+ez^}YMhwcEzv:w]ktZVa_?C'{pqnKN4V^?plžy/_h2v `1IMd2GѨf PT*r_H<~{kv3f`0D>/DM_ %-0gƿPVIENDB`swami-2.2.0/src/swamigui/images/concave_neg_uni.png000066400000000000000000000010031361104770400223410ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͖!oP3:HGe  ?Y~i~ĂE%3b!W]isr>;$HRu1x`BuJRn͉w#|~ڶm8L"F`>o'xLhf,jZl6ME\uWv;QmR@$PtO^,%@l|{<nTZ]R2L&T*JdYuXӶm[FD~'#Z%<F4fZ.rAAWH߈K5q Z^+x<E!0qg٩8u]0 `F#`,*}¯r@햚m۹멚S忌Tk n[㗜z:@sy--˲HRuM4SOf&՚Y*~*7GK%IENDB`swami-2.2.0/src/swamigui/images/concave_pos_uni.png000066400000000000000000000010061361104770400223740ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͖AXA"BAB nTJ@Ƴb JO!D!BZd-]ofs?gfb!"G\R eYmF|( iiJ2 k\L&<+FnJR,A~e`۶y]Yt:v?|>hdlx], jZ_.Dz8VPT*p~ f X,d24&x<rf$ptZv@uh4N `8rhQp1rFR?RH뉈G-z|sOBan:N٩;98Υ u49Gc_;6P~[CcIENDB`swami-2.2.0/src/swamigui/images/convex_neg_bi.png000066400000000000000000000010241361104770400220270ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͖?"Am'vL CH'Wl`M 4|,lhu5Wzޫ2 $="1qql6ujZC|~t:ZV$/I#I\.'Ib|GMN=/BQs1DK^F@1@O{jj`Z6&Ud29v;JP( zJQGr\}+> 7e$l}`>o漣[>v=ֳYT*((:y#>u\>mD?-tonA%IENDB`swami-2.2.0/src/swamigui/images/convex_pos_bi.png000066400000000000000000000010451361104770400220620ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͖Oj@AA n<1@w]x=P( ؅PsׅLJo_޼1 DxpCDqRJ)L{yy`cdPVV0Lj1JH.qi6Ml6|^瀶m""tOىά^El6z^0=?v~~v RT,(b+5f(r %~V|{[, Cqxy }|}W3In[AFQqiXn"RJAPGbX4@utv̓f F8__V+, _@#VS pyX.KN1RJ0a'dnt=z&ߠ /- q2IENDB`swami-2.2.0/src/swamigui/images/convex_pos_uni.png000066400000000000000000000007741361104770400222730ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx͖=n@ 槡A%p$Pԑ@\2t"˟W#k훝1 $"1o ٸ[( z/yNsz^ߏw.@tlFc?>NZv;@m3l-l6y<k0 V t<9q,IT*6,}ؕ: dwGs\NxX.gT*%_(iV;9~(W:xJG5>ȥo/gGM]IENDB`swami-2.2.0/src/swamigui/images/effect_control.png000066400000000000000000000020401361104770400222150ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATH͋E`6YffY dNb..Of.% =xYAPS7fftUW bh-47L'I v2."T I5 "aDg80f7U2ki"MxNPgyE&TnBZ/W𶺞⬑%)Frp1febg= (I S`skv}ȞuM =(%(Ū) .:S6U9Fxr$=?,:qhv2/3SA ~yIENDB`swami-2.2.0/src/swamigui/images/effect_set.png000066400000000000000000000003601361104770400213330ustar00rootroot00000000000000PNG  IHDRkFI bKGD XIDATxՔ DOl2nl$a "M(#i>ͺJ)q{(@`̳1\Ȃc4Yk핧@Ɗ Ts@}vWmSJ)>X"g;R#sYֺ5LAk?n_^;UcT]IENDB`swami-2.2.0/src/swamigui/images/effect_view.png000066400000000000000000000003021361104770400215060ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<TIDATH햱 ! ψ=e/$KWnXHXӓ1, ?lpEeQJYRE)l!kIENDB`swami-2.2.0/src/swamigui/images/global_zone.png000066400000000000000000000002411361104770400215150ustar00rootroot00000000000000PNG  IHDROc#"bKGD XVIDATx 0 C:AXVzHxi-Y؏lla I$pT2b&)3t =""'sGXU\̄IENDB`swami-2.2.0/src/swamigui/images/inst.png000066400000000000000000000007141361104770400202040ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<^IDAT8J@jwEVAW\ 6ޥ\҂bk=p80p >\wvǤ^_8DLsFTx *ܔ+9#b ȫ2$;l@>mcO&H {{Z_"hX,@ѨP*wU8?l޳iFfN'B~t7r88N4|%)GE ƼN.V^H<쳮J75bؚu] ´JP$DRLC*#Hg a^X3G2"㛩T*37n@ӴRB\,RՐR,[ Ê4c7m$T*x8Ȑ8[f;kA}mT1uȑMrT[bt 0 n|Ah >" 2PH?N9*jV֭--kAGFF0ܪ\.7EQ4ڇ+4]!45sRBy*eV)f_C|@WW6lۧ\ɿk׮_zl6A{{;T*t]g(zr'X%fg 4k֬abbbH>O?[h* ;w*OP(4&?p]]pqN:ERᩧ*%{y4Oټ!a˖d`t鋬XwH9Ca$I8'-d2<#˲Ǻuow  Dd'{/?{T q[¡NJ_)ew`ׯ֭[e|?P؆.;0Il6'ioM+N>}zA5 L|}^_~zرituuh".^8;G沭>]7o2664 {n}2+off柎;^.1 ͛7sP]+n>alܸSN>lO=.Z@;;tUU4M_ y7 6Caȃ>뺴ˁIqv77T*\.o|7&''2T{R&9df ?QV_yfiwꦖ2NI+WL&|9|BoV.]j/ fX$ rի|^(NR!| iKC@5%fO*s%j3IENDB`swami-2.2.0/src/swamigui/images/linear_neg_bi.png000066400000000000000000000006361361104770400220070ustar00rootroot00000000000000PNG  IHDRybKGD XSIDATx͖0En\( :pˆJ wJ"` :#J hxެ/pr.@DtF]!zc}^h]5EQ $LЂG?@x7}(^9tk <PSeR*%ټʅ\H@";rRJ)7sΥn@@9p]5t]jZHY%7"@~ d$cD+fo=VqG7x9V,Blŧm/>5.lh&Te,Ki=~:v dc[ PE"nxFԎ`Sױib}ds|VQ3]ӾQ0ǻdA7V76mJˑuuv6rRn7Q+RpIENDB`swami-2.2.0/src/swamigui/images/loop_release.png000066400000000000000000000014441361104770400217010ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATH;hq$ke'IL!hj8" A6 P JN4V 39$t6Ұw]z)v|e/X:"hP ;l2,v潛Z.2`oij @k4ۖStyssK;k1ݚ%;ͳUj޷f۞hֳ?j @@Šk$U@GSS 0&ZLs3l UՔW92+)o6W&'D$)ů]N 5@]{;ٵJF@H,~$k YLяc'~=9ioY?F $IU7bUƖ՞TREpQ;j^<\%=;q- I" 0L6vy[8%M2X^!;"ai"<eq_{ $W@GDsӯF>z}f6c@2@m5fuVIs6=0d5ld)A P K*[ )BA$m$1I6&7] DDW:RH!]w?Sisc.x5\.G/yh1L-K4&M"CIENDB`swami-2.2.0/src/swamigui/images/modenv_attack.png000066400000000000000000000010251361104770400220420ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHV=KA}{*1~9R~J8wն݁IENDB`swami-2.2.0/src/swamigui/images/modenv_hold.png000066400000000000000000000011521361104770400215220ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHkA?;ȅCpzEp?=EH (Xi*Ap`m^9п!M* X4fnv7}291hZ:zX_8o228`gycvi)L2n)RaLkM.-7RN "D dxRZT35>}x[Ժ}wk@թ9P~֝:qܤHE,$sJx@"+[ɐϛ,/́.,>3W<~mS64/"O6cݫ?9{/WgyJaA׫lw| #R5\ `]~6d4y 2CN 2IqƣCNƜuL)R0ftA"û fYYZo"Z߽pM$( lMt:.RE1xwo F8PIENDB`swami-2.2.0/src/swamigui/images/modenv_release.png000066400000000000000000000010761361104770400222210ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHՖ?hAٛ%"^H⁂2ML!Z XX^@ QbO,4p"A"fw,bwfWr?x773{"`ݻA5R< ޯt!Q;9kr Tt`c ZD n0s L<6wTt#En͝g&?`[ '9{rū869^| P?Ńyk@'1DmFv‰a'b ).Mr|ʳlmG2Ȳ :`i-]}'?^W)fdxD1{S=[uvCRrʻfj4~Ck1+0#' G_IENDB`swami-2.2.0/src/swamigui/images/modenv_sustain.png000066400000000000000000000011351361104770400222630ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHՖ?hA]M3 9S"Zi!B1(6VF BR*WmDp)$mZEEH%$2ovNZuwrzK/u:q?w؂/JD] >mG߹D=*f`32~ :pg?%pۀF2S`"9t+W&V.TBMPf;SV]P_ƫEmkҟԪ\,]m^&=֨ &\X76ToVq'YGk:͟δN@+JY{d$bЖK ްڢ-׵?G7tF/,t!.`h8lwĬ }'N^?֩&DCYKG" =YU=P(`MoPx.g<*&ӣ~ޕ˫ }A%G+v߼ְeI?7Cy+s$uO-蓴oYKX`hƢkB3,Q,?at81"t|>GfĄa 5nJ_^sѤ)z9=_+3%Г 9y*C&]UC @tI15f >c%"M 'ޒ"n;٣X`Bq}Jfi"6] Su*7 }W2g+1{XG)'όQ,_*]rlޫ[2>5ƒ )06lmo{TRd@v8-@5̮]%muy٭Նae,}Aeg[. )GOun+Xu+lDžh=&v8X^_2wM@H?cA앗jN?Ww+Al ˽{72Nf;*2oB1edU7NhŽ A]m ~--j ێ̓& }8vvc~w-]MW#xz=MY" ŵG]O{Jg s#gŖV%u;AYO{&tp7TySok1;@Ag翳50芦n(\9_s) [/~ӹoz<g[Bi@wMR"_uֵ-[߭QIENDB`swami-2.2.0/src/swamigui/images/modulator_junct.png000066400000000000000000000137641361104770400224510ustar00rootroot00000000000000PNG  IHDRTTksBIT|dtEXtSoftwarewww.inkscape.org<IDATx]yxSU$M{KkA@TDQqAGw\paEoAFQQAqEQc]kIIi >yys_g 13Zgz0GOmT06^I٧ jk(dM752pZO'54gbJzJRI-R4ՄuyFpE5X80#(|L\*EOZM^*EV7!yzJPT1W=oWUQ4Մv3`_|$_sL"7owjBͿlI{?T='7$YF|ʾ*;|{4Sj &?~,,ImP>0=0s}|6f<@>uzC 0˛^y$(0xq+ex(7ݽAhn!x wLs<n0IZiBFW܈ `ݲl/־e-6 BGw5gSEa뷃9KE |հ/n9'պK嫆|u| !v;(> 0PvcPfP]Ah&r JlBztsg9nxףOF`.t@`17]`K^;faloU gK vÄ5UJ5O"b6 H6?x@L0K+{X!Co>)h.d 7b/3j8=ܱIa0͞.'mLj{]{NSBwᨩԜap 8)spJ ưCOI`ߣ\rߞNN~_9(ݧŧ ЩѕnmH 6AnU ]gC7nckUykeO9N2 ]kbbǦe;j2q)hwŦ9Un%a0 Mu{ݶ:{e]uti+puIl~Mu)Ъ/D]RxnRjYѱiQNw q+ kMcUW,uUt%,LȖ8:{Yܒi9%ݓ+$UtK: $!/Ns,`@Z=S Svl+*Nq&&/N4)% %&r__.IJ )5R0dW K{ffcH i CBXʝ GR5Ɩ2Rl(*{}[Wm}0SRJHÀY'[O.S2AJ30U[ߕŏ.}GƝA8VgѸЗZpYTW/5Սue "z7f #3}f ]>ﲂ KHO3E].ڙ=?m%Bomۥ p wY62aW#a !Ґo~0tҒTM] 5 ]?"N9Q!cqN}_Ps!ޢr+p){˕jV!,na~-$Ru͎ᎎR&OLDB?R"V|D SӂL1蔘^)-_Pe ̝(G/0gOٓ0E Ofbf'fyN֠aWh1Sk;%guM, E'VtF. CĨܥ%UUfSxľ unWMRB,m@,FBB#(:ە:')`zx-D숙ahh?,?tnpX2Usq[NhBV}3*; !Z%͛mK8PP/^G^^r$(4+,:vBAB'GLcBRZEDŽvrh@a3b~WeSn@GA<:U^s`3G }gs-"T.>:'>*A,$AH0 .#uF+l6} C2 ʿV~\Z1($Yxd:A! RyV\JMNjFCnnDž%YΛ#˧nPq,l_vWH G|@&/U>۵!=&.ݙXeN$RboL3_l(F F]N:.}Dx L,1kR/IPu@1$$Eէ&fD՞߻c/ho|$$ ٞn7Bf.EɎGl} C E*1~Yz\;| _R/!_s4 tDB# @$|HWbO &HD]zʐ*6hBc?z-O\ iYC}"BmvO\T'I` I  JO2o {y@ã2x&9!m0$1!2*Te/)bB;!e`Z;m{070E7Wxqp$C–ڽؽ&I C ""N*V 9"b+p8٧obHfVBӳWͥ.LVx@}<ӄcc3SL5InUTi5k ' I>,Yl&Ӂg /> BX(bX-J} S@O7)>/Ooɞiܣ)%E_-" '@jP*a&#ŚacAM`bbuIs%d >If09Pk W-#-I2Y3Fhߞqy{X~zM8)`Ioǜ fi(:4 <ۡ\xS7ɡy1yk@\E'K?zjniQZ ]hG ztvO3q]sr(l?EE^Xb4k[TKMInziMV 3:RRaS.#TW cSYZBZgv\'`CWKCJ `A@1|  cDTº.>Ⱦp>x۹ @/ÿb  ZzJ06e"f<-!{005d,Zz~Dd59Y}WZvy٥Fէt@M7Que0XAeIҷO`Έ \ SÚ[+5GJV9R+sck2AW"h*zH*Wlauq?f}f^D7QZ+.WNl|m=!%*o8հ bŐB74i6]%5ؼꂄo>_0NٖzDGElg|u'{lCg{ŰX0+UF[]c޾pTr2%v&Zx7,UuQv ; Npk~˙c5Fhbvx6h;aǿ kpAnV1tc؉wUWd Y~X`ǿB:%.>8K\]3a]a]a]&a]wa]a]a]ja]a0@FۏDm?aЬ}*EO"0} y#q /`G]ѹIENDB`swami-2.2.0/src/swamigui/images/mute.png000066400000000000000000000016741361104770400202070ustar00rootroot00000000000000PNG  IHDR 値bKGD XqIDATxeKh]bt$T,+AD+(^(V](BЂk7& P@U+b&4C"YHB!I ËB|V) /.$˲ T*B{7xc*H@&˗07W* PU*˖Y ȑX wymuR[[G% 57{7999 iAfE[]`iieE@N!9|> X^^Y)aCPUMT, aXxfSx^ؼ9$I~jttdD7bhJqP.w00px6ɨj jZ.͛@>S6+\,jl;pB:=?_*?33SSj/wwBZ j l559N'x~{;}{v<S7>>1]]i I.9slڤieB `Baa 4hiikM4UgϲY4WWuƆ>; Tsl~|!,|ȷn  133;[@4=6 x(<I: iܹۻotuB >}$um6ӄd}ؾ=N&Ať%H$Νad3G.^p4mqoST v@eY(;:!rUr9v> ˖Hd۶-[k׮_k~muU52GG3!Ls͚u@?4eD=v+ ֌IENDB`swami-2.2.0/src/swamigui/images/piano.png000066400000000000000000000002631361104770400203340ustar00rootroot00000000000000PNG  IHDRbKGD XhIDATx1 x]- -C+O R1){-lI$yˏr-ͺyM @ (Q/_XH"""$Iq\zBel^IENDB`swami-2.2.0/src/swamigui/images/preset.png000066400000000000000000000003251361104770400205270ustar00rootroot00000000000000PNG  IHDROc#"bKGD XIDATxc``cϛ?.0y֭""87,??Yxȗ/6a3kc bw>{ :(A,022222R.N`W["r :Fp{^IENDB`swami-2.2.0/src/swamigui/images/python.png000066400000000000000000000040311361104770400205440ustar00rootroot00000000000000PNG  IHDRw=bKGD pHYs  tIME$L`IDATHuypƟ#}B$ !!"C'V:UlS[E;NTSimQt `KJ=@Μ͞{ Ƙs'ddhy=vfX,/EJ8Hj P@BY'p@҆W< n08%MӖm}X@\vqd ,0'5~{Eo6KΡ̄nn:3|GVyLkHi0h9  zC8Sx;V_T}3zKJo gg`)if";a0)++G0y"D8<>yyɥ ?b2F>(gH13iF-f/4'_-c_L{pd+\4OgQ8E$l"Ć,䕊n@.@_ME#Vr===^,[6tHǎAJ@VDDD7r{A LV>X݈d癧Vzq^92#3 bYxg1lَ~1֦۟3Y*fx ۩dJ9,qCtWZj¿/Qנ_  ȎPW ^a|P͹,dT: 1Eqխo$[pŘ@5$j)?N~ MZzd [Wީfq6igB(FR{͑v؜UV59Os`c2SֲBքkwV+O.[A1%%܂a1 ~Պ֪sWM: \z\fdħ$ bk,=Je%娭AEE/ёEi e)K#W~-gLFօΛU 3Wb\B*Ӕ,Q 0_wdېPQQ_ݹ߫o tz@' h`߄T򫷬娪Pc@ 8FePƀ:9;I8m/\bTHD-6jN;Yڶn.*~'E6kK DCy\םk㣠6}{o򢢲OR}34>VP&'WWݧiЅ~|%Kpdq(~\wd)? DGsa]W㭷-oYU-٠:)gHS5C{0y'  -Tu'D[@\Nnjйv[< 0G.2KJRy_|aaze^Il.GZZVV} v(w ex{KJ]=R?]0FF1KgIJݖeB*^/f"xwwKt}rZ051y|teHƖqj3S&T˘LܸgמI gTr<\sM%<Ig{(3.ni^m]YŪvJy=!,=&/\5UR t8~rС^xmÏ_?ǟ[UﺷtJrB3ZcO@΂з; Xeφ!"DQ #NC~_8U].KeYEVX< S\\>]xH^o>I>3'(uiEyuw7x?3")] 9(<018 biC%nm?áIENDB`swami-2.2.0/src/swamigui/images/sample.png000066400000000000000000000011141361104770400205030ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<IDAT8=K#Q;w&3,3(al#V1"j)XJ,X [!6,bNq2EʸՅ{9T θTmh?PBA\YA(X8?@[Nǀ\.zjUp~ضՕdc+Q^VF@"LzJB:\^773 ) >tڥ\ְm])#y&TB*߸`m-F&4 T67cVఴHh0:ZcpŅF:bY>;;Q::|Rg]|ZOI&=ꜞjVlx| @JkMN:T p/iy OmTyD##e@6[}|3 d2.o*x G!>0IENDB`swami-2.2.0/src/swamigui/images/sample_rom.png000066400000000000000000000003161361104770400213630ustar00rootroot00000000000000PNG  IHDROc#"bKGD XIDATxc```AߵksFf`&b589LB8: 4 T( }AD (<4Q0 J 8,bҎh}8!-f FW„IENDB`swami-2.2.0/src/swamigui/images/sample_viewer.png000066400000000000000000000015341361104770400220720ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATH=HaK.BXbł jiA:v.g!KJIv[`j⤐Ղ~u^s1,Y{s'7 aL5Iŋtw~bY\^bbIelXDDVW`gGƸ摰؁(-uytMb"aG4*spK;oӛ7YZ[ 03dd ? 6:e!<7o햹g>|p]Ɂi^++vt]Ww`r֖ޞ<@<M3:̈a?j9.޾-0~8NdmM$̓3`qQ%>֘׸w/BP٨L"I1LN:PihS\0FwStt34TH(A%1 H ~SķojkujkTV&vHTU D"26ۏy0۝1|7-:<@c3a@uXFe*> NNhm O<Jt|So/gw_i٧1s;ܸqKˮ@}nZnxzwї&u;2T {f.p%E 89ag&+ n;w`7y&mR{z Ƹ6-d eF>܏ݕ.{`|:K{kZYw"B]{f5bbo#&`hX/``@o,&OݦlR Tj3E?N_vܪ:7ǦNvXE_T2*pE2L> CPPWo["m{IF̭yrզZw蚩x剆4^nΩ/rv}/v2!9}o tr{cm*׿r˶n^?uo\ۗuQ%D2?_c7u )j[g,cQ|{jCV9udI}m.NUU%j9ga1Q*-{ 4exoשsjT1R0Og;VrL Mt}lqSn[cbuy6pgI?OncU zG)< 9MH4uU>c*n3B_yIk'}g݌||söMd cuƾu41vͶnCG0lvA ҵDg0ma#9~N昃Y& zjq6M1mJ4i?fsᓟ|*jLgȞeeKֲ#u@<3u}]NvsGJ}72+qqYaT[SZ9=scfYYػm h4qfY{rn?riv=TS#)b7 tcZAxXZV;q'6=Ϣm^MFn[]%|etP{e\4qo╵g`Fǽ8O8˜Zv=0=rmΏjsDm@;D֗ar_ڜbɧc}*caz#c ?6|xzM9)`|yptt =FtچO9~Mh1IO ޔ|.̽; {d6`DPu:0}6YmZQMt5~_:֗M*S[jsan"9?c)_Wi[6cM*9e/cgM4M k ^|lV駟ի X\1TfQ+Z,aoz~4++ޗb2x~so\{Jᛙbϫsض8/fjoU?H}:`!-/})f]~J0wٶr:s2Ax/}7!⡇D5xЏ~w޹3nSl0DVI`)dzI29xe9+Ts+hg]ڹٳVM;u(n cpzGZN ?}֢*az9e[%,C\vL˶r:a6,(Vmo~LiG2veD,M7\7|KӃxEI댚e j|ȖSi^wr/Noʬn[>}q .drc21W܃O?믿ReۓSa4^ggq:G6}gS`=~0q0UvhoZm'i`?]iПb@ߒA~Tma3kG-cΝ=-VP˝h`[c[fz)yZ~_wpu,-8S-rـ8M=:5o 웲)ֽ LD_ae?7VBX:+7b݈s3G_/u؜;}L~9L zt@#/h(ƀo̶vۋO>{O@Ul=s 0j+Nnف}̶=QO6ӳ-.oǦQQƺ sg }0/cg0 aEk pGG''~C{gmS2:`gym4-`޾m{rʦ6]mg^"0&2* ٞ/WZWy-xSFEnGljߦ>{؏}_x/d0Na6еvZ du7K}]L\eی!bp`^mln>[?of3`? ZʬYsݍ7rGjS:۰]YTfuk;;lmγeշi{۳u <}h }zL=-Lێm]lv:qr2_eطg)m`lUy|?ow`]0g;׮.48g{. ۴]` ڶ1L٢ `ֳm>ew=po[Lweշi{ElVPM:[? œa~shc Msط=o`춁}~|xgwp>b go~W>A]%p1`ٲ]墰ۗ< QAϽ/ֹo'[/򲛄*nԻ}[۲<}[۴Mk/ ?<2bz>987^`_};~] 2.M:wYN;W0zlxkxdEMqwe?NN&&L|.QgXdZu1@+JJr5+Pyk+g#tΠe+W.)4Mؗ1u˗/_,}uk>L;?B4[z~ /2^{ sOnlwecԍzbc@Fe7IoI̾Db^h, !UV!y ?:H grSsV=\GGw_Q^$`E_[~:c$x;}=[wy_7;d5wav 0Ơk8>>|~2fWQ3 0_mlGr*}Yy-?1#"%(P#)%fe0G.r0T8S[4-Gqmڏu / pttrd{mWv`c7O#Pǹf*O|#xG$;RLd c[,D JK@KJL 2KQE3k:tFp>ܦ)CEҼAY ,߭>"b$I~}v3*mv"SӶ hۣ-e~>iA}<t>I@t?vk3+OFdt e+J 3[co:$mY,xۂ=GHTM rũ?YݩߨIb2QY-71FG,yEyunťK{|y7nܒVo0i|;[o-K%s7Vs10ik>60FX`@epLlWD~UԝL @tZtbJۇz{)? >arZa}* `3,lxԔʔ}^e@i}w޶M;[_. 7>{/x7:]D`_108zZQPwYf2Yw-WH)C(uN@|asLLYH5 |Q O`0]KmGɣ 2UvG0VΠd7RJN "-|9hKoi}_F"ãc':8QA(|OEc&fG]ڰ}]f]reiZU\6B1\@`?P׶15Ep8k}MXZ1N"@c@ #b@A@$hbeD鎔#ۣ,$>->(;u tIBb{5D\)j ׭tjQ)'X/ 윹i ?2B -B6T'܁Iμ{\`춁}~%Ÿ99I4SU {wحu8("ڀߒ|$ H"wDr B8IHF {12rrNAL@4!*ҳBU$@Y@!,*+9D'"O*ðC)Y xz?&^?w鳈{s CMgOuSTY!1wS<I$&tӨM!tZo,ʇ v(5!2I>+2nR1S-FFV*pW.J:&`~1MWU .+a.| oZX!#Xu@#AL>!"470WGm(FA?#DDQ!E4 A@%b K^¸QJIE]r-~(T$Н u(<Iۈ#Q'gE zeL7'KeII//G\Nn?[?Km.@}0~`AYFi mk s"x*e<2{kP3Xvk`D5ssv3@?f0вڈ'!/c"|d~@F<(Fet.Ȱ eБeAuQBf8#¥:H9z(ŕ"R9\HB:,H鏉;SMXfAWr<J#/Fv;_,w+] Ӷf/`UU\`0e͝ @P EmA ZXJ\2ш' IQĈIPZ]V) <`|!\v20TBllL8YϧB7$TM=zotF$wm!D_<Ʉ'hM M* dHK@OG "+LZ%I~H!T>7|^}CI~-I3Zg"7 [DXoS9!dOdd<7I>).R]v۱oXd! eCJ]dY'66`L 4D6RmZ/sedeyHTvWkyb4:0:}\w Xgдup^qd$"ywT byɡ(XH,{1QCmD$x8!vH"q Bcts y:)dYCԛT8.>!R.@"΀U 0$%t=}&ӟ :;Ra6rW>d]4(Doceѐ0y! IDATL VV ,D)hc;4'Tob dn h/N7``bdb4GyoQ)w6u۔ցG^! xg$_X,-!zh6iCuXV堖s1  ğ0G  ʬ˅\Ҧ)҇؍,bi}rq*hAu:o#uk_]JVDC҂1dI Ķ}~7fx2LKAdEECy": KH]鞊!:*IK I2>H9BW Rym,RPD2LI_+&;_Y'=p/~|k_h*E,b9ʫem$!mDomDӴidgB1QX|# *aCp CIV2S$q0H KiIbSeC*%egŧч&ҳ:=9T_1^$`߅K]feQ/6e:|4?,P]MqFKiG <'"\: LnlyW'!M*ZD7 TB%?;BvE8X8c*fWZ]õ\Q^9\4`Gd"˹*˱{Fs0`!4 Mд- h[>߄ hC@"|B x\T.RMBwӵis"x; 4y~6@ʼnB)x(AlK$;)!d&NDuz;.@} 3}l<܋F@"1;frE:ǩdE8KJ#^NIk}$Џ䈝 Ydy/?b9]洶a{h fJ.gBp%`/_KfxG/I^>T+/6T8۰"#cd0Vbvb S8RW&#sH?ܮ|R}&1LHDmZ ʄ!2$47q|9OP8w33(;|<rqI:)Eh6 3E~rlMȡH(<{\";IwJIGyuD Ar$d^O?s"q!T֋/cyޗQ>J̧8RL\3 `YA䘜HSb^с\SJ[IԆFP+e7@ Fr#k`r\@rܾס}tWWUUɤ3aVYP0Ji,ImӢm[4s]$%~~Q+X{zЅu/3z7:t`GDr˹~L( y,~ȄTN&1 U| _C/ _<~g'xm;IY9'?"fQ%?~,r-4킢1 B^{M;&O/}i):I?$?tT\MrᮂjotPsF>ybtzbeD6/;Z))ʃc(OB"a9xN~IeU%_{xgpz#WU)Wll&4M+Fbʤ.q98e6kg`'$(9Y\k(EsD D)u7dDR[.;GG\1UxHޕazWz+g};k,_׳"ב4u!+r/oJ=4:7}4C0:&]n+LqNLS9(P*EE9xSO.&k$tɩCu.uʭ HY7Hi8ڇRf+(r$O!(KDINE!([gyϳd|Q90|#տ|?ew޹/wwYp)lUI^Yʈ9Q/uԩe΂՗a(> *.uya}]Ȯ/0Gcl_('voeX>1~06̩!`jT95k:kΠGB@}5]*y{BD#lm9N?k-ڶ Wȓ9O@cڑ` cr*ф{LrݿQ)u#e`\/bGr 9ݺܝ<A1F켝>lk&uAzՕS: Uur(HҨggxN Q9YO ?=]9뾽!^'Χ P!Lo+1j8rj27Yko,wTߗ\>]wH#Ee%V1{ }eptx ϯ!΁>@ur\S~CXZ>;c0/4*!&QMOz1Y'<|w)ƌuSǾlWj9/[\2=~IצIڲO3gtXRou *ݤI>2]{f QqSND:) ڍ-s+KgtenP,z˻U~ƓtIiئJ92V?bۢUM]bfNO̚"GD#QxT\N]~w K;%5|hg`@}~:psnvimz{-˕И1uh: d' Cz!I|B(zBKk&ؐ0y+B(}Ot|,i.]Ht}C,EIzYf` .aO0S-lYo9r](hԕh I @ R-_4JMi׷f5>ŷ=,}չlߚM {nwi2,']ljڑ0Dk,Y~ U)Xc~O$bT%'P!զ;-@2Bҁ+ߤс:`Qw6TːkmxOβS|ͱ<|#&M V5'Bs4,n+[2FMo\US==&y:LR|K:2SBHhB볔FW7wё@0J#-D!#AC=Wpxx6)c_rˌ1c[nߋ_ş܇7o`;`K][zuYP~)n^'GJ'12Zbż4Q;$`O9}g,rX&2( o[ ],z+ko^|T=zj/|@:Z0iZ4s֑EZ$ "mc$5Ŗ|]CZT5 L%`]O'JXeӖ!O HW:f#,$͕O&Zey Hc$շ<bZ\Huwe?QpϠ> b`}1yoI>Wչl?O`߅ @ }b[L?S|Y'7AL^r ] e Q)8g \݋G,,ȉҦ+ [Z8ګ$6ߣ+؊cyu.;23&C hiX!nBH}!vͣ``d M?mr6T/W(a6I `|'z/ nܼ7)Nd==礏ϻqcW0[(cG,&tRx>oϑ~"%]H1ѝ/(U$ ZBl3KK-@_*a79-D}HLV~77y! g"WXXd`189;Wusڡkɻ H\1SFͶLMb1fj, )'WK6aDG?w~X4;6Q%jBtg Jt~|}&+&MKriS?3ܺ~iaLHR$0H2FD-:t 94,3LKo9sf92^\OTS+mۅn(`#?vr% 3O euQ(5yN2n}Q;)p.Vs"}2WO$>9|3{ϓϣmO}^3q۲0!^}?-,oFɗ %؟THgyV>3rn{9DSA>kث=[X ͮX{k^$h=m>Z4Τj6Tkj:ߙiuavI,!Ę5>T 29|dkDe :pZeܑ@:Cz>>]+=z>jqI"]}$2yGhJ̝+6Li5 ww ]x8 uRzWsF 715gS ӭ%7@Ko\1gȜ$Xc옐^ec9]зR,8fVrF*5/byGV{%<$Bi5~\ یA[c%ZwBbwAkcO6I:*}-ac bb}E:&ul߁Xk({m>i$b ꧩ(v6"9KƟD؄Mއ6}BT` }# BI7aAqy猊]I9[k-fUqrHc ~?|x≧ym>gPϋwm"lj[/m1 홲(k4Iڦ?X=(ﲥթ./fa:Wހl"9W.]Ye\2~=U=_#t# oQ9xIVLlʠɻ_i|Wyx fW\b+QBۦ9Sf4})F`.Zg_C"!aIbȓuPwv*>=B95oƢ9E|A|C?z]ਯ|Q_=ã"+T)K:2lON}6Z Бh@1,-XO&f#sM8NK/} _§>1IЧ"bBb|@j4|uȏ=OP=~>Oy_3xXi2M5XZyIFUgl|buC9f+XgQj{W9)v*ȘwYS$qswzu7܀: ;>u jД{ۮ*lX,\vG_Y?+_ſ m-=<0֑aL1Yַԗq`Jr ?[8::,s~ߗڶWߦS< }R地b:77baq2; `SNNmlal+Lt q& NZ1o$-մ>`JΌ[Ro,cuC^]D8S==@^ ) Nw'v5IrN,'ls4>ˤ`vO7կ^xe?ah#p(5WN0_vԛ>Ov*;Ŏ®ʓHe|NL A4/;8=H)&I@VAevϠ-_oe謼Ig&zfG i9|%+m9yFH6= 𡉅^q-ff/,;n CC m)>!nߟtcv>}8::o_/coo \f=Y1x9Ѷ-z"|"e;=oh#O|*'wKoQh IDAT˳B /H8Zc_FϤM孀wY [WeoMIf|8,0)~AUUf5HdTzV 5Dde2RǏ)r)gi`o3O{{3O<O=<|9O| ]wo}{+Bu0]`e2 }٭0[nD)Ds8fGnsF"*_6ƀD*63{('g&Y{7prr~o; bѠiQ\=!>%t ؗ;~'0JfF㓜v!:gk1k&i}iݗV@U`zre{ކ33اUIj8}'zr"(kx 2ZzA}畴*VY}!+^x?aq2Gu\[gJ8m SwjR8S$N#ə/i@I{IӜoBKLNBu9ewy<n`Xlg)cxыX,i3 M[zC;E|z!JyIΛq!m(nS3,`]UMm}VU>>\2)[l?Ǯج(GXmPȗ2rp} oC}Y`my,}GgTVen}CJGzyDT1sxgSd]>3ϼ,uS8c x~8%q6*˗/!XHM󢤣 J0MC*$6!;!xaco >؜ M&Q@O.<3lw"8$v^Dg*߸Jv鬅B*o4YJI\IJ 9&w2X<"(o3 M@ P<$} Õ컰ӷR_FѨ}U{mO]m]g/٢b0g v8~io޺mG!5"kղy+`-4z߰ `Ofy[;8q;=^^V,Xã;MQ7ې4$(;V>hw|fmDl M`pE+2Qʊ#DC2e*Yʞڶ Z+W.K4oܸ(doX4Q cS&&ؐDVɦ</o-ݪB*5偕EP }KwU%r/8MB$ONZO-) rCs>"e(J ?!dT@qryP,<▿OguS޾ ?ecdt}yS#tUոdvxov:p6=[A+h{Te{$479&ӒVod2-Bt!}'aYzj{-Rod o`ҩvy@RTZ [}zNK!7m۶ fa^/NZH7Aıd9E]^7qzdW%Uy>[5,wZa.oMۦ瘍UUB'߃}pΏ[@{ffD-نcx!s3U1UoV]WM\,3mgkS͢xaMck}]99iFp>k*_@b6#SF pFYglԓz I7a+3()U0jc#h S޵ߕaQv P_Ǿ̼ti? ~niZ<~ O|z2aw$Oޱ|8'(kB_$`w 3| 9)+e+E+=4vVl$&Uk, N+PkTM秴 bmHsB [ ͡5Y᱑:릌R/sҬBR. ߕaֱm:SjIPbbh+믽"kG>[ppp?'q||cDgw)/* ]m}\)KIQaz/\o6݊It+-`FIj t#j\B)CQSy xVmp  YZ;]0ʣ<\e*D,spJ[ː0} d@`M޵-B{KgB"d6E\\ʓOw1 -+'(_r}6͂Aw nFG'?#w'V$bDN)~X+&wZ \6+,S*i#~]: 9ԉlOh(3zCyit C)cskϫ.&R+x+A> 6V-l}~V8<:f^MX͘ % #vh2$@oAC@X0=^K`P[IW40"}P=I?P[d2kc ^ܼ<:˶28:@n_R(sb~Ed>OWXp΂:aV( | OrCL?˘R|1sjv);b xs<ҍ}皗)8xϓo vl`P=YpK&_H69I?""o%, UF̏'fԐSc` TmXsBX ޻U˗7nZ}٭b0TsYC@Qg۠O~ O<4OAۆ: {MfGzq3z7y6N=XkuUW^דNz ~1a>C h ]&+C_̒]@}@Y}#MK2ww 27" k "``@DiECGFU(~(>kU&l (ee[QRC)GG'Iup0fԋ@f^>g %oo>㝷uz{92;9L!,g4L|hc*?&wӟx>|k0p$kt?[Y(5z!dTa:uAK6A>H$t[ L^[Eqm 1099׎ q39)(`qBlZ '%>p?g)FYH-_;R3R{őM͢׾ c4mȠ;ד/Z{I7uX:Xzټ2yubM9.jNHVG!"=b `CfvB6)1oY8Xc጑ٽ nf94&r+^auuڴX;n:Ok[/>f+ґ7zwGL-ӶW.B ʢdHkȚtٗa]0TWs?8FhL$d{K{V$;j٦ r:xn)?n/k| :.> U:[H6Yuؼ-U[x;oz _+/ ,Z~xĦ3@ULTEX0%ٹXX88k@}Tf}Y wiW9dH?1FrXi;o`aVzf`2\dS}K:\~+S~ I t~~~EKܒOzĠ[dn E[^̲6 [Sp-@oRK9{}I@_Γ #1 } â+]k7WyAՉ{yx?k uWGʃ6GTV4Ϸ0)DrBsۛnzce>cgP\)3t~;63} )cP$H%'n4E;& RГT5`wb$`Ppu.] she.~5С_8)sY1QgͷEDnmڬ/Zh33 /U,*I˓"ldw@Fc gy.#NyΜci@1» u]yvXeCߗWY7=_fIZ;ӹWW_>*Ze.Ȉ褢VB/vRR'w[#yr.Sƛ'CK4{q:nFC60/ϯ8M_NI!18R5=O@[fEjjv 3 ~TpmDg#KUq#V9"TDU\kϡi(+lqXK Z2IXb '2K%b`җǧ}>F2-[֗{HQ63[gCeg$Sl96} vw;OD<ʲ2*kRI*Uj틅6̎OcfhMt7 g0lnh< C70`h//XY*IT{U֒޻7b~/2Seu{7O|7~arkOAHRu "]]n51]4ǡtƂV:V+yHPxPEO 3UbA1JR!!:.)tCŠbEgXOu.BUwD%P!^ڂGKb8!|87{9 @]d[mq6iPa&IƪuUq|)tО(dM'=^{uC.,R)ͫ",z8D׳2c^[V!)AvKk•wOyخ WJ+UJAj'bE..0-|lɤ|HǢ"?=%O#ltJ.y#xю\ybXwSba {=x?E5ЙP bX(PB.@Ǵ"ON??:?pvv䥿x=i`]O 0m2͆KOu=SKgS%s+±XpI)PFz0@pAY???VB߶|~KLV+(@a"C̳ɇv}1 /D9hvINbE0xJ0i4p3ȷeLWžx on}6 a$|JFĂ/pKXr}.UJi. q(D)Ct=aΠ/;s!yP!T~rn3} OGuzÆZT81Rz"@J"š5ӘY3g΢+h'Uli/2q.d*kAi- xdc)4]kB@'hE+A i(l5-9)GNYk}yaT\ L:ukJt]%wA\w(p0^ 7/QxU|7݋O|c?,Ee&?gΜW+g6oބNq+zVdJ,av0@J іv74LԯW畮$_RzkLTF)y,yv;i~Dd<* w {O/|L+RQn^0wnG^;ϣRZ¦qXU#X^+vܟY*FuBsQٳ ?"N^{%6mڀ;[cvͿ4jLMO~}ˇ|ղ3uY& XJ:n!=Q9i/Q^ׂ̓5ķ v̩f/He!pNIuqk69XVzԛ*ع}@Ho#SsW'h 2o] ߌIVk{/.4rt| 5p%wi;dE]JQCp}G2ԫGm0HU{62fKyـz؛hty|w|>.|VWgLBRPC~#tu VKT0΍@H}ɇthQNB!iJ䃹o Oi9{O}% O-R ;xt2j؏Ke2vW]vʬdU^z5yL+Tk5K;[yK_D^ݻw~9gΜyW os_v ~߽I .;@$_?ވ=Skz#1t/ PWGuC'm:U`GX[ns\TuyTɪ.UaTZ`%R U'x_InAq4RtTbGkMq٦//Afi;c.2xs,ykE-}tubTusߓ#W _f'+k꽙?+3-WH*?O3.8m!ƍkeᆽgࡇ z`8wn Ng,[5:%\1YݥgRS29dCpq!9&Ñ%',8f$ٗ, Jo& T q&3eXIO&, ʝL*Ľzؾ}+^}u}on(n:=zG㊐ ;ĉv lkPҳ衧e@e"`W:~KAos'2У*_*b)۔'Z?0͆uVUCz#RK%p)Vۗ.bQq^;7Pg@`S(>RoʹZF9"]|CŧFW=<2tBTƂ$HW^;5$ |Z#EA\//9Ig۠NGL#ln͛7bݺxq,Ξ=:MxWZ޽{05ѣǗ|YYL|;C;S%^Qbɢ]\NTuVA 'y:Rv7WfvIGHBt&iImiu?z_&؁-ThkmQf~]pb`:0??ߚ /얀c왲uQEx/4 DC`k6[EŴ ?CJT077O 'Y UէX6`{'m/A>]ӏoƷq@<퐗G,odu#:~&R oãƯ 9li76ΟF_.&(b D{xJ$ʀq:*BoJ]/3߃ d!ZbCF24\P~M<@ԉ 4Ab#,ki'D!5>Qr]mmP?VWiLp@ǥa 4NڤI&U&:"*MM_-"3>$#H H/.#bS$GN] ؃qFؾj52'A ZU;9=;1k*Y*']*b}jtxGȗWP0\8ܫT! D|kI*~[KB:#YM.}Xf\J]: ^=oi2NQ_/Q 5:*u2J@J/AGE<ncm o-DLۃd~RyNyVz blnaz0sv2x !^ C!v+G]3Jd!6 CgX 1`"rȓ/y*Pgh!r=8E e>Z'F4>܌դS^jwW ;)VJu [+j]E4 X(%K$Xaw^ԠO <?GH OoT;T}GXKMa.S=`jaG9zaGH@[?} A\Gs=,qG87WzDKڧL@lÐmj]l .9ՒOC@O`=y7>^µUūI}۬?_K<5_^8^]ZY8 QhìNZZ[%j`OqZmiC"U㹒NKp(3^9sG)=BX4iDVԽ_:ZvvlKp.Îm]ź5a >ǥ^uuA#K=Q GN8r|'=e<(wJ;ș޳9skFW܏J)phتI@?( /4JFR:.G>=MNS!^|]O؁3Z J>. s^nM0ƃ>E}! RQ2SNa]2 ¢ΣRw2x VXa5{ƽ3r4.]sXv/+j'OW8z%k%ʨuf^~{[Zz^혺W1Qv\=t_} }UD1l2ê`l,29?+/>-&hm3Z1lfK~8E,K,=UQ /=[ L*y_E;sSpܴw-=|}c`\7:l@vOwCY_+q _ׇ(5g//P'^B|w^(1| ϞGY|_@EUAcKM#l hT Z5|}xԦɡBh{ն{WX ;^~q)6X 켭fTEj( r;~I@ IU[=Z*10h~O]+D՗G}oD8•;㦽kqUk[ ] p-EkRC:Zo3U_x~ٳqi . l$JW?0/"BQ^6tz]*Td7bݚvbJ ػ]_x`'/q~j qT_=}I,w 1*%;%ف%`#j(?.Lݲ5epIڵkp-7`i^}D]ačV^N6b6qv,^nהwKZZ~%<26Lg6 gpN>/0ÐYm]T:"7eP -lo{Sd7oތnAQP+bjmp;s=W+s {mL7'~ _!GŮKV/EWpW]}y%lD 9$&$Fe>"7|rg-8JSwҀ:N6UƋWkZ߳޾3kGO]`_?U:; KڊKN(P!x*5F|kb{h>u;C[FVj!={(طoߨ垾7Ή`ϡ pa7Җg_ 6{{4L>*}b?.zjorYF=r'&V@ۑz; ;Moފ߳l^P]Mp 'gxW 89{WT=Q Oj߈J''ר+kw jPUo.;xPi\# xoH0զ9s<x%:&ouu imF*ūѾtsIӌy<طo?fgϢ.n{QbX5kXlwY\Y79$ZJ _NU u8ywx_]7ouW38r(=ϣc~~~>A+2C z7j:ppäҝ-@c 5=|7]zvl鬪Z 8v(>#G8r([܃1 1 q$K{czz[m[ulݶ[l-QHcff]mIm-_=V6-ި`wDXn V'm4LmP +ASJZWO݁;2N1*X ĊSy ذzeo܆5Na577ۏ}zGʼn'eo'Ւi}!ʡl9"ƍyfl߾ v®];155bߚp>] %B(ҧ/)_DtO`'cx'}[33y}x P?ڢ>ٳ=mk׆O#l.kY`T[B$ q~ҳX IDAT;k)aZbjoⲡQD'^(-:l~3V뽸A,{m{ĀS8uj/ _!.lvم={vcRǞuxgs%){@h{PT%C%Fgz 8ģ(y$ƊriEK>o v -4~q׾ZShSWX t%?==.ہǤʼ- v?ngy@7)&RoiVJ4"–-wΞ=oqȱUaIiaL6RclKct5 E @^@+pť}߱M(ƦKt]'hMOWd܀a|Q0m<s0$ۛ<ӳz4T{ũ袆bN@ɵ50QZj9Ȫu])yY2 Cޅo~˶7v=yfxᯠ kUgVT0_~;/8n^nW\3ޔ7\9{tu17D3Н:IevUCWOyV!@zDTճsPFG&kݕyV_jN t?Cq9zp,>/gϮ"Ɓ]Ͻ q#wnCb1'P -I|ۥx?u Qv}~|q)ٵ{'f[bqn獲dr{ `8,Saffݰ7p6o\5nzvqSx")t {Oh⭲ET٫5,K\*Q3H|x{cs L؟ʂp'Otya||B`7e c V vr]U6-@_jă2ݨMN@,s#o=/3yY-t1ߦT=zk^zs[9z9̒1H0<y+~6\wXfYb;qnL n*/Oo,@!KQ%UX%;*]bB`'$?`kIzu\^>?zh")y.Me?n o7tK>JW𤊼nѰ:@#Y4>]%"g |03SS=OK| r8כZ}+ב'^VӲ:8svvV?v8u:Es^tؽg78yiTN\r  gnZoفgaf~_m>q|'Sd*}`s;g!ކxҽ~YX5}Ӓi}_/b+p]cǥ,zozW'@(CT uX4|=4;r8(LcTKh ~):<%U.]ܵ gCĞd` W7\3$g=է7{6WʣIQB|}Ib=\]kwV7zx+Kqby%M*|]pÞ n<9!T@T<[prWL鱋aiJWT"."%,r$i)krj" OQմ/NOz3YD`OYcH`oSkTk)ZVQ-F`'#Y2 N{wK_y1|죟/6(ו8%?m@2n[n4Y|{ڦ we4}iݤmq }6v-s%n⟾:E=:m'\I%}I>h$G! kSͩ]ʹՅE]҃(숏_^T0`h>yM$ws NTZiZ6Rm.0],ؽo> knxJD}lǩ1M/QM2YEޜȿy\em=z͛pફ@ X̵'ؾy}pgΉ.CԺlu݃ k[&/o>({:|1i#Nm\}Zf.vE[My6So 6g{j0oZ2iBgG7~ws'vHn!Mz|b$V;8 AZmjI+{%m P:I>Y4$;4|b/t: C#u%m@;`W{nä0Ha&jE|Ux׷X~9r=!/M[3m._Rҕ{6[AyBKۼ#gOgsعr{ݘY?h? x]Ԗux𩵨RTDP)Wn&FДM#w}+߀ |8۔ztvPU2i57 [Ǒ h{A}:". ֊0AƁ=z+vg ϪwlOZݳnI`?'0Ho1zS;rGC^p>fmESw#YUz;A?nvyˇ7ztMp'7^y7osݦMƿa]}x `$E<* b8F('[pWxᅗױj~-~Z٣#/Nf؛0);k;NۖЈRbh :pE(vN;Ն)>$?s| .a-|ygnYHj>)vQ&̤LPKRz.y^<{xa޻pe;xOn]o;/= /$@RgC?3YI> =%x#GNرJWʟXКN={v]z'z :t$v}>&ؗu//#`)2}D=+K`OK.#7 aԕ )N|`S?K-K5Pv&q땮㔺\BUi{.5[s>c8x[źukJ [?OubgOMLSĞk0]gVp ެQk{!,jǼgP4z/ƫuMeV!]֍ VK:֧4hsn]_ qozhc˕"Ç !p>xQ V@y)|SÝw߆W\nBn{ S :Ѓ( ҿ|R!@[j03,_-j{Q}ʴH;Wp RR R@J?bO)RFo$u/t$g[FJ6 ;.;ߺ}K?\E#`z8(\J5:!Io0vML#~%K eCz~{$A7ј_m[jhe+X-SjG^_π|%e DITsb-0 }kos O`Wg>ĩ$}N]?3yf ϧ!TՋB2ueVi+WCxv\Wvs ~U=t}Q+QN]Vdا{^{n<1Cx}|v^5G[9x/vn8jSF۸QQVFNu%@,fɦte~kBa`<[2 vA 퀪*QywU`g3]98{|aAVh!i:2+@y$;F눒HOI„^[Y\eޅLv`Q2m`_jڒlzxj}PNrlބHC㙚HB!6HBB O~:`;{ˋsey`P+z޻n+X%c1j옸zJI{Nd :~Lz}0fI+TN 6,iQm&/%D+@އ`}j)p t\51<8} >_Ľl.6>/roe/=y:{;=^[_D< V!piiP/ v2l7KOGvMSظq#+%#0Ϙ|pkm "=BvkK˛vxkUtUWD+Ok7p+o"HKqA訐 neKɟwPAz G2ȹ.@*)ZEmSW_fFZ:M27_jۑ{|v@! xW(+,1,VCt9{BU({ޮ0`0(/<nFݫ[7m7^ρ\w.SR N5X!FտEhSz+Y.4|P}Ծsoa-Xo*z5|;\9y ~gΞG8FKzK폽 ۇ7qM/ }s_?FY\>T%J Up7oX7ߧ| C AsUP_M*'¤/aVS$~0D뻀^/U[I"7_ȒiVfޛ~ZC7Vr/ՖJx_ӻֿk \8(/,Źsa H=(T oWߌ]w.vifx=y9ʲW AzjL޵50HVThŸg>.zf4vqtfi6Lm-VCY9xlHr\ ?85A:/$F~/~Ǯí7lX?яCW`E-C֋>{֬AlpnS"H[yo W uP]ezJ0!pC%6n"1^sU *tzy4CcK6-45C}Eڣr+@uزR;:ݽtbjpПlӃQHs=ګ'Wo<;;T#%=H#/!SIr kGC|ᡕY')/q^ zz 9??gYIY6FzZM0ǥ-M9$c؈ &kjqR:-RZg!C~E5o{Dǭݜ>8}e3 GiI4)ŽKn Gc`?ӿ;pA]i06mi6D󳭕j|`䐣@zbF*xLLTu؆Ajj}.{`owԓ 5"#TJwejDN-B*v,6`fghNE S@5>?"GRH>:f kB؄Qկ? e#dCOTѓ(!rUW+%lV,i(طY2ik0v %h>[^b^X|r^ /0vtݱ`y~b {#wM#jJ'T,BaONd'j}*lt0"ޖv`< DK"|00oJJH)@BhZ1MKup^m!ovv - !Ep0|#^pź lOnӜ|#P!ae .@AbӸ+)c՚beRqA o#y!1PRB ^TO^tE)cX=ն&EUxm΂Sѹ܃}i?gOţ?Σ"ݶ(_–-f{w.vb穀>9̉%?y_|tPQBRNfv>ш+=-}_nEtZ 009>M.+#ΗUu*YO=)XeX޹?7KQ`},cFșxG^}w5ȱsD\ %Ʈ +PK2j)A!pc03HL>*xU5D9޻(Jϟӫj A-þ|l=~ަ6!$ͥ(= MJʽy mNǾ7Z.]׾.;-ڢvݶK z=ȃtOV ӄ-Tl;ǥ-Z׉4Ӥ־c/ o@L' qHFVY7_n _y Xg9dT偊FGAba.jg9HuNo dUY\#o߂+ juG.l|u$sEJ+BsiFCPV2y|tv[-\f\|;}BGOn#:Nz>RsFؑx}&~{X([Tk|kt{9֦MyLÇ(~vxA`o@/4+|~ %j6K Xb 8pxEӔUH{At]\ 'qՎpx(Ñ4΃\'XI9!NB o٠zr!*GהO+ޗY(nN>v{,R$<@JkTTVqQN2vlt;ބ]rl ğ=h*ܰ( 6*Ll&@8euǚ*AʋANK5,?1_49<<*h|W~T|F+S]T`q@GE*t\zp/$-&'\@>JKd;R[lzo`ix?nhZ?^spLO&XlHV$!D nf̬[qӮ W;}t>A_1 @^CUe8u@LsQjO+uMŞ\)4g9zAZ;k$;)NٖU3 [wͷmgΜ1?ߏ`O޺ 3`A3+N y5 JA4ժGANwc{' UBˀ 5(,5ԕnR+@> 'rm;pC e899 y >7Vl(:״_YtP鷡;6T`8R.ϡsC9m fe*⼤S4_i2Z-]0Ѝª4GK]W ѡ6=dR lL}z.D{ۡz|HWo vҗp:>ۓăM賝W|IԋΕ+W╠$Lv`d#&}efZsBi4J"vZ['Ă(YA-i?1$L#fKw57%9_fUs}ᆬwRKj  X`b0c qbb<؁{3` $I*R/j[眪̜?˥N9⾪SYKVV//rko'>ӾtēAe g}cXk2j Kc R1b_fJ\DjMؔ2"=A^RoEK:8@ -vsO.},,ˍeh;p8#yjヺW.X7L<,@@'as)1,c+ӏ?ZW=!5r1w$oz,49ό# SgG[!I[^Qz^M8 BȤ MH`E;:fp%`/fu|Kp-u5?'_P`vjc5<.!!\ciFՌVY[$w䥓ZYiYiLKˤ$Т _ԭ?y5 &Lj>;x_1hjvQ*\bsӱe7d% NƧXk::qx!`Z}|*U$@ $kfco9$N/7#} he4.Brc:(ߘMҌj1ڵ#` wW}O4qRRIoO&d"d>[ԊEqd4[>6joqAPF+;8 ylV)I*NYŽ>2[<7i/ bD6kl8ZJ`6{> P2KxP d1ϳm `]wNy=珠m[~/ᎳWbJT>(~{&)Pǖ к\r1fL/uk&)Yu qul|4q|d>_] }폢(x}Y Wu\bOB:*=Ɵ19n #%Bd\,#A4QGlؑ2vo~!;?r@d BPO4eqAV!, ? _+`Pv.O4?5N9k2x;6jLvV*+IdC-]f[~T3M%d }u&1wo`끿\tO>qa-@mxmg] bڑ+@L3#u9\Dp? 3vͺ6+'- 7<=',TQ><ؒsbIQ򶍀G?_A=V*5D9HΧ H[+ڻ0VK >[C?+2!sd@YުndEM(| :*N|1y7 Gi{]HP~I& 1 JBrf훿L/p I[u˴?OcFγ|ЫԻcaRs/]uFrр ?[agdϾ5̺ Ng6rCr F',)3 Vx~'~],cJgد< EƮLޒi-e,{ݱRʨaHƧ? Y? IZ(#ElJ>>MA\K%H>Ǔdd/3:_ 0~+,4:>exGu$ݪzCx> b`5߯[CylLc-?9c~sdr:]G?jPpe{&d#0ْ//wg-Kb.O$=L TZS)$(1MbrN:$ʜؐjx1UX5 `ĴْgLuýEW!<޸$3n(Dž !'(6}b9'g |77&WqǙMMv[cI1;;۸[dfppz%=R\vR c,̣{Cak[-ߨ~cIIYHtWuvX-clZVW@Vռjr?=dsD_?]ML5X$F+1k2]r&_\5Oo^n6W/FF"{f?@7G&Dudž;IYJ3yWwzEܓdwWB=K‘~Wd75Moj/A4~əǘ0w~9w9#\u1 S0>]rNXIxu DjV 򕵨LPWM4w*1v kP[+1)rɝ 9u b6ƲAlU gP jꗾh;iK'z422]-DsO ܫ җd }Bw#%`OڻZ2 O~zT{+[Yˤ+dLld퉽'V$_ Dd]LsRjPn̝A^&:4+YG뀩~L>dl@; ˧'*E5_vj~߉'x2!LɢǸ*D:H3`w,,ӥm^RzA~bD3H'%Qʀ`H {;ƻ[ o=\m 6rʱ<bR_Jgc1'W҂X_=sw# xD@qͮ~o9<ziyO? ,31 G/l ']nK9mjB5YC&<->чpizn#Ot0qQ IDAT@iwHیE($6>1y.ga2{w+/]ވ` B2S1MddnPOuuC2i8s-1󜵧\UDXBp"efB\Y0$dc QgOL11Gi(gFzjB(Ée\6Cl}ѐ4m4޵E2$dIG{<ӘϹgڂ[?=G{y\ޥV&_wnNڵ=<ؓ~}cke^kϟ|o`8RHe\mDx૾N퍴|ѫ cUYFMe fae`{fs!xx`c3:6$~ D"2F#q2!d_0yP0 xFHOGӛۓ2~ {O@a+clK=+LTiXCdD'麇" g':L8;~ {֩n?4z+1cS%!!xan)=TR+;QzT#q}Vz'95 EĮe~. _s΂ 4Y9qE B5N"2y) !Z,Ƨ٩_bПOK0'Ou[ij4Mi#Luՠů0xkc9[BPti}߬:Tĭ[Yy1 Kꫦci. 2wݿ٠eTIW1y v^}$ѵlo<כt-o9+qu~5uGenG6ڳ>qBbK{DF`|n.{ } qz4ȖjP MuV AjwOcŋ&EVYvXZ}oío/Jָ:(WQrUيͤA3F]רdd5AS7_oU }J- j^B$8),X"xi@cЫp$+-^wxw{6Bp;+Ӎ׃|*f且4'bSHL/>Ӡ+Q jY0-%]:4l @@=}uO @vdޠ?@0ʹz{gb ;sQ\q;wLB ȊDc1{QrBb?{ǗO ])g݆LVfEZUq>CŇPd1 `dޏɁ|䓖eWfZ{0=/ <9[PG 1c?ٷe4eGx%;u; ݾ+7γ5X:l^q!;$^{9O ҕd Ώg5Ps3FֻCu. 3~wZdR EMz\ڼ *ݵ?y-ԾiJ39#Ш]z ^ע5j+ [}BVƢ4uzҠװBU;XkѵH+ )vs66vY%j$@ޅ0TÚȖ7@* {;Ż*Nq8}YJ˚%}$Qg޻ǎx/" sV q4;l*uw&.v޵H$d7O$,aO?n}`},nkڨwGvuk&A ]W BzIswO%~w)2m3ocUUaR7QSVbҨiE R`VhjxRCǫ88hJSYm+ YBL+ _Fﹻl$({+*95 U:9zU_| mkNsWs7=P|>!Vq;N{daųY8)>58y-Q r<`i:'<:,|m[0~? 41NMa? {*LaXj"s'$tR~J$@?*.=7赺 Z$EO8c9z#._pK.;edrV6*p~^?e _Vu2 01Fq#5ˎ{?P@b*dA%Y]e$w{4ںZ!f;NAm%1,rO΄Bps3%7Py @[؅tbS6X{1 fiPSv tAi4+{hj% ~v*ǂJ 8 Ek2@Lբ`ol׏_s58N v@r?k8uz{ޝ8uj򘉖Qw-ĊW@$a½>w}Y={@ICN5Ux(HrNYoZP!|7E~aRK.9kgv|X>M)pxXJ"F%X @"dQMMdM2S`ŪyVmoo`i"nQm]A=I{0@ۨ5sV=Ԍūoumr ɟCb> ˤJcMO@ o6s| 5۩;aed2+^-_ET4jIBU%T7 vVjXZW>/!n.~/ X э2R\Z\)sv|>o%c:{%F-+SEK`B ,p9yo- E;a7sO!x;eLl D2 T7dܩF%Lcm4l۰#XC]P=$G3YxnX888D8n}eGE&ٳ:i̞ ڷnG@K= vyKb*N!@ $=wqHfd(p3E` -+K?PN^{(jӠP[cF0M!t(+%!fTr3|:z7Tw@32V&a%SSR"BRV*X:<\+qIpD83}D xs]9ڽLv$d*‡ŇQO`ll'"Ɉd@Yi>+j· (xO!2 * g躇]Ghqm s+ȜF{"|4P:%SԆ{h4`1HLsJ Hr+vqRηpcw= ah"j@jȇ}9Vr\+ݔㆳpqHB<:v}၃9\{l@oS&uE.|gp?p<~r4 / (xC>bsd I)uMy^tJ`!\UN˅b1UU-=.|K~ &l x|`^M`w1'w[~ \)RG-'B P?qr(O ppY}q9\3G&* .E}V΀_2xUf1`.zbaF x ux']c?_n6/Ֆa/'tKjXYE Sss&ȸNe-<6YT\^]%Ϗւe%"cJ@-cXo.l` Yjt-!@|5_ŵUhc,(8(e_7dyu7X7Yq%@h`hsxrp"#xuYL~bLgVf@LΞ҄%'}诜3{$R]P;az{ pߕyU4/MT~MkZ*@?8jӘ/ϬJY8Rb2ƮŴ[kW߇|'>sA&<xw]8c2kFJLD@O@JL'xѽn&'ᬓ"+ H?ؼ1_gC /(dt0UQ3@Kec!`)0l%B|N0#:{p~υ0/@c+,Me[$l\n 30a=mۡҚCry=N EɆU"4ABw!}?c{8Mabۮڕg<]Nv}%Ξ;+W~SEA38[IVK2c]̊wƷŭҘkދ#:Tm^!1568GU8w`~ÇW rCK*<l'ʇ1) D0⢕:P9,* upVgJe O_G&򨹥&jDJZV j+=h|6YiA>!`;71enTg\'3-ua O_~wΝ?Gm;eܘ9iTH2;i3i@m0;ÏaŀjgCɃS9y j l6G>vϜÍ@du%}H2y5Pt[K-dtt:(Л؊{96}9zvڒ )BY;f"|G( 8SxvEř9e>/mMV!I@cIߡsxr𦓉@:!&p$UI}La|e92'0W'gη;o_zoܓ0vZxL$ԛ/1<=oݵry 73k=`'D-:~$YmBv`d 񠙹)ŕGG+lC<\AFA105לY[}p+ukѽQ֟X;-3㜇Z5@)x y臼C 'whN}+f? -:oa\ ɈFX6DA PX\R5*Xॲ6$[$ tpBcCHŵ@+2si3D7~)s:I'` H!d3_7FI~?k^mf4,Ĺ!( p(yp,?yӠ/6uVfwPQQPKXߟjtτ*{94w?)d.>djT[¯V֮tUyE CDg\8$وg$_tEhk2v)Zyta/9x-1+qUԆ4L}vrL@(FZYhxRLbkS>3%W[D1Meox|hEu3~@Z42gO}uYR [-Qĕ,=ϿxZjƭ~nu :K2.E-cd"4'>C{epoβ-IQ5bc6̑B5,-E IDATyYC>]gu{+v)l&(Ytʼn(|h#-1spA|lgX_޺ ͩo=xkl㟺N%$I1z~7o$0|Õ֟u:93űd:Ef$=_yv]eYPwח_k $-($LHILJ?γ~|驵>7n\5$pĶ=v-Nn*?]SaV9s0$CjѢ;û`Ci@hv)|sr; G0րI'<oF ]Ího$'<3VaH.bB;bԃ`DX_Luޜ-'Hהa<&#P-ㆯ[Gǘ?:f:_dݚ!:'IV?z >W' 1jA7,@-QgnHNdYc>sfk5e`Ňm^5qVvZ7;|*`,RrUhZ+[,1;uL?- 7GhXbw \vաZE :q7L@eG"gS,C<ۉ ;0XC,w~wl?NcRXƙyUFhylv0_0$y;,l٧q({d>2Z2) 㲋tg5 V؛/3:JFr!A餥AĮW7,Gu]dJkZYk*ʨ#)_sǜ˰lݻ YXSHȢ*k<<<{['t}"gUUЁ`"G| ` 윃V!/ #XbDXVv Do[-Gdd0SQ|~$c2?;`.3JLnu 0cެ vJ+dga*W*@!@_e8!Waif{g{G26jY@LCer$Qu 3Tf> 0c C* طM #4BC4)&mw;u}A;dύV2*KggN)mQ {eTMS3=)Q:;O=|/7} ʿF~ m̊i@cA&8aDRU?MK0仰lբ(q,>>~4~ kW542YC^!1ZN6&2t)+cN6wB' w,:?Ch^?}ޯܧQ٭$P-D(@޹' _ŴG<yz{fQ],zeZg3v)TWyρ},~^KoZ+dc0!kqAБFGLIlV9}Գ٫;=s[dT1F$:Y*)ks֞LZ>jZdɮ*CC jzm}Fs0ùK, >!}w{BH4&剔 a .?anp~Pw|.|w]dC[T[xSe+At6ASf /tn` P}|c2pZ'8qq^H4Wi .\ib%vZ]@J.?jww7wH^(&5SsAe c8:`~ߐb> cRJ 9ak,}If\|.]Fd $ /*ÓH'4 3dc֞azH X^ fޚ(g3I٧U&e2iu&M[1}1xR@Y;S @ X,{EYZ(!~ƞGsGs.Qo>D͒"PO^,B̎] 'v iT(X[_f;s"t`]HG's9:Ǻy`[yf2P\Iv<0Q;d 13~cl`5s'' !޾D9ś^r/ֳ0A:[".^k 8eA.n974+|,@QTUlGLFIrSz'"0j D`?.bHc'U4chBӨ}paXt}:ħ K-.”4zqr>Fρ߉$s*T1v~gںग7<:AAm{+aLo ;#`v凣S)*3ͼ\Kq);e"U2->||Z !=TezIW7|E:J‹V@2s(ƕݾ|.]ύsM8P EWwz%=$UQWlhm6ҋQIJ3I 1.$ BřKyX>C'rYZR_q11X{5]к[,~!Umv x b#E̮;s-0^Vm"zg2`]y[w m:v[ @<4^g0~/Zf*I򦜵'`ڴ`1oOS33X+A`ӟC Idt!eԋ1 x=lמiC;+ԓOڵkPjV2GCHZ{Hm⩖H J% Mʦ{]>L)ԧ}뿊: ^ɩ-,&0_ b5ÖC]\‡7 ;4BO™t?;xxI52C&a!zߡL:DGչj;OƿGx^i6 a+HwߛCkә4e\٪}x\ z@-&]eZӋW8V3d1I_߬0/a 'rx@uUwwӘOeS4v+[EJ\۸ c A=G4- #,-xلT*걸5Yrޡ nkn֟X*G}ZKS$kԢfCAOC)ΝJ2=P/|7#ng0a(ڡ2Bvy-߅T]Kǒ PX 16Es̯$&!MS>4 Q h`B ; Έos0gY@ ((7qq.c]g&7K,{[쉵ATHǗjf{[|dj`k$YF 2tW3kȁ]{~F02s^pn`瞇!|(~E hܦHzjV\ꇈƏebtyM{?K0;W`ƪM;P\سh7QfҌDD#%[ +HX2ᆅ*AG_ڿEsf-_gP|=L:,?B n[fxUK[u߄wGIF!f݊)Y{c2pP</^;ݢhkX푩N4=7 \i{Ms~k$¼YXo6g`+v ,V?Hʕ"Izg.^ N,b\FR-?21˃4o:aɱK_CirkDzYZXܯrw4Y9^4gCgy(Vgף6tA _̝53{?6;?\Y'K|T !@]n@ykDq#;o=O?72֋uagLHj%$z lK[e&À/.`%oȶ7= o(>W1&Putn)  T+XΫ\ǒ[9$? -%P=fkP|Ȝ>ѳ7 ?pFvI2tYeLVev?ƏZ|n]P7l-.q֪bFQnuo7ݿ{x≧-" ǽ 0nU-q,eַ?Ȯ%-y\GNEPqٟyz diD|˹C9^ҸMH$~L>qZ:zqI@DP#(Gh3hA)%!?בمk7{A? ~mރ,6-+>k:3<{s౸&0,43LaD2IQA)n!w_{qpР$>OʵF:7;]#t]K(on[q'ޗ$"[*]#]>-C>Dۦ48;j^"Px,& f H2G鏛@{c@_S4|eO0{sA뮡^DKu9i7o@  cpݧk&ح7]0 `4==OK? -bVc̶8"@"*8@ٱ*ê{w=h7@|h135]XҊo~8)䘾nt,.{+a1Ւ$evBu}[cpk6aos}0uZl, 'ټ5`%׹C>J4"˔ 2;_+^g-na,3W1qh`K° RG6kȟ8[_wQ~1 )?!eڢeWdV4€ c,yx=Þ ##[ʔi9*{rD+Ew!y{K&xHR* fM/+wcТ;R`;:5ST=`A@&9orx~X/c$9aغ[\ES (߂+9ðп$aK]l ÌsYj-GMdz4 ǖ6Qp7…K8wzwd)S^.5*YYGp`0W5/TL'c04DUOřXl )ޛ?-N05xaw|1쩿`up}'8 PY^ [v5`ԟf\r.|б%6Ed0gy TYt`fp IDATv /w`V׍!7St6\;WLmۭص1,``JhxֳUl|sޔ:7n1[k \nN]`G%IK _+L>5cKah*` $` sؑd; AASEM2>/&wlKs oJe_>k9@ Rو22 :_G{߀ 7BXs韷)_>k=]Ԩ)avt'PaoBNcaZ^ <x\z~}GGrc ' Ueq=wⓟ4Kˉ*~E" e1+V{P,g[(i?[[Sq.LP\S H;ص!Wݗ4AHpgfyTϫ.mp~߆`ӊxc#W`l\A2fWln,{`̘KRgR&̿'ή:fIMq;,39U&k}{ l,I/O!x>2ɑث#a6w OaQ }(Xc 0èM[&0 *a甕 إbЋA47M{8auMf"J% ;e@/h]M'R:y$[7`z>Ff9 :.c[̼Q cU]gYW#Crc#MZC e!;W,yi9mj* /1c7eaœB.́qt%\r֛quSfAZ|iPӰf"l}ZdM8hkF(Q?KKu`d137nq:x!6Ohu[zz> a\2b23d2P@469{ϡNZi8֗pWq@WǗi^Nyy0c Herˑ-^L0h_2[hjO=8&_l}9`Z ϒ:B8o 65ݍ/;Net!!@t'`=x$,(RVMdB@i(,z?eYy.l3X7Ƭa,xoJqMJ?H [n2<šoK,-,=;x -۸h4`TU:CP6ųB@6xx2D!_^MBx3 @&p Cǀ~(??]s [f:iZ,`,M``i-br\flx=ч f2n1}S:M?aDsd%_ϘIiSaoyn.<}QhSK)y~J?Ƞn:30= k+o5o=#8?xeqe0S W:0f9L` X,` 6,І kz |*y5P9|GX<03`> ϫ Ytk'ѤI\Tq #Q:J`_ 蔻cP:>^8Yqs!u|\h ::BƵHIaZU 5 0#WP=SCpfxgxV0сਕ*+uA0m͎ثwbzzz-Y;_DN{>Sթ;Ij ha?7O_ E~@5$RD佼S gc~ܹ(9D_+b(k,^bssCU8?7ԣbg/#hR# N@(P->Qo_SV"FE@Ct˾] ZH ˈP#HD~9cdm*&ݎBJ8&$lUEQU[~8{wҠFBoW(Wf Ⴢ/.T{ҭ{ouq{.kmc~ wh^D;ti:ib<Uv8qC&O #h0"J\%khzۊӯkEF[$!QDʄ@O:F/E$JȚ&tx!xG8 ]~ +D׈XG;01RcDloCtsJn$GۓnFdSf_0E:OY^*ҭry.?N\16˽o܅{ɮ;>=V wBZ7wQWV`h>~D@ݾ:=JG[ߗֶj廄A[P400:>e|p)#aH}"n[ vڌp=#km5U@HQ"eIaAXT"7QR(UPJ(]Q ?TT2?ޣ!忝tO ǏV"0V."15V/yaB`%i !!Y7U(¯`qUdb݉[o[3Kma":wkpeŸGkw{NevMp:r߽` bM_ n y^oIz儉p38d2M en3Da3|{ ]&fB ERWTB0(P$R+URUDIwir* JUc(!8!\ `: Rfn޷zmQq 7KLlwr!$0foģȶ"d0qhUo7G3kp~Ϲ27M<>no }0iٗxp2s]=~hvSȇF 0fxx Bd3|**ƞ]t;TNL"jHB+ ME*BQDeS%纠R9)Ym;4w>0H `r@l]*Ȃ G3< &VpTӘ74x)'ӧ5Rƌ ƈ74?͆&S)MvӰw+w3s쿷wd4At_ϵs{Wtx)uUQ)R4nQ0Ɍ0f5Eû<׾hk7K"@ ߧ-JU(i%+*%TaRR6 J]n&3`Zmr'-e+^]< g>x?dxtH͟&1}{y-L|L)wwz݀Qֺw0;|g_H}}I0⧻K+ܢw`5*IWKtMw~D41rΨom- ^lZ"L]݋BK(Q#R5)!)Eq;BډфH I0%k#jGI8ڕ{BJ$dxt3{=w x0!v: 4L?{f'#3Ѻf{]֝'xZNymwٽ%Te_ lk[rGJ$ah8 Y:#/)Ӣo'Xoj vʲs`oTy7j(} ddaQEE%%J azLHv3H 䩴t- By ؅@x:b 1ph\/!$].7+zB͹oohKy}xBu7(ozhƦUMHPutTxА y4Rmw޾ \WA%a o؎LS,;lH)9xd֚@ dj&dшū3gdB+Q΋ܱNq5}[v"pf';O .AH((cn(՝ƶGL`͢#!jHlѤ^&6? ;GR F3>xũ8ת"]p礋zc }]k I5ݴIwWx#;}ܻ3n"Rmvф( g>WwT\Uwܱ+h86]uk\\TS yރd{dp;+leS߀(8|ٌ # ) ƧOC ŊbSM(;SVWj.oƤޕ0{I@5vGGǙ#tųu$O8ݥ }g;AG!O89CDl/y%CmբmSQ[уw}w޺}fWU_o{ޟZIvͯ4k7.=9FIֶ3Am=+w xSJUlsr>1A'F$3x@<|}f+cXc^Y]k ĵ!ch%r5<>&o ds+Cܽi}ƒ !A@8Ϙ}'$2Y#hcYpglW%j4vAsu3FcK~s- ^8`yNl?]m[)͛7Ha,o8>Z^]5ɇ-7iئkܙ+*EY|MU1K[K6ݸ| s(w񐱝,/vǻo6Yu7륷}^OŶ7qj9Vr-U.N먯t=۷yi&tsQhaRWEl.W< iv-^HX"Ðp8 9RذX93 _ T^Q&Jk'ڥjSգhk'QL$$!90:FU;Z͂/X__Z ҷQ'}@o ˇź7] :ufl]γtm}@NpA0 QJZmMxP-ˊׯ?HӜ({Gտ=!w|_vڈDFnpFwELٌik\#MC•{Ukͮ ti}/;D*>wką˗^5POtRiFX2}ɓS8-Q7 ,'p作t]\(庄2X0mL%i%.yj0`qB"EI@0$#Gc Az^8;YymL[tnwᇅM?=X~Yw݆ig-y p݂l_|=2 u *ܶ>TB˒Aν-c t|O՛M}ޱjE4 t[Cm-W%WMy mE_=L5DU9fC:_0=}p00wyg $&=2jEvR,2D]hmDJW4h)BA"!ѐ`H8aZ Ji,'+7WYf5zf k{Cݾ[ oμxں[>wOwDꤸqb!qa'7Ckwa"D`rsEʻ*oAt7%? w7?[Ak>٤57 09%VԶ>pѶn~IFrVeNUTd 'O>5B1Ƞ.wC(@fN22|:JuNn ЗKdYQ<{(G-l=Zo" 844S p$CiB8 Q%K+%Zlfuv_8;#_Ъ@{=ww-(qda xGhKgo.xiZ IDAT#{ZknnEQrs3ÝiXn x9m]iXe{''TU(m_oQ7hR35 @&emcĘ[֞*ܕ/jbB̈́<)#PUJ+f8oA.f0m! UTTiI(oReJ נre"HBGi$D&! "LbѐtF2D0BA-N>2 `A,ɖk6W_bqt*2 ~F">kw]t߷w`ZCpv so7vigdYg}R m])V?Qv^?k@^y'i}ޢϛw6!'5Y N3niI)|mfK;4hislf +ufӧ v`;Q"R9R"&5(Sb4\PK9=B|0G &C#cdH )IѝxxZˊ|f 36eueGl>t}Csͽ~yf7d]c^ݾ/n˪=pLV8[p}uWx;BΎ1w{jXX> gJ{Koϵް^,6!q3Y.Ps{^#sRFCoiF[(H  gJH =\h È7yet=ҁRa㨣z 3G,kŚc'Gģqq?w jW"Hj$ x0@FSFء ̹`C͂3ozKֱ ~}qxkc&q.v;q}7nvǹ`^Zۏ@CσB>|~(n`onZFךJ)ழBvv1,|`)jոcu$˕F˦љX;2ڹ&3|e  *KPνhF ڂt}tA"3h=mi8o4(IeiA:߰fx:&&p/߷ ;eS7/oX`}yMZRYPW`߬sl;?Qَo@h4(rUw ]i튡}[صhyd7iSjƩȖЎZo9fwh B=qM4 *DT+Kt,6cZ7 q-J d=Ci-匥qg`&FH}( 貦eYhx`x@2!Ttج̂+ЭP VݮH-$rE 5;*<}A9m_Qe9eZRR7_1<:dhd@>2K4'/Y]:"/( e^`5fݿ>*{vnphtfufyMB]v|lv>J> 2_VS3K>N~x7a)>f{H8Gηޱ?,@|jF[w9:J9ywwNz dm&L8[f!JkPJ -9̾Ki86"!1ӓ9x> QyN!B@gd?_zEQV~ 5l=w;߅yCCM5e؀Vv˜n5wk#нM%tص5ΠڛˊofN<ڻ]@Nns *kûKi5}Ѳh3@kE C0fz`G6њaU2k傪(M R4BVoM4*Sh3o@Yr,Ϯьx2$0D5eU^QfKnW 6s"M- {lFBg>b4;&Y,FU9˛רJ&w[C;Bnܵ7j{tu\yל pNۍx ]=ٗ}6D9v @SЯ@8.[V\xIv2#͋5B{ƙթFBHDZ;V`t&c2(ϑ$ J`%%e*Iο΀l_KKg%@ԘgQm/o eȼXd7Kodf>:L( dhM fhQE.+XgWKҫ9͊bQ* w n Ød:{m`ߐ5&stYR)eځ{;POa2`tpዏ8z1SZt$Ol6s~`Ot+uۀ^dRF5}Y7Q{\|%xopwc2o m o:ۛQt|>_{ynfu[:FQ!mZPL P:Z"rډ oh!nxz^01!A%c/Isʬ𳓫.˫ +ZZ9|GkI;9kR5);i)Y+؜ƛb1t@<OCĠP)eQeJRBѥB%UZPؠ(הj-RuFɍYhQUZK}#m0k@~ƿ'@!h䔣>C8ֻA ˜(qQ9vnMnֵl׶7R&=R xss ߼}+{Kn4bF@S97f]\̗v#,tZȸgqB CDqҾR J)Lش#ԋ{ /|N 7.b説뱶Qޅ[M }Oc;\ŚK]=c{J*U^`$ˀ0ah.J1PQ ^bR*{CUNVsTX,E(1**+TU+Ǜ;WmnZvqpv}_|pB}m0h浪nۀ_tjmK)ZΠʝb a'@#?lGYo_c)ʈ^FŽhP4v/YYw04Ml8PV@iͯ( 6?'fZ }!{6nvv8w䩒&>i8I4eY,d-U4A4`0\p,ߐ.o( iZ:ٽ[q;ihY~6D Nx[VBU3~uD9ސ9uq"?Mrϐ^,QlС e,ID$&f$ڠ]u ]0&A'pfO_0<8"w =fTUvE3~maz4n?&MA;lVS,OѺ , $o0n+5m+d#x/{\;kdm韟3Yu9Dj]Y=j'VUe,[TR tJTHi-1Ɓ5oG (zNa89&'m3kS A& &Gft̍Xcp*in3)kwp+-է_hy/~Sk׏ JJURJ)(ʂvqORaNTUFTAa\*vI.0om@@^يW/"#Nc=GOH#DCw Zk`fy"[TӲ>P}~o{͙o,c`my&%s;C?&wVNぽ Us Bt@SqߚTf˵IվFw?e+R !Z'7YZeh,gDP70C_6ن( E9 d HꝝzKUD@FD 7g/Y\VܚM!Can^+=LmQ3VaQUGPױ6?wh!$f0=`}U>St> c}Zk;tAח~c\yW࿟}2KDo{4}! IbNN)+YPU:'UqgޭĭAZq| 6?5O GXnђujZKTu!R;݀YYt} xz5)z߹$LhB2=dפMB9]YLs Tt7 lxW#wѬ*2|;>4譿ks4ĸ'p=&' FUꆫ7ELׯVdr% wvk*P2;!"9ůUVDbݠ=/V{4d2, kW߹5plwnutm07\\\_˟s3_RS2njP-fo8^̤棚wwZϻ{tPkS3*, ]f"U5O'4(;5UEAwϞ%CSY cA4$LXbq~N\dChqk׶])e˫xTnZζVoGEcC;^z}uM?!yx0dxxsN3G ^Gk"e5w\"[# w qtصF$jVwOݲw^7Vnk}2!#ݿ|eY9½ߍ~|,7= MO!!"& 5נk_7o锃nFa6$n ]YL{lT$Rz⵲C jCu2٬?f'3lq21!soX__.Wf`-L`Ԝ'Es~nWھ愂]}燇.0ki&\x wJ= 0$jx,ѷkPwLXIcU> _ Owm ?_(UQлBu ]gky]G7eYqs37p%g=MNW_4wGpss/N#uNL>6n|k']"I,oܰ*sDClX+-C`თtM^= a<8wʼ  Af `u~|$r(]J$.; F;Gi-n3}m!ސޣڀ֤~ae -9Uu}eДܸx^-6h|}V{仾_3O Zk,8pwͲ/hHj7@t]׿}*Ūxȏ>ϬF;|_^#o'U ܼPT٬CRQ_Ws"'Kdă A H``2cr%7, ,t*]˹ njGsp5P4|-YJsn6dLf{"H2ySFfs |8qpWV7g\ _՗d2*ۥw]~Y߭W-F m;@ͷ[`ԳFӴ֍w)Dž.. ړ펫!܃N:]4vC4Z|3TS6闊G5# :Et* +Ykkj&elaޝXE {UVʒ<]Y9|=;W |@Bd@21::aty%rMfTep&U<IAޥfLoPy; ]fdl}ߵ>`ׂڤ1cѐ&O Oif+.^ן7TʘƲ~_W^{ϷR"6jFm}9oVkm|?jՄ-o,V4Mkv ^E a̤r'q̷c~V߱搦!5f%kGbB(a3@/31&Vmh! *JST)UYRzIsx1S5M<&]AD44 ?oX]]"[, Ue:k?qـ~m|ϛk>@Mk+,) p 3<<"LCUpL7RKn>_qs%̬ @O@S*qG3ZњD}|ڦ]w5NɄme}]r/foI9:_⳯J qꭆ|_ol'o$7 쎚l65؆ xbcbT:>H1 IDATqv66f8>&J 14j~ӏ=g0-*qB<2<8b|b`}}t _m򂪨p]0;۠[;,oKmkڍu=ys020!ٌd4&Lcb]<ʾR0yf}s\-lV7TV[[s 8o .pD_FNZSZkdm*oR5K4~w-߭KO'T-x6{Օ&OZ"R~&B_}ɷ w;pwj7hfW;a(֍7-\LQ;jMbPT"ݐnX\pf'2=vTMKմjgW P!`|^o(2+PE 3;ӵRWd+-Mv mW+x3Um])22HFK' Hf3qk6eY\1?71"_Pv~P-|{ R;6kCO9tGMCtnR-~x[w o?9wݷ^+~!eLysWJAY"YW"`w??PNMGM\w|1ל匛h76/Z vaWni%XҊtuMnX/X\qt1'3O2m[=[,!8"Rf|&].KbReu[yDQA؆*]<˙v`^H RE-@8HFCtJ< Dalvz ˂t5gyן3?"[RVֻyf/ v|ωY;붝9mS 5M<vo7>pp؃Mt[#]C78Gi,gaZbUkEW}gIθ.hp ! K43M)=]];sF4dύDi6yAYY\>C&/gQb}=A( 0I`::,9eoRt [(7)UQP%(B)` mKճ}עޞ;miǑy dQ2 & x`6!L#d@o}VP/) g_p ׯI7sTQ`w΢/ѾO@(vZhBn\:3H_$|;3LB~jڝc-4vu+d_mv3D1^x;{ <3Ժ>5x+{n䗊ժZk~{/[iӤgny]oڵpN== *Ug yPENZ9gzl}xh@^rkmV4H c1*2)bXۿMj| ]2h˜] _-qNB[D@1%5@F2 "H8 Cp8$ ABEYA|[{ŚLbWlW,._rs%W"۳uZ.0XvdՌ@iMPe@1v~%ۃ0~݆;NDڷjI㶱NK*(.&뽔kvjZa=e"<@գ-,in4v0(7vWԌ\ZRWw;t:ykr>)EOsmb*GyZWD,q[5hE5:ϩktK&Gϙ=y)hf|<U9ziG)24MU yNB W*6UQ ( & O01͈c8!H0&Z#ra5ռlۮʂ|b}s%7/YHW7Ej"i>m>@7;mZktKsoJk=VJNw'o.o*] ޞ4skʂon4v3Zu4V__(ՏD;v'|_6uxf.D T ikv5[K6\jJCh.* OW,.fzN&?4쬷CJh !֝|(4[0ZX,9@lG'>2 LN^0`:=e4>"L!2P)(-aVJ̟/9kV79e6MOgh iSwCw?;%O7t7F}Ǯ[)3|{ зj|WFIw-jF;jFw۴Њ7ח!GJ^g) Eb.uhQ|x )+SRmV7Z@񁙬Z(ɽٓ2M Q lokp+xZ82@D $XÒ"H5:Gd:!MHfcɘx41Lh*O??*]^Y\|sv/ _?ž^o=_֊h(&sOA-z߾* s>*g}uh}_f- na2')e IZsr2N/ͿES?]7Txчe?zC4?;KΜEuq{+ERf&&]kƳGO=!/a4ͻTYzBRR<ҝ?mC"M|w@_5M̬ɨ"].旬X]Y\K;a/8ߦ7'v~;!+RkO?ZH3wZzql@CSa['6;|C[tv4{sf0 ۓ*ojX?뒳 >Mk1H4ݝ(uܼ=[[-nKm}YjrNn̯_4< 'NgI[`}\oi6 =+hOз`#?R6 6+͂"OQeq뗇}5@ƙJ>0g֪ ?2}Y1^S1xn@n{*U&Y6N+x zE]lZXX"]c(-[L|U7 ! /<;v5Q?] Emƒ)#kaj;o&s74L[n51iՂB T2OI7ϘM=0>|b}OA)@oԧRv_im/XaB)6ƻ9%zNn1j۫zo7@)dXO) eTlkYӦr 2jh֖ lkk;ErJ ,\;u ]3ۀ.j\qԌpv;:siN5.N ^WO~ Kk}~KQ;i4q &8$ Deg+|MY6E??/ڦa(]L/ko+Me-46ko fG0'Ɉ(IBX5k,U~UU6 ط=|ب34جHW7/\.oU]^u0;KCL8fɈ 5%%ERU;`oV`ZG'Ro&rϹ4x.Nm$[f5HTC: ,Wc(3:D(eĪR|FKɇϛxG5I~Og4aRHxx򜃃|d(,[^^Z1"K6 ~#jk@l=Nv|h\)<#[-X]_2|MͻD۴L+H>|UvjB(a:{gϓ3;x LZQ)y6g:s/>cq,*.83#YVvSٔv[ivBl(ҀmI7a< F$1x{0uQlWkN0~}WVS/+,vv4`@,s2_mEmkP TeIYTYFV 6s"[S2C8w}nJ6m.W !SfGsw8802Z)ˊLf (*#MKjhښWﻏĶތ+U[}}ր/\&M7oy~#wQ:cf^@:o}[h:7O`mdA'.?_P %!2$st|dp@4D13Yߵ{{Eh$$$)v͆2@!e;W26 vىlRE9A싌$$͢Ѩ߷zN8Yy~G̼}s{=VYbQxA j} n%ZxAxAh<D9km̔*J`,-r(f-'ylKi;.<3J3CV~Сzͳoau!Zu۾_ϰ;NuskkZDX,0%ja.A]%b:֮Q/FRE]{X) L4=AV{En]f4ILRL?4:0ƭrOr6+c̓K790"am+H_P)i6b1I!Y:!K[m_M4$RjQB DV,%OMdbYJ%yf{0b`_$ncY?0k_;G!uo{QNs^D$V9hZlrK\kUԝ!.,cQR@ܕ*unoU;>76z5}KklQ)|ê.m &hUƯ>۟y/bhWLiVCzfg<|?$z봻gػ WIFdY $ m]%=>={>O_)u7UgNO頻"KuiY ~&j흡`2-x@Vw@4eY0zQ( { JCl )TŤjLI!i1O:1?$}"ozB#“,X^ >oM(=CA^V7bm!{n[sY1LœnFwj?+W9[U*iu,w7[`~_/y v; 3n͔8eX-jNVEyc 'J+_4Dǟʣo$|Ǖ{5̹GmBwϱsLHlOu> ėxpT@1 uArr1I>&͇$Ő4vסG4}ds , Oh7'Ї4df:yQvgyV7`u+h׌aTx'xلH&h,Izfac]2Syd*_&w of.Zk$֭ޟ#'%';mD]?Ug#Vs"RJZtxQ}RՇ|RJ?@S1IO$Oޑt3NjAFo{}L;Udfߵd9ܼٙmL};yY&)tF3r'd]W Ac E>D{d!񄇇)!;r1E> ?4J-ZU+Xݸ:AhJ17zB+lLgcssn> ݕjFsf= WH!ej4if!&\75>ڕfږV5ԢNc} WN5TmOkjVh6dbٻH2u?^xQqu* IDATTׯ]' i6bpp4׏ <>~ x^(Z;C+n0WNE|ճFR biojp[jsP J {Fzw5#};ȅgTYi^I@h$Q |氜,R!aGw, ?@oAصFϓ{. l`:[/u9ëdԻV';ݏ}UʨETc3ydIy9h3>A;޳>w # Iy9 !զ@N_4^>#%h IS\nX{"tvAi,_=l'u֚0 @ox^ OearHA&l:HK"LsL-_cj}mպy_:l=%W) i11Jl~!na F2y%kwPkf >֗nyV~]`cVΜ{ k!HTwrQ*33Ճlo=Ǎ?]Jq3I2"*mֹٵnd;Tm邗*7@p ^;>u+moZ ϓ,kЧ v[VWG1$Ôsz9iM-y"o~1`& pfg5$] xSI|/]b(lh:[7%JGF] 8#e@V{N,{^`:ᖩ֓L( aՙaH`:OSWbޅTBn<+1퍿cq]ɭG(#5]H$P,\XŠ>l~GFݕs/_=OI#)Ob,Zxenm^${*'NK;+E@et{AJJ6ڝ8m-Bex}y~C&vvsw3MVs'4z%5VENO{WޫmmslFr}ǰwO.vVVrRP9y6!IH}㧎6)SoCO)*::aTɿXnըo3oot= 6ϼJ)Tj@cJ;ʟ;vyCEZ{C-ƣ-]1 :k7yfY;\klw <.KۏcQ“Պx Oޠ)awawq {'.Ӕ4{wV @T9$B Jȉ|5y֚sGW^3,S ^'KGL&G7F+֓ɳ-iu|¨MFo<à 3:"C,1Yk6w,\lm*74*1)2*A{^c1H#zruTe B{J:<ӟ9'^@uhuV[;Gw<ަI/u̽%)ӌcxKl8xc5&W{V+7*#M2ݿƆ2yj)\|Z[܎??{vo?3y8˶Mݖay(`lH'˔@_+|ݫV%Zy 5yf' xxᕀ>qn#_nw[[ǘ 5J xx`3oauAڝMv|aVg=nx`:;$yU*ec0_q-8Cl5 ˦qM\eELV$+^(BN'mBl~_ ,ќFK vFJm?ς :Hi[H?$luKYzkCA{џ)I|pp[_bg x|$}AThe6o<#MEIhW]x4W?h JANZ!APmL$3uʁdY1!- {T£{G񣷝nIo?zthc}e8Z&wÈݣ}37v!zkiu׬zP_nGtxx/r??dKF7IZgQy(3~%ߍJO_t,`ZwwJ옶X{OO2vIӔ+Wn˗ޱ}zp102_l}˷w:nK{E aEKrBmV914k<̓|S-9iJ}nؠ3nzG 'M0P]ig)^[۠?CFF][AmŞ)40`2;Ek$>yPNyY2Ax#G̡˺*}Y+f>=:Om;.YSuP}V+"ަ]ZnՁ %ua L+H 掐$~y`=_euMzULq>SLٮ7j<VT4cFťfr_~־&/ߐBz.xɯW^cggppo45.xb?ػ)"S]7n2d Փ-_k/^<[go0N`R eʲ!-8{GX]n,aT9;4{!=!j]1@Cɐdr`$i<~LdYjT eLhȭ/{^d*&+R|2:zkFOn^SV;zHr-kiٻ>!HL [R0 #Eu [xJjyg$eS @uzlQddqm\eo{;88d|4բL L}>h=2ؠu4|ԝTR= 19y׮rq67֚$IyW;`wwOK3Nu[6]ckRi#]؋Zi7~﶐ Kw2VIX=>I$||`[[x}-4]p)FgE$Fmk ¨omZB L^-L,&#d@<H$dL1i)򂂌B:!͓28ɀ}F+W>r^<` vAI_-5JH3 |mQЦڤfV*NFm+.g%Ǡ$qvZ6 JH9 N'_'3/:I2uu&(x$G[ z͇GsœTJ FB&ꬢ0btDģ]&]&=&}ɐ$ZUɌU"QOxM:cRޚ2~4B_duf6/Q@uZ+lѲչC\Jx8ѩCҨJeYLO)3훽@k[1wy3]|逾4ւjI\Tj5s]_?CQ}|~'/_lڞx 3ݼg}?8?݀I?IR[Ds}x.fZڹ6.M?P7f^ƪbyS{Ǚ3\ygΪ\f,..i< 2d?-h&)!~:Q6^B> M%Qݍ㔎Ѵ?&JWR۠cW~3aBuVh鬜~yq9z+ghuW [=mR@[`o˧yb_[^bƋl][7goƣ6ikc֊NYY(`O[WuK6Gi2x 8鋅Zm, g>/p^{^Ժ.s_܅]],cހܕ]VBQ8S . -}2 `53eM6_ Z-׸z.iZf:1vL;LF'{٨nt;\5ҵM!`ٔV@99J@$0BcnOx2|-3GggܝDUi6̀91o\:dYnk) Q]~=B>d~j4ޛnϊ?wUĀzjSdQH+QJ'2!7yӦ~v[+ȍ!,G;"OqriP$TaybdlLHq|`q2 \ٟFMݛA6!Cva% ;A (DoBz_>AM"]u1}άZ9j[rwٺ[7>ϭ/*i2DKσ>Y,Iޏ Q zM1lܰz$j/p}|JE?LLk2wKki?3ljv˵97IKjڻo٥˞cYo]ZuFVU:JPVM5Z[!zqwuЛܭ_ M]kgήK=owS~겋K05~lct eC-n=mKֺ,|w:̎5[{%>^F:׶Ϊ,A9 .Щ􁷆UgE %R {=m?\~$Nx̺HRh<]XCY$7߻Bw++_n<a'_ Hɲ Y6!IoP{ H!Y6f2$n$7šMhw>FC+[#V dH}8>>=@yPJc 0YL(x|H?c_ uiNf1m3+ n&ElSÁ}{xXF.AIZi@ox8^go҈Z2tnpZT͉`TAۦ(r&yޜZ;N bE{-y2.y~ RrP,\W[Ѓ%s},mWپپ<7_d t<7`–\dh2Xe`uv-:RcvS5լS^r^3c^f]w^SSN2a!67W|Ze,{7_P'3PеV%8;cil7ϛp%rI3rl!H_Yzɧ'Mn޸/h*.g> 6A5ڝuZMtag<JfX, C=MG8~$N>mT\'Z*:֊eEa~^7xo}t!5EBcpt3,E1aرjRR qɐdgn2`<&$l*RÐ=& K:t.% LWQ`pa_(rԍ(U܁.|GGn짍G4ٟe@+;,%- j@{|D>m3IPmr!i iX5LThJڧy)xxO{pWyŮy3i\G= &Y{t7ګ6!U$RՌctbi:ː4$#lLFL&MQv&M:ՂVkv{͂:Qd*P aiT++Wke@q|,9)ma#AJ5-|?@`?0} IDAT;ی7G;L&;ɀ<؁" <:̫d,Ov9)2YUgڨa!~'-[[҈ZAicZ3P XW,e?Ι8ϗI g>{o(){{fMH' MHΰZx;XpEruo},_ )aSW)xo瓿iåX.}f>e#_' {UZ5M:u*~}A2&dY!M  k{^{AANZj9xῡR{7}DH.&;^hf6!M$=.Ѷ)qn;`//% u5gsԊ8N~4K&(k[;`s^[O69~,s';MT]XnL @UǀMEԔ`^ `R]7,^pfx[}|>έOc*gd>mVNl(dcrE5]OL!Hڀ|g<&dYLM,rКum<<@hHGJSIz!piA^Mpz;+Lf%Qd2@.].1u9'ğ?uHos?=> Gtho#8@v569{$c ͯ>O+y}{{%!x'._1eR0e\ qj3p%$?wFߦ8QjvJ [֝гn|؛.QF?KRO(_#K%:]p#]8BARUYZG9I2b4^+ \jµ-\1|]ů_>.&Qr^?*oɓCmr73,+aT +w_ ,XY W*lKAgX7Dx@ e 5_!\+Q=5=JWxt ka3t:CL"Zi@u1FXOҀyunrr8th_%M? g ~ U\4BHa\ OƮG+i[Vhu ,mH%gI:979w=/c{sݺ@:yc}ݩ <qMՎLH)i#(`<>7owSïw urv< k- otJ1Q[ۼv Egg%m~/C~s|9k݉=[?Ͽ*=ևmy^n2(J JVKNeDa(`ojj î1d J) p.4V4\)jF9q⏺V=IhqD?@hN~S<]# =65[ܵV F{qv~: փj09} %;}Lkε)ǀ+X7%ǜk?EQOaާatDpŦwST*-<!<8"-CKjz# I[wuέ^ Wz]Oh8;Oo4WЎoBer[)@ʕ^5'L?~oQmtScxH.Q"gYF殕us\lj"O󌼈IӘ4ւ6?|v}γēSPv-Rz\j w)^J1'0Z)`cRtgCR"r:iNuRhј0>Edc_tE,i[!W>ZYd(-duȌ %.q;,j=N#Yyjrc|RTFԲ.l<]Y>Gv#rKKQIIHw7E'~oTQՂڸ!էcB# AaN 7b,n0`tQ]?YFE D;i90)>E} gJ`<`?, jF]єu l&,\}dMjZ4'yq >7>Kʢ (٠T{^ .ף.Jy˔UKZ(ɷ?ΥW.sMkbYj~Z@%Q#tZQ\cwȒ_3, _-y]-T)meOlKO ݐ,=J/BrB0>'w%gohq\7&'C啕K>ݧjIuF> d$㌨e$3m@TT{J%wяcQ&^#{qBpu89I.vby3.?_A[>d><՝aJzfX|Өj^9o}uG?;'+V喋>y>_DưV ৲Eӷp"-M`g`_"jy+azAxfUv!eykܺ'ޟNV-rCWQ;9QT3:;ƹ9.dDwbu`Ϧi]$ԦgL݀Zaz\}'Fc|5p/WR^*3KPiq;^&tK!R ,YEXH !))Yg_'j36jQ{%Jx$Y$v{5D_o~ Uk߈@ncO sAy> vh9Şk2Q^>,P5KUiXh ]`gm\“L3-~!6 |*$l] *aIKh DtVe2)$&ptu^wS 4 =roh{ؗO", ak}ϕcilſ@a+=QFf)k(t)%:g2I}h9Ǜnz32>6P;^UY]0K/S7}[?Mf- eEa3G1yV]7*a5x˯PkwxQ45iԍ4+iFAsc`8O=|~^Bxok7q}_H R P+ؽ0@b@[x0*-[/ٻl$c'Ep>W{p^ܪDvm2D!"7L^ R  Οl?$+g:B#dbs#!^0}7 iPz`gJw_ڀ:}m]eUCNgv `ZO5k:*N*|>M5c&n •4;^I>GHCiƃ}| $cTRAR5 ޳[EX&XcsJh/\Q}PfRAHɲq<ٞ1|"!$grx.\W2wiip`.Jm 4γn/|4U o߰xJI(e5?57d 1yY'm>l ny7L=Vk0i3j{#a<k]if~\[~|dne F)5].K^y7pu?(wh{3K'f?[=x/o&t́*v=nm"cs<7o~Rl" @/EbXM5JWFxR/Ab9xM~JݾFZ/G ê&(U2I%QW|Ͽ;d u)H.JnY`Z>a0uI5X2@ixZ^bj} 9vxCݞ5ڴlFF5 WRP@K㪦,mz s#uTƐ'sWoR:cv `}}n;{IPgMlnzV،^Mb1md9%x%Kj jdN|E/~6sע_gpŒJKJ:JŻnMJ{P`wT >ZkVVt #H;H"m8Y.5.ޫ <gZVsE f|Puy@AsO)mB[9J*uTe27QF؋"3ߑǕbô|+"8SiS%T#we$%+hׁ&,u6_FE!mneGИnow\s7=*є~v7F+4kݍ.k7V( n 36/w}.;_]:ēygf.OUz,Y+٥3:W|z@uTNH@8AH s7;S`$WYi(z-Ze]mo眉1u'{.ίE1$Ny `N륇~/Yl/俿`j s IDATn~Zt /o A2`_<v?t؂G?^{6{ǙrGY7yl7oq\8/>>N?[~w]&^x>8ã쫿qfG@]OimѾ9990rqE?Q)o>_n'7Z'g;8Q(U}U `u~AF,)9]-?xֿ>CR̝b'7}["/]ݫ ]e 3Vh n0_󝕬 DlͥE19*jOd.P /uK&7_m ^|ymZm]Wz7_ncd^+m\]]ssئ|Dx{eϓkɧwns0'_HE;t ۼ.h~i_!ns ]"Fa@6'n-`S`Zo|q뛝<9Z0c?kR\gw! ,aT4y<ߺTT. n]߿aN-@;caL1[>U4mg۪D*ŀ\4AY")dPAm@}VQ5c';cKobP.ygPYެX{^~\m# 4ELwx=w{wy^wux 6/?{7:/Dހ)6(K ?8XΥuQ }+Y >U]z(q\L (7.zmr {{V~qĔ;WZ2Ǖ\ygл;klUn۾ga*Nj@|.:qbmpuXZ޾d j[T3&,m:+޲UWsgiZ;va:+[!A5Ͻ%rxCԚ!d;>ptF@woFw}s0z-.BroWt~tǑxJ9 ebtyƙx)໓-f si`;dơ}םY~se0!DB=-ZG u/ {yyCC!y(]"PDYWhơ]LOdmLny?Va[{`&S@Z2.R:F _3{45`{xҌVDcۗ}fh|2 qFǴ(aJ:]SLFPloN_CȋSσ@w}~d؏Cgzۨ%~ܓpݠw~ޮR~ /Gw:ҽ?Cw w|u\hЛm^zK:`XzjpRob┊{6*,x .\8-BBlq4<38 Z)7{ "X1nu52kE_>(pz-N=ĄV#Xܛ=j?Qc.|~nݗ!C34QsvJEgӼc<ۥʼnc4r;fO:/Xi@,/?_㕣7-)UGק\(s|zJfj[küg#% Fm޶%Is-aKnIӅF sբltaT~9gg Ϫmл w/y1׎_eXĭW=_=`}TDzSIJZNJ.PMŤMf"4{B32u[<;&nqe5oiްL7Pǽk2/=A&!ROPiٷɭ_pw(1sg JF4.ݷŞE~LbIBg 1]&$ hPnE#ML3sc|^yğp^o˗s} o-#}/AtWͫ Eg?>; i:O[c;aH4m-]>VOFVwVy>a j^: `*M[FqeiJ+1cNloccd}?/wNpW~&f]1OY~izӂ s \=Y{µ;G!FJ]pH-.,C:zsg he25/ϽQ4".Sj4u 2E,ŋMQƘL`~d6V."F"1▖(mc:HkA\ c2*PRyx1*fص$ P56!TJ<8n>* J3uDC| Jzx G}'ؒf6;vּ%WVPZ$Hݳ=~>[t.J':xcwp-6wޛoVi3SLFA m);΅y8-tr덻^tfl.]׬nȔ<ΗߥLkG,^w'T2nA *u"^ n浙M"MJ%yF8j ?iuNNĕϷn5ɡUbK q m @UKt!NyF/E(#@Via\ B@k>W3[bP}:KT+J+Ғ`']z5_ {xR>ǓtgIuommÆO0uR,#`o@MYx%B'E$>9ɤȏQrAVq?BrnyuazSq pXy͉giuDJprr̳glوxE1R'X(Dj]v/t?wɪc,.9`Zq`&>BsT՘YmFy/*H{;fL0ك*xqV]>-r'm77}k'W{p53m{) !vt/nmhB@< qzaм`lu$m "dn߃7B}/jjE6d"f)]]{]l~5X?<MG7ngx;PV"[^՟_f,bu,O%-E+P1E63 Ek}Y{לq 8FہېV6$"YDR {gE<^^hiW,rc׎-d3 #ԆV) c2"$jLΩzZ*}p=XlGH \T=l {JԀE)N+ Iv?!*J@jK$Bߐ@XOTvƅFQ:+y oPVu GߋYbqP*rMrnqѲL~.up:ǥj2!H)v;?5H! ƠlzUY.Vf}kY|0flQu@ My;!ŬV=z+$IrKI`D#>sYYLkf#L՘`Z'hM7w@&CA˞`ކVx\tîV0\;ۉy5p܃+iW{-hr ?GɅ U-)b.$uu^ Հ HtUa|F 桑"ðVH9Z`X@vOB6jshEnRX1@Rv6hDJFBjK$*AWѺZa,|xU` @VOס_C gP( }JrZ%蕨ZnGģm'~]žk; q^TkDɎmydZôIv8{nBv+| Ϟ3~\4=7] duJnMfVs)H FO%jl$s[HNa2]-t ;%[@6۷_x.mP & n nݶubg)Je'2Fpڸy;V n`@-1XK\Rğ^z!;99gRhOh JV6aj H uT/ kawTUbJhDIT*pC iR٬$%W{ #RkNF6p6p9!%S ̨Iu8TX!!|7)&M@)Ν{7J3:kAaAꋒ͖s)2k݆2\6.=MVn@as'7m7/lD:bߓ5N2>{ cD WȻ-eQw^e|b ͳ<6kg*+Z8LS)0>Q52wźsoaI{9G$ԟ '=N{Z9i443f-z1<0|P9871PReF:,&QxATX{](YS.>!%ےpsQZڪiՋ8fazk<H!@`Q L6[P:I 5.i͘@+ƻ'P^ǞU0ERd? h0KUp~W5D2[C%@H' Yl2JXS8K ս vEaZؠbs=WA>bQX+>nT'|tDdDuoU |6L`=Fյ7ԆǀƎ\@F}g]S XʌPGu %@ʾâ &%eO@PqEHظhv"BB EhW7FzZR >:f{uo&2uP(G>0|5m)ӆ-sJQͷV0WKJV2+Wp8xzz3 -d/\By#΍KFׯZ -Zi>;?wsR}ʸٳ $2N+P湕m&m S#Wb0I8jkQۀ#mb[Z+㨭hBpz{=`(Uyp94MjcDUSb* 6#p@Z)Nîh!)u 䃩h#Z [49zRѐ{qKߢbs1&Qד1!*~:1%Sy6-`M''GrޓWA\<Vj! d}GqK |%R{P$P!4`jUb~A"1ؽFMDʓst=U%A]^ #:+5B!ӱjtKER:pWJv+cG~ZTPvޠ)i\i ⴩c]E<(%9F6RLcR mOVwxjQjٻXy0I^* Υ$ҥ63_ †O|Sy7\KCJzRNKuZ퉵rFD:}\v0 PG4(B7ɤ;RHĬb)Gy KkhsШFa^3 :lC[`Tb2y}qh5^,JW7~׸zffH(1aɫzb؛ ͙"Z+%gb4~S-'l|vR*!vl}ţ>ugw㲻!5$`APJԓIRtՁ8pwHT5oWcBX+%-#ުEIHE<;~vg=Ihc% 0pуWΑPb$$/V.2fKF' jՈzU N$Oo&Fb2(e\cykeOrQ.Q;T kZ fnj4RcF1Dww]cQ+ nM {2B,9;4ErE)F__7 KvN[Z \VhAc-pnۨ[tSufڸ7?n#vz5yR9! EY+V5ȫ{liL^6{r p 1W?˟'mxݯq+;w'we;zBX#:Z7pf2u>NHy^h?#֣IkeqRPX~@;zU)Kj *ۛK?-KE& 2[Y5oVEJ&C4+ M.Y$ ŪOE OU@LS) 8Ҋ\bPZ9{dʽUPtcO$! bZQF]yiI|3PjȖL޿Ϸǧ_#"蹃N-]FVFI}Ge-B*&KbPXl#MV6rnUVK6v!RB%pBFJ&ֶ>m jf:;C/xW$]?> OrHݷEurG1G %'m;yӷN\WM`N%'ᒫ'Wv B"FMQcn#qfZE:17nL 7#PtM404kfv-oLfoȈK%g]cK]OwЖĢa  [o7vW035!m4by[uv i] C'p? zVHH8FnXntELG6xSd߰yPV¬CMQYM@_EoEW[.w:sZP&56bg4$`)C޼v&{4sJLɴlcBX %.6&%!gΕ5U j'Î)qf"Q ]pX-]RR"ք,ufpUJ5qzV!$"0HMOG%K%H%Ee4{[ }2 9R5Rb sU: r*O +3..; <*7_SfŞ]dyw- ZhnVU?'Rɹ8 {wa *B8>&_d$݀uQPGTU yi): GP_c]/M@dQZ[ˍ}<`)@;r)?s)?W<~:b1!S{+V2g\[GcqeԪ_+h]*[7: ndqQ*`52[:~z.1 [н^n\X?}=&bEnnwl@d_G?Vbm?y3b%U]£Go2[9)7yA4ӟJ3_Z\` tDoxt1{pysm )bvnCQLr80V 8V&:K1[ =gugPGGbKtD߭YD ~Ml%fks:F=GVI#D,$]# f?n׾6c鈬b":':qTMC=\NZO[E6%Owwa]ޱ/{MP?|Ƈ5L)U?C^};o?6~|*UX¯RKpVkЎ3`-fT`A-|MnwT{WIriֽW BPu@פjӓ99aun-2r9yDՒqoDj5 6w4Oy[N0m(X{q8Gjq6c6*)F0_sѐ%q=\ՏT[&P.BB<'`{ܿk])" <|S<} ZroJ{p\[toU:<3(onz)2TD<~ȧo Wg)޾/EWFEBF؅R(!ze=0N8"W) Z)b^(NRL2̐ՒUmOKVUSøJ'z[+]4i{.w>% Э7)Lmɮ9?yj=Ϯ4MBdcQ-E$@zb^c^1y!:d]b#__tK\9%fJ1YRIۍI8ՁH$0qDcˇ/"$=Ɉf4Z9{ A&ԈxNz!)hթER4ݢf@qd+q-˭H,LK û_m:d$ W{[5}eGk ԁ"]\CDzDl~)Os욌~G7a,/i dj#Vsm1sY{fW)q:!ɥ0JfžQ FĘdҊ;+"H=kMb^=fQRP-B;Jl!l^ژEH"莌P(ᚘwZs:9-lLkKZ&oѺU~qa4DdZ$ⲲR) i64 Ŧ2uY=(Ư2䝝vodsSzŒ-oZ4*A2OqWwSruuME>q oQ+\=yDbH Đ.qqO{W> kɂ|!qeUZH{Fxwu0_ҶmՄ֟}v&!sͬhU<q-DduBޞelz q.H@)\=cȌU(cNRF3ku }!gKf#=VbJBՏ{\ӚRG`bsDg J'/uh˹Fl X3s4"1$KbR3)t~n5Vi-H͉hn[1]gTUOzEV~g>~Q@:X2[Z+ ğ*^lƿޜA3 =N!-ݱ C.jceYʋ39JWռh},췗~,|j%6]p$@ =dxֶŴ0G {'8;/_mZ7YشVrF_ӭ8/H+$&[5'J!N:GT6Mw}Fj!de-+6㓗tǶ^LOwD\jH.nPWepPhPVQAI# ,}P! j*ZFskUV)Y^+O8;?G4c~q~׿dqQb ʸg쭸h͏_ͯ>b,Eҹv)A6]H#E+[HA"tfج"C\GչLarV(VzL=QԖt#PzK&GQ1 +͹ B[.ϝgD:}ӠZ cfc;~ZlPܑ20o I(L%xt\L^hU##舔cu|}i ΈL=[ < +lN^%o%Bm]pNnv _n;2?~O(Ơo>12m;=AґIE(Ծy6kB1QGBV%/W/)>xt!1ua7dނgB3 {[uH+v_D54 krJк#5+c-:d S6 )DRgC$@K)}k,57a^SbE!ud.R(y\8jeWGB&~b:A6Xja-0VX(bo:xT@4 )$Պt|;_yD g_K_v1=KcduzBZm肁ʔK"[(/p)'VԢZAst/t^Xlղ&9UZ zh ɬ*y8V17TBlϞ  =ð#aݭR+.VMxzݸesAkPgNU rqC:B& G94 {{ yU{2Үuu ;[ hZC[S`9mY2;_7~ O+S~'~7o-7M*nR*.fwA+J#lC;9Y:wS&}ka$㄰ :vdޜ/!g":_%پHANq!,\B`|َ%30"+&6R(\9iYpI=^c=;*ٓSP+aAZ7->}ιEYbiYe#0l9Qb fF 2( 3ʟdIgq;v[H$d5uϽ%~DX{9~k}׷QmjUdwi >1M2 4Ȇ8GS~w <}^) sKhVq.*tLQ?0N׽F ZqըCU.1'jL,H?+ϕ }{4i,%k!w6:))&5S,\P }DklcdrNļ{@Ҩ7zu5bY=.Z>uůY]${_]*^F; E\tB)̫';/|40F)L. eQRD']q{qO >`dы=x+$jZQ&Lebw~/%yiY]AsƇBѝY/E}= )%#pmJpW漰jn!WM[-5I*h3%tI<93vj\֊N&G*w[VvRG-mWlC#85"$< 4N :knpQ@¦4Cڕ؝v-^5:1xGHm.[jWMC*;]Xa>7Ra{TVOwwՔ Ӎ~*th<DTֹѕ 8xlFYnAhGIY~_#DbLY#>NJ OLq_߲f#ztrlHa=]Զ"$+2Y)qYB|F_ʴwueE$лX)+=u-}Ûۙq.F,yчDbS" JM]vML%r:Zډ!$ iRS%FR.8R ubv0$RB ׮w(jGVuQ 0=HEMODTb>TPLb1l/HCv'҆R::Ko!q6DeQ'ܸe2/T3x#6㆏zWjzyßk@띖Md%rp<ܲZfs`DiV(e8胓vO!q~npW?ޕaFßpFr _*_6Ujگ=r^c:q#KX(sFH1)Bp7R0d gog{]#N03CSۯp3*?s|үFd6 aP!,0Dvs׿#Ǚ2.o ..EZǯ{'چ"*NxϾ[xyE.`DS27I{D]1š$.nR.ư8Y|~> [.gDbY}"=^AoNƅz9ZPFH&7S)3άqxJR+dta| Nh-⇈MOt?. RJp?\7^QokF^<xQQ톚Ӗe7d2e֎ҹ[(;(zS0$RH±?'^p)巤zsPuNl!X_O v-񫗗|ƈh&>Fuūw5I{uU سlTJN 4qsG.DpimDp}4[^h ÎT?pxR罺A^GfMqZgp9H;xtPpjY0jPr{Ǔ7`Y޸< >|$xss&Bh0! D<xvin#ئ!T |`fv8İa BqW=tChLcQELeoD1NA?| Jh˂ Z<@\ׯe168vi`JQ岀߻fݮfSFK Yix:'\wx(qh_Z6XRیE jqؐ#_y6߲.W-{?yb/ˢN-̍i펺,2Q[gJ oPsU'~y90jϕ̠EDm#@iE)&wAZf8]W ҈t Akz]|d3n~n:۞篞T4,&hKQ];Ǵ;{W;I?cHlh &W¡̦P{q\%xG 猛G|m=t)Mh<Y4qEqmذI }`I֘/BzJD B{6!>@mfqR3\w=+ 皪׽ଠ ,2/qgt$}i#ިywk5[*VX5ԕŽ.MZSu-N ̧sb5„UeQ$Ӳ??xxz~3M /^^,.8N6 lwٱ0H_ )\ l,uwحI)Ot4-NܓPJN6,:Z*!+TV:;A&jç\^< Kյ7zn8fU?hq|wbi!9?|v[!`v y vx<)xuJ@tđѠ' =@jWo RƍtRgHaBKpg (2a 2or,A(DII)7MpLy4Q7-c R[ ~CvmALT3 x5#s>RM>M1Fo*P{1[e 3I ݼc sFuYizlSrf|fg@?u[tH-jW ']+t~+ǔÌ.Tቚh,8`qŅ--5:扊gR^ ?vpJcdzu9\9="PXNɍDH q^+ExFt g)G2ԙɳoӜS*]cʺuq0.nÖ`1X7Q*liG"ZBsn#[h6/B2!(~"w8j" !Xjfn ϓ'$I,deL[e{_N}/~m._]R$/~緿V34Lv 9M8KWڟg|ǻfy۠pxN&cިebn>85/_-jlOz7Zfǫ ֈ 2D̺ jevb |nb^xh{]SB| `B~ ſk.F치T !//T5q?< m/˞iړK}U{z[LGGo^1GWsͣOkŠqalq_P=n/~\|^i{gѸ*E;59~ʃ#@wAa{Gy:X'ט3R*}9h9ިRwj˄4Csƴ >kgr%y q` oΨ^~ jg1mH($cڐ7 ad3>2 #)FVs;nyI~` #)SZ_tBBg/,c;@ll޺kζOJklRbwls|L4(A5R5r,Gp6Hlvg?2ɭ(X pÐ pvm3qh&-a+Κos?Diyy%Y;56F3Ʀ5+VKq}CHxJեC'(~1h(OBqj!J։ֳ #|3YOiz8x` LT>PRp" 9[/|g{*3~\K7L~e ks{FdAӞ.:_u޲SoHomJ:.Dt߽+eOt[(!00?~ Z /{ JiGhĺ<]7%OStr"tؓR^P\%C٩0fwAzJ8&>{=N]|Hsx4 0ϘF ̦x a!͖odc` xv:5ŜO %ƤOĐl<}AB,҂'m6xIy𙷈iD.\kte",SZ$qIan6A>5x[j CE'*~Hf!!+E XHmiR~tz݊8q^WF5m<>F|x&=K/9昹& PO\l9^>2kjV‘q>}D[l:V#l!op}'UP#1zҎ !P,Fb])7CI+:LỆצB"B>@6a1j͌1&yL=:@1>}ҦW8W@5a#^N7~"~kT(ײJWnPƛFGJu'B,U}Ug .`)6њYjM.J4`'z6=# WH/U~x N4,k8-A]{GUҿ=/2GB;"ЛV4(7,~PHnKoi#uB%2jr]Mo>ϼ#Bo /t\ IDATo6]7|N骸| I1wtV`gvYxq{4*_Y .9&= -y?1Ndŕ="n!iY]XZ]TvMCd HF镸"~xFf)Dž|,N5w1tpq=j#z͐Oa%35D 3遥 ow8>|);uqCxJW l O߃RNGD+y78QJH_nu]k^^BߥVm3ݤJ+wm/GhB<ڌufgb,JPKnx''./Bl{Kƈ:$r U'm)8 !I;/w\̵ӷ@)$z*s4sHxo7bSg!y!oEJ<_s]a;A\k#tO0+b i=4Qb;AE|+_!W~]`S͔PYn=N鈬k.!F%=],*^9^?c6{eqH+'OtVnU; _)!~Z=Ma .RC[ 8Vj.$tpK^.,η~WNN* 19i@A^8ٚNST{l:3A;ȝZ̷z&7<]LA;1dgƾB*45h%K0q$F?R-uvL]f:4M"1 !ѼZ"5FdG]9gg>y1weOY- .%r1d!A6HWoJ'K?'~&86Lٝeb^LGqU)rSe(.b9je\Tֵ3Hw sDw0^ps#6Q?Z|X5:p}#^(Ȁ N2Khb?[Q+4<.33ڑG@w54bPD[kz/}mTT'?7t5ALY 58>n)A7Z' ?X2Ϋ=X1f44<)SMc'h'N]9#';HNpеrI@A¨pq3$LׯoY1n\t,% 'k ;YY/9MrxDX$u-(#oLwTǍ9tZmKWƒ-4Iāyw{avCgZg쀳% pi/Hᴔh&n[tD(947NDVwBFQ#o~,A!'f"e\K]Lhr]nNptwQ,y -W⬶E@&mXsGxOBqq$oGlhlC:'-0['kaeLn\b#ee& [ЩlHi3L|bk\";9=eALӁNq.P7/>C莇!{o;̡VSy|oz+@{]pTKP ՝-'nNBZBV7KwKF Ki5I:1xEAhB?RM~>qeƵ&Ҕ: ZlpSN9ӍA5ٲ;Hy˿EJEll!N'= A_k lXe×[}G}׆OgY=dΪ_˹.)9q43<8C2{P͹.obE~4W+5A[OaMYCԨ5vLO_tycx=RhmV. &yщjm`B!z", 'C";1βJݽ װFQIQ<qո+8eu,:&ZqޛE%CÖ!SǛ]rL:Y:u/3CYRM,TC5͎=yJ7$BnΉ!FjB]ewтp!i avQNBF>4QΔ6l&Jי'Rܰ iCi ܉\KgM*76=8=7sY_eln]$PoD !.Ń؄-0λS[m n~aGǑ-/~|&*jv*K,W¼eօ%%Rz#7XuFY:fw4 ug-$D}O:Eg~s-KvavlXeN)r$.삳mClwN0:wÀt^m\q~ɣ*Hh ߅o],5jB%5WWJ՝2V N[b)(n:ǀ4O jq&m!ﵟ0ֵخmzw.jit6S90@ku =8fNna?EٕB*p4 ^"!sڒaM˅*. 3I1XFq3s|̔o)yϔ3s)6{:y B ōALTRHk,5#R9ۺP#v]=r^̯xd"8z,N)&]tqŽc' 3GBôŷFbdnkӟ78Ok*U*)D&,Y3Dmr6E* } ? {)t]$ZNM|TK>Pptר`P÷N~9tRe[+mihL7/>Io7|éCmJC;Z8rn֩M^:)Wq\-ޘ:!薳"N6.y&7^h2])>dg8o;&@EcPa@mS*#.#͵uKwzz ͐ȵp=ԌH#d徆i9,WI9k%-OuYx慗+W(@*C@u-qfڿ$uyٱ±@g&ΞH/\YҀjCH$,E;݆39Xxz|M 6A-  ޲\_j);VWr9hˑ}D ]ѦAE?E;bӬ~eO-K ~Os}NAfނ۫yٻV)L.J ʦksN3yrKAZ,MӖ*iVDV3JA+(}3Gn.D~~O_7n6R+|߶NJBmϊAw5^W3|E.iX#-&WbIsE BR`u0y2&VKvWmYk0f~b2t凌B'iى^I).r_Io.] g_ 2JH肓fXkNql< Rz4?d5VY|`'há-83-*T !B¹@ 1ĐH~8q< 21 vDCIq$ Cu*vk+0J<==*!,gn3j* viGnܙ#Wg7Xze#vo2U YMB4C@|4Pe9ނẐgjyC虔.d﹞1 [sqvyc|YJqǴm1Kl Qb г~ݪ~Tm-93HTt%Q*>z.I' DZ #۰!O/~Pkat2K?q-\,L1u]%$s2[G|K_A7*Ҁe{tD!",# wڒ6j]j|fyi'm[5eW] 3͞mh+ %aЯQEU+ y) VYyҝ [>qjT,Նy$d QޙUq$A!ˑ^a.N}rִa~:]ɼEcuWф>%Oɔ(LT}Ra|_.h%Nv|RHFUgwjO'^J52hyBb:_~*\ `6!D3z3ۄ)l_pބD v:$qT.rOڈ}ֲ?ԅt:Q^qqCJ#1  {idY&RX 9aGH8qXSpԲ ͆c) e92{B6!A/B&[Լ@yf- !Ri .{M~?6Q &CUͰaiKAqNQG];]]G(xY='n7Rxxj#0"FUNկ+kBW ;+R3=dxqsEՌ2P:ΒeKREֽZtz6A-8b}AVԜYc~z@%syc_#7;oCbǁ&AAcWͽUrN=-(Gz>M ؒl%ga.qx 9e! k Q~]QqGltlk;z75YPFWq]\ucD3r1Xؗ2{<y#E"sB"d 5 !DBQ5DAZfv3OEwSo0\/eRNMTl #:삖 y![jЖJ!!bj"F,GtَƆ7x1TGPX-rY&h{spg><#nnW JUMڅ7PvFSnMxȥA3sl@1YK|D)֧?OO~wyg6/牏s]ntƠ\$\n(C|-0=r~]M 6I;1z%B7/p*ƍg-bM'xe@Җao^+U˳.6uNMNz`v<_q`)NGwWqʟgYBk"}Z{X}5NKlԃޑhS\Npո_azy(BסK\2 @:iN8q;tG kGE=hԏp($oB>#ʽ21F-6z. L:lTYJoj%*9/FYu9rqrԇ/;}w~$iCsf"2=۸%gY\\r,46~˛O˖Gac y2ȟӖgKn-nECsXҀۜj VKo"o~둫dֵP̭p&#. AHBjB*J$6GmOyI'SBbru͈Æt+cf~weQȁ'<㜁?䋔s>kH^fj:́ nO^;SO9;^̺MP WO)#-vMt1Nk|CtiHI&xZ7H.oGއ,?a%Wvy~y."2H*ܟ=Qs=Qy"QNMQe,a lDҰ1йѯIǙ:ȝ1l-.m^Q}o%CH=F#oD `˜Vl /!aBgqVT=9۴mG7o3%?az(CR}wk2RdcّfTCѩx9}myCUw=f(xWJv:W:cT]h i㕧K}l 9u}1 7raV{?p8$ IDAT1Ecu.z& ͬYIT!1ZHs@Şd4ɔ5z"ʢWD'klҡF-)I|\kQĸ42WJ>WZ)hbܫ]ZddyNJN` k Wb½U`0X1-ՠ4ZA3FDQO{ Y+_A%)e1XXUm=9LsdCpɉW{@}rtCa-Љ=.a8EWu6ʞ3o<9q 5P&+M҆zŧʈagN2DֻsbRRݩM.Mg^x~Vdd_39qjH,bhjx*yJXcDzIbAlf}ːŠඕ9(4\4) 1ɤV3<=ͬTEq-Hi#q88Ջb1r1X.beA#ZQU{#@+k;@"vesсYUl@׀ 7V? ]ZQ0Idݳ]`jT& (ts VJ*ӡ5־r%=U} *)_1̿jE$8XQf@>ED.8cкQL6Y#0BO} $\"T!?wn&4]/OZ VACP'lQh04(k)6LKڔT(TJtйF0!E+Xb%%̤,U1"7%@ħHӵ DHom!ذ-IS9?aΨUul\̍s\x攢(V<~{s|=?:dq65h >:ё+dR0U89/{!Fb(MpZ#F#3*&0'&Ac)mFLE"X7Pʰ><1 QS/[\} yF]A3[X,ڶ'F"N4nNӝ 3P:#KbHGE1D&$ h|5R\pƟҸ3r:eKY5/; +0s [BhȲzNgDA$QS<$-ǐ2WG* 3Q{M4J[jYMVBN=^ofS2\&EɷP 1j0EHJBiE O\Ŭ"jm&<]ZLWѭ3jw9t#b1 ǣ0  y4e \m%t4&zӧw22VEU-(JVWp@5-ሙ9DYY(\fh^w=1v% YE@ ^NaHF1E\jN[IedYNRׅH5|s=i[Z#U{)ȓ`"vcY0Yt'/ȗ:y\ɄEPD N6s8ՃE`]$ K69LF#hf2sPyV^$b9wI 1#,됭<]nZE8p[ɔ J=F%!(|3HTAjJAlW 4@]ßJHtjE$Drk姡K rX@ɎIk1D$ق&D91Skkm@2OZ )4@L0L[!1a@4 Kҁ;cGOX8rD l鼧$yC0 ʬ,'X6 -݌Ɲxo~^zQU8?&\kh./_nSJ??!Dy? G7X\|Sn޼N2A4 Jor%o:`oon~_׿O5gt܍EH&Fn9]&7^x|'|Y8yje6 S;"_8/)67"p)хu={ow~ <?_5O>=%jK#IYVʤ#Ğ;vvG|+oꫯж-/_f\EIҤlnn/o|\zuHjkiyJ8躎X,ܹsooq#bgزDWȐK1==Y< d C'(<)aHKκ' h+RB$0@ h_w{a;䥓WVD$TiDGj 1z!?L4gMÈ5:kmN"/nՎ_ʍxSv{8\D?0;!4W2"*'FKgLFS#ȁ bZ n . U^12Q匫WڞM")5 % F+/k㒣kuҤ]軭5i%78^ɫmꮧLmNT ]:*'] hDo9d-cf)͙nU|ዯ[[\zdN%_bXk)`ss7 gѬ]*bw#ˣ'.x١Ғ9L fʵ}ƛoC۶ܿ,8,HYP$vFts\swگ}c,w?GDfcƵPe( E‡#\q:Pd:b,3L/|od2wG3b 6uFC4Yqw?d{wY4OpFJ hZZC1Ϟ~qFi?w?#w=&JC&aÚS5Kw}sk=#JU$R\Yb/[IpG\ˠa'QLF~H.bsPC*# c>5읟yK.a_>'C!%*˭5O,XqO9<9byv8\"1+E>D.O"Ы"$qģV7f%.)yT)*yHѡ)%d১7= X2LePfz`uNn3BdR" gOXrCJҘB)VWQdENJ~֠3)b"9>QRj|3z)S(IƋHcw(tAnŘj-`@fr2Y5>9ɐ>K\Yș(=M7E,3?z-vwwZu?֭[?gs>|D]7xc@MgF%Mw)#~?cyG^6ܻ{#E/x|z1- k)-FEJNR,G{\ti=,& .]mnyovNoR#T1d3ѰYp~⥗_d=NOO}6'3b2h)l)I!,ؘKL&b\xYBlPg{GH)x ;RwiŒS;k{ϯIKk-ΝƍYNnͬ2yﭯRx̕+98@-Q!)?|y嗘N7XUZse~m躎B,ee[pttJbHV!rK`E׃q+=p |E]'W!RV|d_A:G RR\9VZT>9TDWZs wl! 'P~0r&j|ի  ѵĖ$ʗyc؝Ƨ_ ,#? ?k &ѩ!9!EBr(JNx> p@ D)vhX.069E;#L^%| Dr]!8ϓOP<"ڀUmScŭt&v-6ԡ4U.0Xyw=du!P A[kAEk2QYbvKM͓P7*Ệ>,iS,k|Mvww14 }tow890@[$uphRfI.f eJk|σr^{5s%^x{ũKtwU7/0S_\~hYkUoTj(ʜ*gM37s|us66sϱ^=zlN96*baѱ81K/o>).]b<M7d\]$[|Hldv¢>ťE'|Es`\5StQPnlCjU&dUfHa{5|]ݷhy6&VW^AUUﳵ)/z4 'W?K4=󜃃&g}>wea{"vPs:"UQ53NZNOd'@^Bq4O|pC@ˢ_YrMϲn9p@w7>rg 9!hkօ(=ʧ1%2T%ɳIA3#7G'=_g(eh\  !D?Ka 98 $z_-zA.B=%_AV[~iW!- es ,R 1%Iv Qr!>DElKJd1U#Х~04hdP z0z IDAT6'(/I0Dw̛F()ks E7RW+<`0J VL\;U88ȵ*t1@;QKKC9B l&!\AIq C1eGCё* ^|:o%.\ G>„bh&ON1tw=):h Q3׾˗?QJ9[[lnM1Yxk __iʫْY:bP&j{{xxƓW\Y4>Ƴ)vɋ~hOcnHLEQynUU;iI*#IB8cdEapY4 mv56T/s CUMxƋ"u 7<$c#&: S)0DxX!)x%:Ay>W/|kP]a5KK` 4tAx28ݙ2q|tB*&>#m:':(D66,5t 73,Og yATy⪙uRUk|nrR!QVf4}OښՌ]BiK9-AȠ;\ȕ"V2pƞ6ErDJ6ElM' .^HY){Ǐ~ć7oIhYB)%#3=};=RM-ϸ{wW_YC3qeYG4jf2.@qۼqE[/RC<$-5lL+.^==DMf HUCr'pZ3pO><1dȋJS$D%$l +wdS'Eבi`M}E%4DȔ 3&m5R\ѹHF#q@fJG Bw1ud_!2F tLjMcEEybTnP(& 2NyCzs{{75 J˓2 ǀҰwn\gss3ߋ`sk-2qs"$\QOGc8"-.^:`<P5tR~OHhUо!w6J)ڶiFzW<;ѨKcuCnJbrưo7!zCtc5;;;6>ٿhTqH1cZOJGtkW?s=EQx<B-U6drkQ` tU-1D$1V]C]+*#U h0RCl@OhsR 5IC 2 vA1JvHvR4mJJda '$ᄁ O,b Of_=yBߵEu#A9_$)kRb:1.bJ/{ʣXH;2!24V*Z g07Xh H0FeU[ z &5Ie,GkbW1ܟ:p53>]Mq&EGK {mV t:u=UYa1hOkBG+i!FkKy͈&Ǵ=>8Ya#'#֔ ӊ#|B5-%mUdYK]6ԱSb\QoF3 ֢ aѝ{&DRĄg*0FP20DC^IvR–J32bSh&s)=wsvs !9t&.r|cTefմ ]=,[d>6Gh]PNAa j_R/b3˗/Khsܹswru\1FvlLƠp%MBDLFM]tXSM7p/WCy<s&cnLp!bthիL_M9fssJUh1-&l;$4~o]+ɰ)'dL1eUUw+X$G0AQܡ+J!?hYDA"L`5:Zbnʵ;|w9ak uzǾG鈊FiXcD7@ҦuDz lP ҥ r?cw?HqQ2IKX!i>WU- Y5<Zy3͸ HkDZ_k&SZ<=`jmQ0(u휠 ''cƯ{Q]r|#Ub ʖ̎UvhJ'$ɠh >J0vz_p/eyp]pU%AvdO+SN[hѢ4[[&v[',9?޽{dYFY_ŋǴA9(ACHiwwCX,8>>f{{{=bss,}BnEeTct_zݝA8|rȭoQ/j2zOFƸ@I is}wxFkxwI)qdoѨDkcq'6l z9mC Ӫ})lgT2ʲdwG#QC=$>.|F ! 3z lno E=5=*A<}.yc5EK; 1 Dn1))[SJF=P!Jg T˯xx|C ??&\~)y_ VA} i{FB |ҧkzDrzn>5};(mmz)]KtfQziaUtDфDZ՚#]N\jb8&xY=kQ4ڐB&UN'\W'Mp=P1]fsֻK.o~m~s}%wݒ%f1PiC,1x #.%iAbZ'8::?䭷dssc [\rqA{Cɗ$עb6ׯ?֠D\ 3OzZՓRb\rvvYloSV'˖y`cclq޸QşcRoHA259f!TW s#h ]0p^(D ʆj$7IfC :n~p"FT|z&@7tîc3\=EtLWC u[R dYwx8kuI9=:ѰsK1VD/Idd: +Z%N,۸A)uc|3_oĪ5њ33K¥V@X{-`xO.i9:-e䛄lIdIu՞sm_4Io*o6YbRD~Ict묓[5ES 39x0DgfW*HmJD[yp] ?Er)!J#Iy {*(1FUyF=6Fޱ>x&oą d2^cw~{sxx:iv6g1_\64ubd1oh͌Sll^bkfnApm0t:+L'c ];=#z,<pǘȁ$3IJTJUj5m~u Æ􃿁 _~'hejI T*drd1G {>S*`dD{k_!9LqMvv"$P5Ox%HӔX[[#SJI-fT`!/^kAwJQ7Y@@JNex:$!U9J:FQGOO Wi6<~!kH<CX4 w` !F!a@!Ty$s2&*Uw[QU14z[ОsJ?c%I" *.} γu."̏IAhD $#f8k{T’xXDZfYpЬDax$Ns!X@0]8 TY:hAJq5l^hDNh))(5@, $4h-qc}8{qjlIFTJDG젔,K>}~3>s& ys||h4j~.A+sSuxkB]664Skn߾s$IzlunﵖǏ_wns=W۰Bé(<kA%sjƄKH]x,NfsҬK KaHO> Re>#0v됊N>\%8 rSA׾6^|onL"$`lZ!`bsh Ǽs@E!>Z"ǛfkSUo,-:uEADN^4/D6FHNx[YlT UɆ kp3,@1y%j"5/P1R{R8k1y5xnX8R _,+k$Ż(9uEx,h6/P>N r,"749YhTo=1< 9:#E0DF ITH"c.Q < _kσﲳDžcN t| ޺ſg3_*AYzH1MS.//j%nѤ̚ϸ͛7ş3^~ILv0UE*s;akc\Z_GXWl33(u{#~>y_W]^\\pzzJYiADŔ,-N贏=|OJ1FPS|{7ZO0ϟ?⾽`'(OQP`iiZ9_~%/_|^ϕf9;.31sḯˌd/ܬPoL1Qϧ`=ݴCt9Ϲ92ԓST)j.-Bue[ kPb6o8&ZBy52q^-¦H!o2ng."6F$Cp+hjECt ߑTiZfZ_$K[NB`8ԻVzG53:2x|Yn!v,jIDՉ H,xq)4#tԍDK'N騵Q*Z.t7"Zvff=xMxp25U\;DQ U!tB"BEITA6fkkYlDzey褃+jy #,/ \4KY[_s +NNNL&1N׎R{=666Hrh<O0{tpq||'<Ƌ \4ssfGF{GKTc 䫯9݆!ۛf^6Q3'J!K1u)L$+ܼyUZkd7 mzt:qy}$4y)rvQ3t;)`n眛 :J ^/Hź䂫G;7'7%Aam׆<. p0A؈ 0͕K~ RBJf@#}E9i'yp? Tsx i0qB~G/ql-'vA@)䄅 +R.q O*L$x!"FF@TD!*8 O,c[?o@)SָyB8Dkdkp[b<=G QI[{K}-Y8!Rd8I$>aJ-dgPIB!61gV_[&gYL<$!VW}6>޽{~Gszts,%3O1:E؄h³FFU%NMnvk_|eYqyyƘ$4[7R-{a^NxϿdIB%gH?c>zZ hgg;wܺuw} ݥrvyA]x]K`TQcSMc\ERG];F[~~Η_~ٞ0ϙL&Xk[(ikknKrfLYbU/YU,h ucgp)*%QWhZN"gL4ZZLHj@͠nQB4O,j)?O?sZnK$mA[,Up5i*8t"HjWPd0X&Z^c^z%Ʉ#Z~^k0&B;BJUUqxxȓ'O0:XBϱ"C$Е)Yg((^\E*GwPܕQ)U5^%)gqA(%aac&sEIib@ܡW{ RͮE4=BoT!fXTBWvP@.k%Em%H^Ʀ$V-,?j4f 54z/ĩ^ P4yC}TWp @&ip䂮&2/ >o_ 4]U%LjC(uP |5l iǠ*K1VW)"!A+"SÆpjIŴYK0 y_(eh+"FEI % 67$* b)"+|B`mP{**pԮcU ]!tdVI5‰ +S{͜T㣈pΝ6&mo_<@Xs]K4,8=9c y3 ؽ`g4bwwظc>sNNNprx>/899e>~-umIvf!=M$i5. x#>| |.n;X$lt:lmmŢb#_g.8G) x2)F#^|xY2v%d@f6.O&dHmA*rS1B%xB 1=x5H:ԣؾݢr7A J}\lMӧ]Axΰr3W!]mQ'INFHZd?+DIeYq4KyˬqyI'(T2!W);Lm1<&Q)FʦضyJ5=&X| )cA`Qn/[[HJKeMĎ"G].ЬQ:*uF0/kR;f$M89G2wB#ECD>ا d]@ %lIwH@(][] tAɓLsت9iLuGyrA*g!} %W#r(ض-3 zTT| ңD ;H:!AE^Ŀ0-Lj4l ҕi8gIƹ QkNOgx"buE S )Rdc \bRKIN]qABIϾ'ѝmK7 M3海okը (] .1{ ~QE4xDp[.u5R RI;I,v1Gr!Tʑ:f (2%o0\i&IH :cӿELhIwC]M4 "eswx_@ cVD\A6.T3и$!E*raDdT̫T)ȟвxk0F{XQPOif* | R:Lj_3syPd:Gjj/MέV:-̧,*J1IμlIUw0>*wecc,98xN' T1%\\\l$ Y2 f:3~"K{dI/vhK G( )dh4㓖V8N>IGkO);]FgzLL̜s>;ܹs]zL&0s|VꀋK1m(ݻwy뭷-Kf23)G,fW3I{=!7nt\\BSН%4blMig`ZDu]_sY`2ґ=tEu6aX.:ظk[:2?;D$k 0PPS߈ ZJ2He\{MI@x-%㩂N_}-_|lq2^0t]6K朠6ns~z $T#ռC!4KY%5Jjb5*MV_FX');=Y"6o,qropD~H@glU5G4K^4 >d#9,Zih>ӟɈ5Z\s, H"`zhzJ!ELPU q%K~>5k(/W xGm%( 2Pi'.@wPIFiK:< 6,hf|PPY!ohAC4l@ Q$PR&i{91I H駑Y\UsNO]ֽKKK|;9ǭ[шǏڦwH.j̬gU-ae (9WF0qvzNY Z|mk-GG|#O/ITJJoQcUEiĉ!Nmיf{mXz}s..FxNI N"wsC1/vQeHDhbJ𖺞T^kZi6joIt@xʫWcZKž`u1Ϟ=YzRg̦7oelnlǯW#3GfHVWhpʂe^Y)`}+pb/UEWȘ-h6:0B"'W>*ZI$3 ,ɨ .D'ϙpVN#Gӭ EHzu.J@!&\SKGtkXȥuk(t3{ Hx*nk wEpQ{UM- қ.zp o~^8[KIT" [ƈ(3|c\'U$~@SD \|0 ߊ6b[4Rx[l`]Z<1xn- sjJi&<ۻ[7E_W Fˌ =,C"EUe9>:f6^ IDATնKKloob:Jl+HK& o]E g%y:@Hq/^ru5ƍhhhѶg2guKW)͔qq)-Gn[_m+ܼuO3q ktIQAԮj3m1Dƚ"A T&@&1$n6ZH:͉6u n *$xPXPUB+*VAH%3m($ád@@pP!"·p!bqtgo}a"BH4;u0B05%rJAUrՄ`IxD}fnE4.xLcY UL2WGnTE&b뀔gˍ[ f!6 %€KӶRX,\jO `v|B]K&>t$4eHeӑbqxWc/,NuI.OA-$Iɂ@v^"h(L,J}rnRi|=$T\UWn2/?:SuW\4JR*oߴ9Avon~f)Oy|ZK>y! R $C˄D&T󊃃C&)[[#_~>2=Yۈ[}"XSNN(E{,--vvvx 8xO}%Bfh#fHIDlb݄0[nYs>KR%B t\@Z,AĺAOӆ%%y:$옃\\ciQ|V>y QA?'=L(fjx]3u4~յUf¼FDHܭ5g0dT^Pڒ4{kՔ9BhLC;$Ikl\lO>.)$Hu䀛PᄦA89K7u阗h~R[,Qc'Ȁ3$^@#\F*RRг:YBubw At"D"EJdxp -eV %<7. (=p̔5Ri Zw"a]=59D'm'uHbZxbе9bYXf Ƥ|4 k0@;pb#1 DD"X9B A\ +B+B4~R@Dx}3rB RCJ 1u2>BbP )hkJ1\5l-EF`n1 A׹9)G%*Ir%jk9N-ꋏ~g(g59$YTA+0ZQ`0֭=WxlQ}T e=2U=vET: t"Dga8_EJ)*?ζǁm%` -|xj1(#~9c؁B䡆F s2 ̵'ELeatCӀV ,*G)!>{P6o<}kºo(cB^1TdCYXBREmN_#YqZ"iZ2( Dohk McPP֚`t225Ě9Fg[7"Aw3ֶw.O~»J9O?׿ LJt% k|?~㏯8z~:d\8?=oJEqN<Ã$1 G#.AgytM:xtw5DTk]=޽œ'ϘN l%*ÄxrlnlE:SP9y6Y3j3e:+F+++01/) ҼG;('5Q]olyz}.}{5=hfI$$BHA,eal Bʘb;T0)@cb;c!DDBˈ43=[όݷ巽[xtDNܮs~<0;USe9rwN%6ONZɸXg0ktu(QE6BWbe3,b⾷K/do)d$/YAa (EZ_R4蔗xLg5`mrd8^i)ӵ5pD ɔWξd2a}Tڼj)HKMM$|H C<پ*H$NT3^@ a!3Yfp!M&K6&/zS:Wŗ.KubU (TEc5&JMB**AdҠF̺q@O8vՅ96ԭg6*i+^"h}ZX`Dz# &O,r][_t [<+#G1i<1CG>M [TX) ,76޶6ŭx_0T&1jikӄ[=D_ 6ncv,Rk: AȉQڔ;'ɕ@ɈE'pPH&b$ވ3Dr0! &c_x2VcsݼMo+ŪJ쥢, Glێs'V쑲,KfO la86: ɕ13El/՞X!w9{%AL/Ry_tt*_zL&p4g]i,0$,<?!vvvxꩧ9s<|K͈;)BΤӶ !$l2K{I&cKhС .^]>LR'2#3%X=\xWjZ;w/bV3D\G:b٢ҥ,kkku FD}5Fkn;ro~7MotfY`0`}}{8r}ݘėU!N>p5_~,9:Q\).ztI-.U!)4t]"Ҷm݄Υ5Z'ɓs=uyG>.^BH%F0i O-L#n[_*H6*x.p=&jSW f"QIIIokLjM"'ui(F1D"~i,dK8.xBO/#pr؎wY9O|ٹ㉾ϲ )1Mtթ]3Y졄ʂlp[BuF"NI6-TJhcЊ 6T./tJwH!&CX9 f2—4GEkë_kkk(X[[KP#9/0[goP$ԶhEr U0|qt]>>wvim,$(M Jv1L'nXlo_Spugt96i9FJ6׹V~UUqY|ivwвd\l02#[U"XfAkLΜIрݗԪysoCD?c-LkbTCnɝkP#mPy=Μ9ã>rK=_LzJ ^@&3Eån1w =vuБIʼnno{ o7YC|2z~-x<-F!h/(P>fa:N\Y%bS;id\Vs:QaDDx>`| Mvs~xۿ:Ndss'Oo;/"(͜(utȲjjňGX}k,R(2!*}hӾQyiNji,et}V7&yd3k;w.s>qݙR[ã_qh.:21Y++lbWˋ ߵ|4DA"%!B8icb\pK/s򁓫u3˼җ*K(Zӵ-.$_o[:VAO=gϞvc 6Ȳd;|FD&7Qu e }'ߟR:Dn,2u嫴C=E *c?N_dYO=}tH:bA>L^:nk8k'X ͶeJ0^K-x X~_ޯkǯqWػOBJ!ù@QhӃ=<VnR$6Fe ,GF׸nNgvOK@g!}v^:<''xʷť;9i>ug"v s{@#:XBMBP"tzZd#j(S຦'xp[Jw%{pΦMO]ó?gFôuEhkQo˂\G1# Aw4n{ŝ/*LŸ_&/"&bi-a(kD[(B`uh(``$E*TaE i%hdk^ùӧn- *|W׽JK{6BHr \>>+g 㮻bkkk׹.@٩ɄWqziN=,U00cns||[G5aAir)|¦A9DK^;ܹ|$[[[ؽŋhp2,d#AԆmv:_8#<`0XuGۗ!;bmLB'KXq_ғ1yg9kfm 8-5|Z>J8G3d_=EQó>Wh:Rb,錦I+ۜ?wNp;01`8`#7_x>&k\t?y^ǹ8I%v4-4)_9Ǘ|sgs%nck| ֆ3E3`0\&N~MQ10o(m*gwg§?<㮻9K/Iجob 7iZtRFe4{O>uG4L8u~z29rZ[nQ e>g30t2|lfy!.1rɫ'Wi!*TH BtU_>r+()9⋜}Ȳ%MO{+N2UqmC_;ldщDE\xGX:3O}צeQ \p 2]r7K_|\5;gΜUd@ɅsП9ׯsر~S%ϝjEM68k] ռb;]y=VR›Q:]OR4ED\L e58pEXiWN3; VJ,@tGK -ỷ'xG_p%l4>'&5<1c bŒGwu*G[孼mom[Ο?ϧ?}y 7(K8g0Y_pDȌf<R- hGi YSXE"8MuMb%y[$0WB 8tJ7Ǭo:cz!u4Ml6colA2jz8'ƷqhbTA#dBp8C13;;#m'rh9}O} IDATk#:^gg{ '}kGԶcgNh)u&۴.Ǐoy oСC\|}<,k9|:.*&)k bKY$Ɛ3v ,GVL=fw}7,2_{^9R@$dI{a~ 4;^_c<8|xn⮻nGZt>Imt/ե2w Q@ozL/S!jW|`a_x|><#!x/)DǥǼI?%26Hdyo0ۏm46u2)RD]xȅGJEއwD&3$h AFCnF)OjvwP2&l5$4ଣ!)dF" DzƉ;xnJFkyZh(دR)ŘyGqK$a6f`PT]D.tf*Sa41ؾr¡!9GQ>g7Y(u3w ִQ27\;F6XFɱuaro: 9><cE|h_'o!EF <}LLU]p\t!$ X/1d-}u:P2\ds"KDY^Q&)9O2 2\ޔO}q~9|L'3\B'V {` ,B "Cd'-VH2*9JB'FHБC %،r,>"d2'D1bAt ]-sxַjɓiiPb}S<1<̳|[[[k$=N1@ @2=]_ "?xǻ+<̳;'yś7|M,<_s|RKe-̴=Ȭ8|U^onđ@%?'DT*û^M}iM*@nZI<*otR}ݏhrɃ]QBH2:|@D- i) Db(R!ų[acB@o7+1R@!6D|cGJG ՈECL0Iҽ@ 0cxO]ZsR}%N;7~ky/A:Hu] h\.k =7kZp>AADAhgKq׉lvTyRÇփ>5-Bhvȉ^'4 _y{kGz0ϐ(X UBo˪1~.?z˿ķ}۷ݿCDpvYON}ocA7Zޥ %0Hݩ -fby$~*gH4_tMÒ(wMT$$Fz~|%7J9 %nJOϼ2!EE9»!cz 6v 8/Xt5wU( ̆$L)y,Χ{@KnqEG cZxPʀT$#t`h ]"@.4-~~uInm00%mQ0zI蒪xT)HuZ%nqD1YI&"Jz wM#U3GibT3aE9)){6QDf9 t!;a:kf.PxY | 63<% gѪDň֒Z݂_H2|4]MEZ!AodfW,'; + H ( eGb(̀Q9F PX:g"dV@lYl@ RUJ(!L HAQ )".x`qS4 #s~jt2 CrtT T tޥlCUDT t >`x"$ɽ"Jf@q @x^K.s_~?KR%hyImXf9w}_Uܧ٪JkD*-h아Rt[pY! T+p]_z~~~O?K̉}.Dŵt=fy躖?*^U*4But"kLQ8GN[LoK+z>}H[H:}V~:$ڮ%ѹUQ_i $˄h%D/9{bI6-Ve " W(ry^\JW/1H*M^DG2@6݋ $Lfb>{¥hlBX3PQ(t=Rf(UP(E)% Ң)48C9:K!HdN|&H!dMr Yu4.Qą^XYlVBǔT#BDۈV>MLddTxM [:o%ȤHUjГ5'jgmcLפ6l{^տKO%$"(eQrY\&z*>l2N\' #DӛyYH-/޻z~/MQH߳(Y_C;@e γk;\, YHώJj Ib!R XZTdQShC>Q)2'"m."Qm)))JHGŎy5C%@4l#Й@3Dw]v^|%SDa"A HB\eJe;[ 6]$cc46WQ,-o|7׼f8YwUn ЇR(=}3lj"/ha]!^BY7EXXIL2ij!.q[-V'uXlj;kkဟ?`6W=e띺Aj 2P5ӽm \V ֑C<\8fHTvbN&(w $fR”D-!^*.ArSyN#Hh'ޯ%#(D{Oi#e~s\zyth 9>>B!r I KLɤ#D!B2MnpEwxihT=òdn-ø)u7dJp(cg8@ )\*E a!✥wPl"90ӋJ\dr%AQK8B.nGOO4-\-8"9rIJJ/8鈑C#B3D)rd %QQN &V*DB!BjhA& &unJ%;iS)sM^jE,О׾a /] -28 M:*⩧{{?J^ 1Y SeD_{Ai j  cwbehrc$ KLҋsv5!BO}@D$^J x3"Gi3Ɇ#?f^Dyd*3l5sߢҋϑ ң&^Y1R#2u.F&ERQ1@F#:: K7]dKY6!Rdh >$ڦ%jxZ7gJ,C 3`vf0H1Rk Ѫ"# rDkH"ׂq6@922}$+"1Ȇ^Րir,r|LkG&zIE}k\zyj#:tXs9t[FJXV﵅'9[JQ}D+G.L;iT^c}Gv(0*ekjq'M2EBD(QhiOAgtb]s%aƦ'jAk/~A~ !}zU7OILDyhjT ons{iVEGcĂ@$dU4cݏDޘO}|N"KN{Vzj +X9ۗ/~+W%1`<ޠ,J!0mGv8a_zc\2wWcķ &"R/O<|,\ZX@tt]1DtffWm0%8)ɑ֥h1g"K*Pzʇ֧ME]t;doFi&B)5z8dN?4y]c 4v`n!4>AI=hmH d>uJ&/Xt õM¢NE'B*rTDή AFC,Y6$ )/|·4co \) abU2|GC%` {!CvCz/}4."Z7 ҉'y";|=ut?w,{*f;v_Mfmk>KgkiVizI<砍!Ā "C; d)Y%!Gj{)WT> f3R)D'A10`=·ȳL*ڶ1⠭"9jt1ikPjGtV9q%{%)Z8TtȐqi$et"œŕ'}~>d\>u ݜ.D069U\ @割KZcX&~ t +!sT3G!sf rp "+PLIx3mD|RŒxae=)]%5A`dN]h R) F\&|Oqa5 6 !"`EKOm P a$ ö)%hiɿI7s>-XCc$0kxt! 1#FA& B&{rX/fnB)wq__?0댊-1' ߶mr!pX_HKɅa E EPHg@g[{̦1v騴!  ,C=MS>4 "MD5Ӟ- YH zĈд H{Ӡҫ﷞9y{n Z$$ bZE,l@UTʔc;B9* ƒ- ؀I&HKB$$R'tw<콟i罷}^ZV J ϶JmIO^~)).=IՎv3X3DCx" U9ρ:;pR=9 |p3~~jo?WF?OɅ7p!.p56n/N!y3%4+#c0rR}UaS- }>':hɸ1IY5FSe.r:=zږٮB)Ƴ6x@ DW8z+I PʆJDNtuM<]{/釨Rfr_L zL.PQkΓJ77D[xyK IDAT>t]L{5=w=.8|I_be2a35 '~#ĺ2 D]Іּgsto;0n'̩9z=yǃd>Ɖ#@ W<{7JFd?ӷf6DP'9<=~bu>ɇw*oo`/=#SO?#'xO1LS1SqT5 _󥈸MA7=4!ҚJD "j[ERI%1-V8CIpejِKa!&6c$7ZF77_$*WTdD.>LM#&˧IiB-/x'#]oznBU_z 9743L ԵYSK)CJͧl0a.h{'E'R>eJ[٧i=>/Yëk=A,fTT̕39 +Yiz>)iK#-U2xF߯h-qi~f 0eEK!40-.xjŪxAVJ1=E4≥0H][ow[5A>f>0;{J̌)s,T Z++i8X36O$*m GW#hR 6_3e! W.pKO=!b>%^:|[AfET#{ R+yLx5Ί^EJ 1坹P}qg*A=> -8:Yzn5EaX{R43 3.}Jə4,ZƒIيsX d*BV л\8XFJY:ֱACv̮:~R9s4RV8O9awJyk(1 ek/.Of_8b ?k;+{>UXYv ޔDz X*s@U}Xja =/ݟm!1h-7\ۊgZk)"TYv/{ͫ~չv yaZF-):i;ᐊdѭ''t3*o%ыhX#;'tA"R_`yeő*@Zfj)CtEm(LׯuP/]ၷ%~W~gcD~'x>9D8jzK HhOpΝ!qqsӷ w25U pT6qmRq9W:lSsĒpA+FJ+> 4MHX'p:y2-yPWpձk T38Ag=(7TM2m i!93L4"HTs,BSg7 SF,|g28dijfk',Ê\ qKѮ^i Q%h)aS8ie s u I┘ u0G+-1'..2Nd)o<9}E\([cmKZZU4FƜTJ^[*c6 isĺ1ƒV@ZRhB'qdxw(6W7S#:8&3X' Jµh):GlZfb6)٪dhE(7\{*yēr~Nuy_7|>YZ4D ;cg)v^(ԉ"n<^o,21<?2dƒM\|ߓS 17adTW񌗾$ПE3ygΐAc]P6Ό]$M[ z>3LJv)*9~{}:|IR9T7 |Ehr(d#Ю ji~97RGE.,EqZ9,M V'nvW+y(Z6JIi^HA5AJɘ 4Hٟc x89J9oPD{r)S4m_ )wܑ,Kn(usa{WƖrJ&%ix?MڰOjl7?-H9\b3a }UNrfAB %H i"1L8(:*Lm-LE1t. Kʊ{a*#c\ (%,6 ꑒ-ڷ,ljd #$Ww4ƚαJafKi.^УHJ  v,X1M u&xb9|, Ɉ9B{Ɯa^4$au]{kEk2nAnQx;p'PK쥃nmme/{1g9;>{˱Cg*eUq@>7S%KóHꮰHWlnxZGM0%R$?^:5P'6r&?b)v8 ҇`nZN,:wxppqo'jf/JijM8qKu NSMFKJh6TU8WR;/}Ws[R3 xC)r^Wky5O dR :dSN]Te\Dz=AH2:Q#h9c9JZ"PheL4 4T5>9q+(*YG&r-N߹V%0o s2(bĨ<4jKΕMYvd&UI,|*wsi gS%d- /\blȓIloc!w<.NMBf= VNrʔNXj]s?goo|'?C?č,ozӛ.7O y4o}_۾/w׮]mo{X?[ #4="g?:{Gb ~E/jpZ{RLF>?c{z;t\`E*v눨R'՟f5}j_Ԡ(QO2JEhiF[&Dn沲î!(竚/"4M//~ ы3sԒkКOh[vmAzgnmV,‚{/^"N>bs4+)7)_d*\댺O渇eTUI):⼧ƸNlV+ վiUR̰%SfHj4K/~l(kT6oAUEU0(Q "˞9::21[P1 Fa-w\╯~)'hS*#x{EpB_\yX3Q %k1̶RL1;u tMGEi} ׷'*̦&ķx(|K) eY+eדiN8/rN:YPd)RKƹ/ɉqBʂ.r&٥Dcm|FR-dQ[Z2i&;׭x X욅nqiueJΝr"Ցm\b}8aMYr2-Ut8\21O ࡇ~7G4~q|}r]wիW|W|sU\>Q^7>`ΐa os!>5_l6 q =[\0\sAdarp;.s]ᄏoB a#X1F*'מBjVU[)3sVaX?N8EKDśrZ]"-EozoAz'ݼsaZֺًC7Ĝh<)q&o԰2l9U_ptrx++V&|wD4aVE5 GWX%$޳^RXP#ja'rO'^A$&oBZ(q8=$cݟJ)_M>/jn̋|V%[$|' L. JwiW4 tƛé&20 A ,4!Rڵ㋿w}YBb̑)'}O?!Ne4(븱wUn2= Bhu-$pG$XQ-OŖqaݹzd_BY￀+p6n٤-YxЭXX"b3=.\`g*ER(o)bPKHXк%#$̯깻~lsPG9# ׇCjơ!@-kJnIU)2DƗ/v:`F\431LD W k_Z^{ӟ4W^; ;) IDAT]ןn}q|_@CU9;oy {ҥK[4έַE~gIWWXGD\-7b>:mx61g;x "ݛt?1KwqppG0&-; %?zk}S( z;1fJ"`DfI9j^ >TĉC'zD-Q8˨ N6?ݧV(]g^fb I9{rKM]Ї! K'\;znѓDj/rpwN'B4EIlJƷ-qnә8:\1"JN[71/8s8CwҽVS:tT caɎ͂_:~@Ϟ"H a2Cxq\=M +^?1X+\~+x?3VNkKͺf%дn9/F BUG)L!X@:aH#Um0!,BGO*ۆN7h':3R51÷-4ߣxo4/JzZ/ػKl(zX3.2!z©,X䕔\˥ZįH97)kf msmX8H7kjOv ڞ gw3g.xb6DM[S:G;g9dVd5u4>fb QŕO~v&o _x Kj,Tw5*&b淑,Y˛2_玸v툣#' ҉mr|Gh/=Ɠi#.rNQ*!wAJv8()73k:|Ǥt;ENHVw;yYZq5ffbT X9Duf% i XD]d(Jh2iJ&qҳf&/S)_g/_a.Ұe:IL%#la!HW8)4}K})fKֈ Y:k#ʤK:g,.X%$nLG'4%p]`u:1NZy;V}J͏fNeRAZ^{@瞽f;̳C1dy[j5ж+]sM\zV~%v[v0-gq[qd f!99Qfa2퓏= ;F*CI9RXneq W]ָٲ)4liu<ǔR3"")JA*Qaʑ6XJxx q 6NAV|Y4 тEJ¡~n3ٮd<ǾaFy\ κrgδ۵z[P ~p:2NG "}|O#UfM{Zq634󧎪݄B{b-9bێu !Hyk"(Q_Ѵ;q}FB93eB-ײSf}7$?N}pxZveVe`;&Ou28o)831wɹJ%}A++1pZqZ)B@H`B)j(9Wu{!1o$b-mV;xT W7HAic2 {[xN>ozpaŻ~8=XB߶4S$cRm$' ,˭(]*{0RRxgbWԬp{qs\qU{=t*dɌ9[A"4^S.;[eڻƇk;}h3;>VUD6uW IB5"wԊ  %&GIꏨbUoªmY#Wx &8Z"_&1np3idU3ql큛Ao4ʾys:nr,84lM[Qʍ? p˩U4:ʜJg ݥ0_EHg΃9VVK{q!`h` -*eCV7Kռ?O lo]rBshWtemBڐ@|BkoG> ?_1O3tR y7p49"(q¹?Z%C.5*9e75lx3YWG)dDThKK.BTEghLm%2j"TtL,P548ojS4{-Wl6']wtAYq+m&CZiU9'Ƽo E@+YڞON<84UyqB9l&~ӊRcզKKt u#f |>0uS\;w7c)l $mn~XZV-ճ.X5 >prFB#A(yk[㲂q^lg#k𭝴vTΚ8wV;OQS:xd>T3YȆ: s,K/ڗsNfVUWef 3`KFeX,!1O˒l,3\{y.{u ?D}NeW Ė*}Y{X_D|Y:fƍ5\^Nh=!,#5FN A2P|CzkmM w1A>x??xwZ-A ]<1qZD,, !rMuqf$tlIKbRx?wݒJ7l7W6Ӊ*21* aG,( ]ofjR,Z@3۸%*RW 4ḱY-¤Qe)`Q) ǒQ YY 7Fv7<ë|LaZ{2\󍛟S@{ZrL8doEx=n4q^3.KͫJ !DH4 ]Nm(q ʒNٌlgx 5)s ۡ liH")M\ɖZ!L^mЎI#@%aBfᕱ\ZD^)_9M 6GנXPH!>gyt:DX<,\ՀOh@ݢ5Za3!F+F+ Z;;XD0sΪ"&%EDzX綰\??uH!VmvtvP[5QRk%~{U΋ż%2nn,9-.MH t3ʙ :a=O-9y915.["9"ܹ< "uIX{K]L'b>XW o;!Z):4X! 1ZR6 ZYKCmO!vFO:C!h hhs%s!<__~y _Jv&(ɒLcغiIg19Ϡ5N`4uWejufӁPe^,ķ@7;΋O~˻WƱpЊ)j2s8KI36,Us QoJ^u20TabcV-iZj iŎ~G=28jUE+c<pu)%yA̫5ei90)M?g#t9rϩˁ$̺.8L{ʒg2HT2c6N<Ɖ 9 ]sCP#e6h #%wţ¢Fza3Bp,'h nSKZ't,ȃtq:q7M*/LΠZNJ5l)i:I[ [Z vW,FbBȟ$#?Bg}\fUBC][ eGBAc2ӪT麎Az!ǪZkH?o/3 cC8=bc(y>јX)lF[z>z7#\wAWPJOduHBUb \Ü)2Յ23DT 09maW1QD_ױRTu&I>pZ(|#=7U)ÎybəSYisޓ7pbfSϏ -]- 4Yhmj T,jPDzͱ0lɣ/p_r!T-5yf|pvNClm2[RN. n "Vi|aӼUGJ?jRPik5J)",2 c~ gV=ޠ~-DǟK^z8Qk$Ey-jpB'oޤ\Ij h ;d+fb^+ø!%U8yF\} :B2O^XwUXAz r:VF%QŊ4Xx:$|kҮ؅_^bqDu%r#ˁ֒ {jM`(=[x)RDk$ˑeYm;:)N @ aq:Yrf. 鬬4OR }`{jt\HK6}V=]e JDy{×w_t}0}K,\1fuBMG\݈B%KhHIͮt$@RLa.'eŰuz%SCm…Fip)76n {,.AFHH4?{4wsf @Pݓo]`  77;>}BGCņPZkLQB/C3?-?|<ǿ{iWn ?ZV o|v?F&Cvy0ye j4{{?"/'fmUiTB,1#ljf| @q' ?1ڦQ&h9S$/l}ȼ²QĥД+% Ebz$R(TT8p᳿ZHm ,z YXИF2dQ9k )!^!@GZudJ3%U #ZC )d-hLa>~IHրDR }o4֜2e>BrT FP݌L;e1[}1#)eFQ6  41} Xg4-|5w|:@3`Jrϡ(r6i]_6 RcVZZO¹ USolXH%@7tNB@(Up9:yn~iw 5?#nۭÆܻzuEi\&2JZp"6VǾEY!UZe?GNep8lpR2eP%<<⦿>MT i^@ k|(T2hOOLy@hae&UBB-Yƞr?QHY2u1)^ ֻZE J3/3޷v`O|O@p:ueR%|k^H>Re陲' 8}hgO}H^91kY L*hk+.$o_w^^,fs .Y=z'Kt2h-5H˭ˊϤzM ?q9f֚ɽt0.6Qav&6JתT [o9̼,\@͆0(tnڒ F~C)͞ O\ݭ#Pyqv-sXⴖvCRIPiXBU! u . [&鄸W``@RYr6ڢ-s;t _/?śÄjcp4p~R  hQbEO#3H©Y CdnG܍ɚPE驟lD A\ kF@&elx!uJD@w= ƫ8+VF{tâmuj5[ah 2{/-^%n-0̶:t|E>=u *] x٪,QV7#iYDjWho#|qC[H~vȊaA;_,BZ<$,lwzjU5w.QƭΓ #EN\ CHܰn;RRr&jN5ɚIZ)="c1XxddF\SIu#r璭j0M^䣯~_17 77Ϙ'R=h҃B&Qmad;4. @@gKTif_4XSD%M~QXk%x#uzBx)5WZc9OK*\j2/C4((uW=S%O @-Y h>b>3 ^C H@D"R 1gjPAV8RFDtzCI N^y!Dj^:+ӳoAC0]=$(gc.腁6X8~_CӊP"!( 'Ԓzܽgۑ?<NNGy!/i*38! ʊ"d<^xşW_@}ԍ4˄Be3kG3B8'*M6/3Bȳ\R+2>)K<k A@u8 Ռ`afkBVde? lզQiSk S:zfj -+}Xߞ+Ao_'oK \[9R$؜Rݙ > ,^MN^ 1 ]sUIef S('ypi 22S v#5lu@k&y{['єAED SoEJk=pϨi$T[M7X3"sTǎrΫa*tc*=AZWOt^$je5Z_6O f:wfE FYj-a zNÞPchh=|Vb R]0]-kyhXL=I>2BӅ<a\)5/;sX[}}xJc֘њ1͸[hF~W/_d1|'dCibV  "d>owCNC ˙I D%ZɍHcXNoШ@5#yr}ؚ]Q) aa^hm #VfMv7gzзť@o[f_?O/hmMp+;rkR\x]+C:.'M»m߿l[vF*_$xB7M2s+LS bG]7T.Y^B"jgQDDKk3 ΀О{Kd͎Xq)Hajb K͔jy!@L$LqEq!!Z;q1,Ymc*QFzR U;<&J gK{vCy* wzqkONz~ aÇ?hUw\x" 4VaGI[Y8] )鈤]d[5>3?o6Vluϣo͟؁-ktQgTcjU1$lѺ xbWBOax6JiS7hU5@DlsE'm]Cq TkTLl}|.̌CU3NZV<}p gb 8G疃 iK˝U*h㼼zKm9Pk7^ùɪy%fX%Ud]A v66w=C8vV ]EqV3\¾eܵ%taC9N U[P?up.k?og*x/EPCO_Be{E[/\O?Ą!_?smn Ž^]0Vt-"1։*=8g>ظØG<|;(2kUhy/6j mUG:cdB,Jh+z0Gkm:B(= Q!T)%5m!7Į1ˊe:UkTewלw]|ZPT،[.kӇ*yOD>ڏj *h!k֤[ތt}Ҹz0ЪWбzX"+?P!yY8[?˟se-y 4뜼1;гTAcǁt0lV>o@G]8W!. z..~/~cRюy|.9XƖ`7k<|q=?4*\ U@c׼ml/os`hIl9"˟}xIqWbX74Ɗ񶕬kbmA"ۛG(t |,o?t/!S' k$egwDzslGd X $[ޖ2Q aPƯ0#nf^*b=} ߗE5ke=,s-_C+h嘶K_CWϵey+|N˿7/ԻCgz.ͩk%ⲹ^5㕁g<>/~Y~`ml  6FsIstOU{ȫ*ztadgV %"(7| Ed,klˬs}'Ed}zիN?ͯ]UGE",~YU,w|k30z`n~k^45ZwKȍ"Ev_n馛:O}+=4sLD/"-xbnY[/'_?TSUZ+"7x=izWw|o|SڠOr7;^AJi<vYZjiiD(rK{,_j'=ܳED AD6>^5??ω'9u'OԉS9s%:NK)F#lvZodÆLwfDP){ӛtIk'޻SD~YD~h5۷Gq)KOQ(~|E4\M7svNX?7S^U%"""geKKKpqN/ > F%5Ǫܴy;v`l۾0 W˲c";??{pȊ>_E7)fNiv8 f7@׻P;S@=R[ZmVr7]v)A2" y[*]SD"K" C?,"?nsԃpgߖð R3ёa"򖷾P駵|HD/Μ >ujHwR=S=>B=Wluz)`]\{3VYGe]{yV߱cmށmyQI{ˊg^{5kڨ D]"򞷿逗xJwݻEDBD^K9t0?w#Bz(Ji-ԁWY^YV /5]ͺuka_~Wi>ܻwE"i[XXoꗿ1x*ti{0@ębVoYm\hS .b]a8Dux;izR{zz9/?7@R`9д҈VA2_>V|7[r p  6MYfw0K<ʗ|)޽{o>3ZkI6t tu: (үHJԷ췞Zoq s[<+ug?[[x'<ܻw:Yk9{,qwPhP@k2(4Ze,S2bدd ,~S< rTJNaI02ZN.\~]z?eϻ%(1EcarrϾzZ#-εwݿi)h,ѡWJtI)S7埯.]Аkz[ef2_7Y]]&Y4[/Kd웁DaF!Zk\ٴyr~{˄zSVW{ Nd`)?&ֽ(ڍ-awuƠu@`Bb}Sf*p2׬3yR( 1jg9NeB늵mtL_k] > `S-3/:Xr}{ O4^Pz;G] "ӏ!ئP2F~xƸEk]XFieL1 1&Dk-AhBq3ڙqxP D)d.?dX>/ۯP*֛`F^l4/k q${<: ["1{8>Ug('S.:>:M;ڱ2@mѼ 5Z{0ӞʹR̠~;w2YOqҴ<%Mc4Z[dfѠ B h065Th}#RHBD ~Fj<я|>jڸ)c(^J6.*mtˆڃ@hUQeaڄ$mM& ct t%M&9 Qz2\Ryݔ^7\gIp8urjB2*"Ov7}T_}T(V|Ǜ|A pۿQ;S5S.(\}ԦInH'^`;bK9;nθ}YECj2 /GQJnQ(f;K1Di v<=0&7gy=\y j9r(?$NNF6 F`1_ jA4/e`*wbaV2 Vтʹ:ij6%I;tEt8Y"NX8ً 2+2M$X0MV(] (齪᪒ۿ (=1TG+):SWakYW3R?w8O>Ww~=eq]zV &/jb $BE+/8UvhaNQm|ʺSJwrY $Mt:xj‘%DWf'Iv什$i("(B 2^Pz)/JAPf#h5,gQFsu *@XjFT'ʛRL$ ι*Bc0Z;S̯Fy Xd@c΁7ha|Um0X%N馋tlp#>Nsg [/AE#̈́М9F69\E;MIϊW[N;4p˂@Y0Ix}%"jOOo9_$}<^ŃC)U7TS0hmhT?(/[veL1#4_dE*1]ptala3*ܻ `nI"]"]f`s 5:]CX*ݳ[d$"?%ō^{q_Y0 *S2]Is5&jW5͌`ڱL!kqx9M5q7IF~N: F1T&t6I%IUWB5ɔd89Ce)Ӻ2(>u}p#tڝA6_{xjh7honZ>'bqRtAQ7)d3&!Z(bGZ)P) -{:*Vcj&،6NW 1c9 {f,!Q놊|J|6vAV~sWkZL}zla [ *@PeÅ'biii<׽u**(.KE5?wtz}NۍˆFI"\ds9 ̦+6^#t?vyYrA:\sVwF-LxYjp|V\5YԐl4I|_I2y)_NcXqz(|To| ?e)U4+m4AdddVIkh a:7 AMV:L%wnjL)dE:!40ᶾvð`f(>vs_?y?uԯs9}vBK7tk(O}OM* ##4M6(7#iB8g%F9Ğ@}G+&Ա"2?¡EQkDN Z}lEhC79顸+#>FN]<*.7|s%.;CR@{zSPRퟫ͞9u#Lo+H}Zh=|oy/[kZiT) 5hZ4MFIբ52HkfEAF&B4=|e@h=6@,)٠ltHYS5BT?5*"w"(Q^sKڐ}T3bm2[zʏNa9NĊ$׾ݵRV8_o_ZÇ/pci]to#j54[MAnpLLcF8&U9b(8{U5.wS tDHrOZX$EC:¨ZGyč7>J6C!{zSA*7W?35C{]' ,}6!Ω֫QVhD4u|~F &9=uov5 gRaҌ4Q,Y ~[ M$$ZPx-M$Vl`$0 ]^կ7M5MHdD( 3ACѝ0Hl*Pb f0A3XK; HXjo%XKd76&}Pm]Gn)Vf{|%&M *c䭢kn`RH 0,}ϓv Iѝz3fKIF4 z@7^#YgviFRq/N;i&vnċ)A:$;,/<3NÙ3d7U5 Bj^oޞy3QdI9mVY cEZ2H֞$%Mؼ~ŦK G!9X$"" mwcJZ+(($Ia}M7x6\jƄ<`;>t6I#@+LykT,5Ael>Y\HlvHl`l8wkwfLq(X0YE'IФx-Q1;3 1xl_U\ܬ}HꗿN^ֹg!!Q.CMT L$uKI,XPbZ$"1FuI_ϥ=)RybtJB*߀R`"NU k4cty;Ic!* @Ì>3J+:\lT vmMJHs_= K#>y$z)_.Ɣ@z~Y&ׯ0E܅OrF!(^;V׳lCZeQ2bTX!!#vU&f$K_ IIb %_NJY'jAZZjD/6iuvGP7 pbDnʞ^@`%qiX"Nvn# G MS@ SuT=ͳn?C?s\Ŭ@o>U Q0&;d%Pu.$$EYV +QA9[tActeb;I0a&Mq^v3QMQ7=[{V1A#A.D\Ȁ( t7Mt .m\0O s_*nte<ȴئj!">XgR~nve P>4Bt8{_`|Yxg|%n*l^xy9$m\BrEq%mčŰdׂm+K\=0IGikikiu  30ki1gxV  7[YP B" u iLc뺬g t4&0m}nsts +={\*5ܷmjg7 /Цi(S?PI8֛f9J2P\K5g@ڛi!tx m% ͉BPs,ѐ"p`ʙ04^,J}wt!(~47 IDAT :g}ɶԔ'. h{ʍOMnUHf\Y)Xpoaaoax+.@њs]tIP 9@xf/xWRbIS i9mO(mWN TD })#"qH.ۮϡ,%3Rsnlť$iBv%R>Or"s?;֛&CTA)3*̓6 L};7Mڴ}G: vH\~\ٶ2ץNN19I'YNOIInĵv x84cqD'g)Mڄ~ %T|Q@,PU8Q)!\--ցyY9F~m? ۪| |8}wUzӋXʟ[}q2qrL$Pag׼ض 97HvH%N.rx#:K4sSt'z,S,%gs$iT]n17r~ |;ϰN0Jиnw{? 4-"|Nu^peng/oduε`y?2Pi1>_m#MS6%.].е j(:څ^tSo6~,p|ȧ#.MYJαa!>B|vXȨ`9A*?e TahadxVb-q# :VL=R|dxN:O.bEsp؎3$B{of]RmAtJb<@=|w m A0e߭t}#4Js/땊zYV[e%yWlo|K^2  &$Xp|nҙgpe"և{*K_e W4@iP,6HX:`נË.@м+H>C<3ڄj#`&Ut6q&NۤtQE B㼪"$tgh[f,21osߣSh@!)"q:Ob;Q <C.K6]kI8j?J-4>H/ЖVVȌy֛閙yyNw0`e@IZ>Dr(X^> p`A)"eu^|[ ^s' MEƆ ;33ӝyϣ}`]W)t{1 I]6vN%$$ҵΧ`1 ~,tsXIr0 z _hh SPw]I >M4ik7FF[|< &>9i"ɩ¥VaUDԒ !M]Dq޴ҋ-Wc)KY~M:K_pj5Wd]';H}Pfh454qtnzr|k=&*KOl`YDdsO>Sy;.78% $X u1h3z2+U=pWh4C5!әIp5zGo(>O);ߦeFi`Ќmr FAl`F+1IUW4Dds "u6v.W *o`8~6㊩f@ߕ4|",͆eA# Dhh~|^n9ewߓ[ROܧń1an%T7,ʥzP~I͵(06tVl. MJk~OޗZJ9JAdžD 4v?Н2HeTp vd3D4<ypi >j/ɷ| g7"R߉NK@5Pgz*Z|)2z%rÈd;`y|rt%D4a -x'&Al.Cz`tYmyҷ}; \E% (i1G- QjC)UG[:1l'@ۭըj+V_^12]Mڍ9w3}ʍYhж1#(Y %2O'~^c$fҸ7OBXS%=oGnGw"TP7 p#Dz4\&沞"Z(osf~ YKKFItfaer\| 6_~%3Gs=[F)t-%6':vX!I$^U@A:^ /`a; 4PvAm vk~ 2fAپ 0dLl™C0s'TweM)!LR:ɘÖMQvmv2A)Q9#ibdYC% JTXwHq{O)#n~qŲutO\Gh[f>TRf_X9v (n/})aӅrN=~(I+A$=O4ښU_9K-W/&T{ld:.{ыyiC//gnP!nbLB%. cuLFݴMWVh->j4u 2>M'| Sm< }C+xۘ;{bٺ_YuVܛO@+[/L.gN0~5®}Gマǎ79PuPdh$DgZHth'h6%Mf F48nT6QLn򭔛Uz_Lp鋾ɋ/xκmZ "s"lǠbqck۳_=t/`p6_[8v}t rs{&0m lbc0ɹ¶LQQi&hInf˒!y2h๗ض94ws\OEd{V|:E٩<훑?[}v-cͶ-p3 |OzB|, 7DQmg5UCZ =70*BvLU5MFG0sȀOzέ7"2k(W@ܭJǕߓ)=@釥F!_ $g򘑳e7D4PY/W/8pH"2W A _YA"^֢uF)/:pӋ D`a4}/fdbmۣsK3wrd،Un*EJ*UU"Z.Y*+be;t.c+>Us|MBajp\ygVq7]zU(D<)Lu9;ya *Y\dϼ /_yݸ^lOp0{,H*/$dQ$!AهinD_[ieW^ƫ."h К1OL=z0BMX VVW꯫eJHvE!NR'Rgwwnn©tST@g i=qv\lF|edZ.|,:ɻe?OOT Հ/k^fڛ c 6+sV ΀,XQESGYwbce8Nsn%I,̟fX}tt3K+S0v8ko`lںu VO}rq 3G|̱ ɕ_]y<`G*Dd_NO'}HnQM} (+vyHv3`@I#,pcB. k*˛G+E)L+ \v"trP*(‰њ::j'"}0yg)˒,t0[Jଭ*us|ի^uӟq"† +7hd,Jb}JheV;*SV)g$rQma3X|˝A|\~z@f։g~l~4&{ Qq&)S=әn oV%:16o߾Yf}2'6 FF,-vX7[7R J;j*"REY(&@gsXQ0wgq$K>E;Wı4E+]eQɀ~4v~#Ln(oɭOneݖKwۗ..z_ŪbuTz*)^6tǴ=2hLlMnD }JaY掟f O)_Sů&j8ឯCoˮg_ pJ7m#}4񞃌 Z'ixhsُSfp)N-vSso_xV+ΟzŦ,SOg&r@k33-Xc;7>q4zKډY8>;,X2/Jlԯz|<;q#u>E[.CoTA:6\-=~(ǯ+Ν;kϽbRbdVLS tʈk0iMWtѕo:gvu*,zS95pKF,puTe{X,XǞ0:ylzv\l6^|٪{GIx`KWe&u[۲n#"Ll,B$f :6'wbwj-I麼,ˈiaq ڳԥR #\\xÍfA!Yje#VC~ɛaQwٲg|+y֫M\*%Gk'/G@)H>kh/DG}4OCi[>͉GrsZ/ԱM`57bcطC|੫Kl+r_hרJ_Eeʖ+;L@-w~q]4des+hM#ୟfqlh ~M0_.׫@?]v٥|+BC2JpL.m(H`)$ykOr\q5/@VJnEqSsr~T:9P=Nr֔_f=i䎝l޽u;v"V[O-)WAkYu9s`d~-R5 K.}=σΥ ˾lC6Q3b'.<̹ʃҐ&ٿA\rZl^} 9faf@e ]SVYpFV.+*v6m0a8Jms`?؄^Yr97Ձ >[[:p}B"17x=7uI!{5t[Bf @PBpi_pK}0:CQ;9>Ζ+d˕WbsO0{8SgX<}4.'w̚W Y b 4F0 ݾɡ1rtE:K5ԪxartQַ۶oell63yj(a,g P~gTX-/@IG1v^׽vAEvlgݎsX:鳴t痐nIS)J0̀8q‘&#0y2'4辻x䞯8wfz=(叫LdLSHpϒ)"VRӓg"#7۹kϽd3kPe| wq,;q3S>U^J3g:Dٹ[.^Kx{W"pw8qN`zg]+kM/g˶g09y1wD&1G6/gq^(옃^o4_wE:ݥ>:|kz_pqU{n7|3/RE: 7J)1 f̱G8{1&6m« Rj0DDҘ(D}DҞؾxۉa\uT|^/;`ݍٯo> pbI"$yʢ~gFNfNf_5FҫٺZlpuq݄z9+ƖMϱ}wqwi`ƃ)#U0vv>AwD uT J>}LDJgoAwTi]cV+):MUol v,IE?xG6e5\pUD#,"~$泩!^6Mc4Axwqᅲ'.(T/kxur`M)c"Ic/+̣%5oW EATP* a=liAFd̠vl##H L: [AAm̷7DDn/mUE'##oݻףt1#"wu/vqlټFkoeld ^{׮A4Zf4GY~*dž(>5–XÎ$ۿt:,x]uۻ][چ])wpGnhAҿX֞q5} W/!`_-ӵ5Bb{>ڧO$^ n$}՜pǧVu!=YsBKd428npDK&FJqxh2,\q ]EejFZ`||7uaYyFxpaEV߿Rz!> wmcbp7A59J8klϧ N< V޶M3O׿P:ݫ &"WKKjOO7\i zcOh'S=q 9?=~/xaQGfXjԪ4S-1yC_DlRK_BIT]!]mSuϜVNqŶϛfM3@0 \76i53]{jٚѬ} }ٶaF74}YsEմ|oOTYyg\ K[Tm+S3E#a}%L:+J7NQ潠\" )؟"mfɘ^ I'j67?8SZ3#g woBy4'6788nqִ|[Dhr1;^>wNj$9Orؑg?f͆Gdx~6_LRq' ?B3̫pPͭ#I=ps]KQ<2mdgbݑlZV.57ϿNem7<<\@;Ug#/lj`52%~8|.vx<Η0*Lv%B X^5Wz?O펥:*?'gI=$nL=hjRU N3LDÏ9#OTf]gsꆟ#%*=Ϝw|AG/x%tϜϩg|Tz6ǓTS.Gh1,Xr$rcƇOr Dݹ\jȼ[ȋ"8jDm} 1˖SN]ՎN"" #Nn!qhW*DfߡwQ8?21:{܋:{m &(~ zӘz COջˏeDzhQfzfR;4;1qDa|#"rr.O[ -r!𰈨+I, ]*:~f^O]V6N C2*(⇫QRz3=vT'*ӻxr . ##Z1ġ+5'c~SPϳq25IDr[dַIXv͏EI2_gtu-aqb&:헨\j"8(rR)kW#r$3D7z6]R]hSUE:2Lfa`?B>,BMTUƀهdbl6oo} ߷MC ͮtoǩ8e *1?0Nj"@o|IX#Ӯ;޽kzøSNF{(q6+85|߉ UZ^_:NZ 8zW'$?w===$ϲ  CLLarr/GcW2n"tTa&TdLGݲiC!7 ;=;um$ vX*+|y)׿>yPZ2sl6 uY48`ۥHU*cXVJ\\b`~e/s}}+YNWW?Nzr2RɽfrrSpKy'] !H&dN/uɱ6,N0yx.W1y\R-wK)_Wq(|{v uNg/J՗p[X7]LP^4#i %wlHޗffL`еIő'aۗ2^DAWH1U*R߃VѴ0FU%N[K֬4'?b~o|xPD˯GS;v폅/ ^4h#TI#jH:}9ӳCfDд =\A8{)(T> *AgŻ~h蚁ZxLQĩc@|pCn rH8nË`1`Qȷ>zFHTHe0ݧ+v~L6FBw23j@<)TQ( 299@0DP._E<5*cW蚁' (vLƦv r|zK/*5͆B#ky98:% Tي=Nwꥻ{Y-!YTsKK]\S(q0H4AA?9  D˝.ЪZl|ebX0Z& 񹮃Z(twO$"[H.g9F'itDדQS9ZN.bY*Z\,QN>BWWn'u(N 3AHb&jA<ߡP8/ /HXi!LlцA}N7W! DZp<? LWWP琁NrF?0$aW*x+RbWU$o2QD.mo@__/'|le ÕCGQ>SQ~ aHXJȽ8qJgJX'[UU4M)~vwt-'+WSFKqt&ʑ @뮇%?iaeG?|TؽiK}:JAW'12HJ,|hlϢiZd&0Tt$:Iq3s_5{Cj,udn ]?̳7 n뮻_"rL[Ze=&KH 8؎a\D1s.Jw`[kfkkXncQ{X-Bal|n{$R]}DnBD60f0P԰r*t/_/"~v*@kV)"7J|Bfs9e[.?"z=`U躉蚉3Ӵ8O7roi+RDK!l1ޏŶ**U;T8Qf Tf eo&j tCkOYB:̶P75\~GߊeUjal|lxX.ZbYuakdLk> n37MY{z͗77whԛFreOHSn+Gl,Ʊ\\7ϥ AbϝH|{6I,0}c OOv:Ŷ]|eK; sVT^D>mppp>6 $|^&"WJ˲b! 3}/@?'LfVl]sôy='~w t|- `,ЇtHD}+|~- !t&KD"y&tDD~iE(_|q\F8>)j{7XQ.#ER Sΐ(ozt_.s$6ʅ^ӁE|9MZ3K`%TBK@IMq|&H9>ċ$J)!l n$u]d)IqϯsIVUUq5Oa(˲U'>r 0Zk躮[!0mݪfwF Nm۶PE|s97Q\c1XO~~=ҼE{H$nͲ,i_=< H.WljbzZZד4DDZhO ${IENDB`swami-2.2.0/src/swamigui/images/switch_neg_uni.png000066400000000000000000000005021361104770400222270ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx햱 0E#Vt00@1C*fV)AJ85t~$' QW!nDߺD_($4MI2}X?ogi,2ad$‹%z%zPvRm[e&MֽGUM4u='5<ϷIh1lkޚB)ڿ\pcG xk}h֯]D IENDB`swami-2.2.0/src/swamigui/images/switch_pos_bi.png000066400000000000000000000005711361104770400220640ustar00rootroot00000000000000PNG  IHDRybKGD X.IDATxՕ10EWʾA p3' (QJ$&KK5i|Dxs6uB;Xkf-I8,ˈ$I94M۶m/w1G1t]׹O/f'i\Ǧi}<=c J)t:asg&EQH){cD(k(qv_}KeY-\ٚY-TOkY[ksX?zNGz IENDB`swami-2.2.0/src/swamigui/images/switch_pos_uni.png000066400000000000000000000005051361104770400222620ustar00rootroot00000000000000PNG  IHDRybKGD XIDATx햽 0F LO2=;QQە;`$ VC" g3sDDDRJ)uѹvẮkȲ,(cy)wiEQ^yZkb$I0 gPD/йD/}ԓ}{qog:'ZU]uѶqiʲ,BZk4SC1q~QX>4\_g xI._Zr[IENDB`swami-2.2.0/src/swamigui/images/tree.png000066400000000000000000000012661361104770400201710ustar00rootroot00000000000000PNG  IHDRĴl;gAMA7tEXtSoftwareAdobe ImageReadyqe<HIDATxb?-@XM{69 ~ 4 R? ~}Z@, Cݍ~b`O ߡ ~= 4?ĥ'а_ 47? FbC(@,|%@ FfFпȂ@/,^@, r gϞ͐J0r@ 1}GTP Xgr)VC!|bt s- _8 lA gd-q0  6y\BL90ATM#>CA F. E@ dM`_Ѵ%!f` ,@l YJgj;@1Ҫ &;QL! <IENDB`swami-2.2.0/src/swamigui/images/tuning.png000066400000000000000000000024721361104770400205360ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATH}Lu߿λNYdCO nmYmsiQYmMj2-@8B9!q'&ݾ>پ a09}ҟүMh `/ C#Aq`> H>̃]/# <0R@ 'f^x <;/HxYcȯlR @V:4_bI@:c."MV4[SqL @7'nlm k"BAcYH:G~עعƮ S-DH "bK_4b?hrUD`C $!c6<\T7X.~"Yߜn0T7jWLOiho]y#"ghnikylfƩ=}dJv䈱$#3GYm w Sx͜.\#e}~ֶCE-DnX=r\"->:K  .ux<E`0(%qA cav mfkRAacʱ;벳ы\cI|v^ KTg&IKe lYeWX3*,?AW.I|Lkn܀'zXw7Jۅ;ݚ Uuk<^dnlM}4g521#1pD{13`|~WLni'<8ZK's2tj sY 6~SD(%"LaNDLmDf{dYw?3IENDB`swami-2.2.0/src/swamigui/images/velocity.png000066400000000000000000000006351361104770400210670ustar00rootroot00000000000000PNG  IHDRbKGD XRIDATx͔=0 T݂|jZXpC8M*(lkq,ӗ @YDTH c1{B}Z scL^ާӉh}3^(>C{ YŐ0 1"cLX|8=y1T%$@i>y !"^͚OY'C}WC THhQmHm6O4;DDBDoDA @IENDB`swami-2.2.0/src/swamigui/images/volenv.png000066400000000000000000000010371361104770400205370ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHՖAKTQo|XHAЕ W."DX`oa_ h'FH"UPBZh7/1ׅ>љIoс sXk-@c0Ơ>/zzuq҈l6K&T1d1\M Zpk-s{2@`kn}sӓNPM/bRʿOAB;29եW;()8NjKTڛ|E708es1Uz9an+R ؈s'b<\!IENDB`swami-2.2.0/src/swamigui/images/volenv_decay.png000066400000000000000000000010601361104770400217000ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATH=K1%A 8WD 7 ~QХSgADpqpsIG""[UU\Wm=8c ( Bu 1B`"u1όr;'t: ,&Ar(r&R"& mF `!&@X1քmt%qPW#Ibwk/sΞ5P 67Gu l@@8hM)>n~^;?x1NWSAb`h5,` !}"`(Xvߴr>ȏ3po!W_M9Gd)`5 CAb߀;{G/!:vS;9^ ~s0d2堔1V8jU%z.IENDB`swami-2.2.0/src/swamigui/images/volenv_delay.png000066400000000000000000000007451361104770400217220ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<wIDATHJAIvWc0QDH& ,,,b&X(HI+6>oac#V*&r09mnOcfna1ZCk  3s$ҰMj3za4nOSrZ#v~1Hy(@.jWwׇ.uL\pD-l[@~.&`m_\qɓyK'cwȵMStڹFѦSh5; h-ZDl"ه@YdӔ9RAw`nӶc߯eǞuDR'?IRWsf7M79R-$FvIENDB`swami-2.2.0/src/swamigui/images/volenv_hold.png000066400000000000000000000011441361104770400215440ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHkAs;h ؤR01! 69"^F!"PV!`Ձ iR(AlU$xfnwz~=̬85Zk ȞEDZ2ԾFH4"#ңXAah-7;ֺ֚֘@J9] J m xs|ZsnK JGt}y{!њJBr˫> nlf0݌ߛaI;-GjqsC&U0Aͻ1Qg^qr3IN) O^\{oH`Ga8- &>J}FA1時AaQ^ (u(7Q`'W"8AE' *Y$i 83Id\yeg0:~*)iiv$z՝TnRJR#n7/ LL9IENDB`swami-2.2.0/src/swamigui/images/volenv_release.png000066400000000000000000000010511361104770400222330ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHՖKA?;3z+ h ImS)tA)R !RJ,A  uH]`f&zwY ޗ;v8bxn^j@AV2lWιT ={VS*XkS6(1Xk/,#wsYX\CLWZ: r.Tw(bџ\9(bKkȥ⪻5IENDB`swami-2.2.0/src/swamigui/images/volenv_sustain.png000066400000000000000000000011301361104770400222770ustar00rootroot00000000000000PNG  IHDRw=sBIT|dtEXtSoftwarewww.inkscape.org<IDATHՖ?hAݛx"!FApK$Ui hccellH*"B!p X6Mψqvfg,6 77f=kZB)Es(=k<"J/ fvV!֥=Qy'TO)r&؎H)8/@)²ea23G<A+}B=l0txg A&"d~CۇH)]rkl5gG:|Ok؅ DxPG,> iS &3?3wxyg71f㳗R"8.  j 'qDGX(]f_^s޼D^{I^Cz/0rn.f@jKxA`^ 劻|]{;[qcLyv$S|_(Iv ț]2Y8,~9H);Fl[J0(ܠIENDB`swami-2.2.0/src/swamigui/libswamigui.def000066400000000000000000000012771361104770400202550ustar00rootroot00000000000000LIBRARY EXPORTS swamigui_control_glade_prop_connect swamigui_spectrum_canvas_get_type swamigui_control_adj_new swamigui_note_selector_new swamigui_spectrum_canvas_set_data swamigui_panel_get_type swamigui_register_panel_selector_type ; swamigui_root DATA ; getter function to access swamigui_root swamigui_get_swamigui_root swamigui_root_activate swamigui_root_load_prefs swamigui_root_new swamigui_knob_get_adjustment swamigui_knob_get_type swamigui_control_new_for_widget swamigui_control_new_for_widget_full swamigui_control_prop_connect_widget swamigui_util_glade_create swamigui_util_glade_lookup swamigui_register_pref_handler swamigui_disable_plugins swamigui_disable_python swamigui_initswami-2.2.0/src/swamigui/main.c000066400000000000000000000135351361104770400163510ustar00rootroot00000000000000/* * main.c - Main routine to kick things off * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PYTHON_SUPPORT #include #endif #include #include #include /* for getenv() */ #include #include /* for setlocale() */ #include #include "SwamiguiRoot.h" #include "swami_python.h" #include "i18n.h" #ifdef PYTHON_SUPPORT static void log_python_output_func (const char *output, gboolean is_stderr); #endif /* global boolean feature hacks */ extern gboolean swamigui_disable_python; extern gboolean swamigui_disable_plugins; int main (int argc, char *argv[]) { SwamiguiRoot *root = NULL; gboolean show_version = FALSE; gboolean default_prefs = FALSE; gchar **scripts = NULL; gchar **files = NULL; char *fname; GOptionContext *context; GError *err = NULL; gchar **sptr; GOptionEntry entries[] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, "Display Swami version number", NULL }, { "run-script", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &scripts, "Run one or more Python scripts on startup", NULL }, { "no-plugins", 'p', 0, G_OPTION_ARG_NONE, &swamigui_disable_plugins, "Don't load plugins", NULL}, { "default-prefs", 'd', 0, G_OPTION_ARG_NONE, &default_prefs, "Use default preferences", NULL }, { "disable-python", 'y', 0, G_OPTION_ARG_NONE, &swamigui_disable_python, "Disable runtime Python support", NULL }, { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &files, NULL, "[file1.sf2 file2.sf2 ...]" }, { NULL } }; /* FIXME - Kind of a hack, to use relative dirs for GdkPixbuf loaders on WIN32 */ #ifdef MINGW32 g_setenv ("GDK_PIXBUF_MODULE_FILE", "lib/gdk-pixbuf-2.0/2.10.0/loaders.cache", TRUE); g_setenv ("GDK_PIXBUF_MODULEDIR", "lib/gdk-pixbuf-2.0/2.10.0/loaders", TRUE); #endif #if defined(ENABLE_NLS) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); #endif context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, PACKAGE); g_option_context_add_group (context, gtk_get_option_group (TRUE)); if (!g_option_context_parse (context, &argc, &argv, &err)) { g_print ("option parsing failed: %s\n", err->message); return (1); } if (show_version) { g_print ("Swami %s\n", VERSION); return (0); } swamigui_init (&argc, &argv); root = swamigui_root_new (); /* ++ ref root */ /* Load preferences unless disabled */ if (!default_prefs) swamigui_root_load_prefs (root); /* Activate the Swami object */ swamigui_root_activate (root); /* loop over command line non-options, assuming they're files to open */ if (files) { GtkRecentManager *manager; char *file_uri; for (sptr = files; *sptr; sptr++) { /* first try and parse argument as a URI (in case we are called from recent files subsystem), then just use it as a plain file name */ if (!(fname = g_filename_from_uri (*sptr, NULL, NULL))) fname = g_strdup (*sptr); if (swami_root_patch_load (SWAMI_ROOT (root), fname, NULL, &err)) { /* Add file to recent chooser list */ if ((file_uri = g_filename_to_uri (fname, NULL, NULL))) { manager = gtk_recent_manager_get_default (); if (!gtk_recent_manager_add_item (manager, file_uri)) g_warning ("Error while adding file name to recent manager."); g_free (file_uri); } } else { g_critical (_("Failed to open file '%s' given as program argument: %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); } g_free (fname); } g_strfreev (files); } #ifdef PYTHON_SUPPORT if (scripts && !swamigui_disable_python) /* any scripts to run? */ { /* set to stdout Python output function (FIXME - Use logging?) */ swamigui_python_set_output_func (log_python_output_func); for (sptr = scripts; *sptr; sptr++) { char *script; GError *err = NULL; if (g_file_get_contents (*sptr, &script, NULL, &err)) { PyRun_SimpleString (script); g_free (script); } else { g_critical ("Failed to read Python script '%s': %s", *sptr, ipatch_gerror_message (err)); g_clear_error (&err); } } } #else if (scripts) g_critical ("No Python support, '-r' commands ignored"); #endif if (scripts) g_strfreev (scripts); gdk_threads_enter (); gtk_main (); /* kick it in the main GTK loop */ gdk_threads_leave (); /* we destroy it all so refdbg can tell us what objects leaked */ g_object_unref (root); /* -- unref root */ exit (0); } #ifdef PYTHON_SUPPORT /* output function which uses glib logging facility */ static void log_python_output_func (const char *output, gboolean is_stderr) { GString *gs; char *found; int pos = 0; /* must escape % chars */ gs = g_string_new (output); while ((found = strchr (&gs->str[pos], '%'))) { pos = found - gs->str; g_string_insert_c (gs, pos + 1, '%'); pos += 2; } if (is_stderr) fputs (gs->str, stderr); else puts (gs->str); g_string_free (gs, TRUE); } #endif swami-2.2.0/src/swamigui/marshals.list000066400000000000000000000024331361104770400177630ustar00rootroot00000000000000# see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # PARAM for GParamSpec or derived types (GParamSpec*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE VOID:UINT,DOUBLE,DOUBLE swami-2.2.0/src/swamigui/patch_funcs.c000066400000000000000000000743601361104770400177250ustar00rootroot00000000000000/* * patch_funcs.c - General instrument patch functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include "SwamiguiMultiSave.h" #include "SwamiguiPaste.h" #include "SwamiguiProp.h" #include "SwamiguiRoot.h" #include "SwamiguiTree.h" #include "util.h" #include "i18n.h" /* maximum notebook tab length (in characters). Only used for item properties dialog currently. */ #define MAX_NOTEBOOK_TAB_LENGTH 20 /* Columns used in sample export file format combo box list store */ enum { FILE_FORMAT_COL_TEXT, /* Descriptive format label displayed in combo */ FILE_FORMAT_COL_NAME, /* Name identifier of format */ FILE_FORMAT_COL_VALUE, /* Enum value of format */ FILE_FORMAT_COL_COUNT }; /* Local Prototypes */ static void swamigui_cb_load_files_response (GtkWidget *dialog, gint response, gpointer user_data); static gboolean swamigui_load_sample_helper (const char *fname, IpatchItem *parent_hint, IpatchPaste *paste, IpatchList *biglist, gboolean *paste_possible); static void swamigui_cb_export_samples_response (GtkWidget *dialog, gint response, gpointer user_data); /* global variables */ static char *path_patch_load = NULL; /* last loaded patch path */ //static char *path_patch_save = NULL; /* last saved patch path */ static char *path_sample_load = NULL; /* last sample load path */ static char *path_sample_export = NULL; /* last sample export path */ static char *last_sample_format = NULL; /* last sample export format */ /* clipboard for item selections */ static IpatchList *item_clipboard = NULL; /** * swamigui_load_files: * @parent_hint: Parent of new samples, a child thereof or SwamiRoot object * @load_samples: TRUE to load audio files only, FALSE for patch and audio files * * Open files routine. Displays a file selection dialog to open patch * and sample files with. */ void swamigui_load_files (GObject *parent_hint, gboolean load_samples) { GtkWidget *dialog; GtkWindow *main_window; char *path; /* ++ ref main window */ g_object_get (swamigui_root, "main-window", &main_window, NULL); dialog = gtk_file_chooser_dialog_new (_("Load files"), main_window, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, GTK_STOCK_ADD, GTK_RESPONSE_APPLY, NULL); g_object_unref (main_window); /* -- unref main window */ /* enable multiple selection mode */ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); /* set default response */ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); g_object_set_data (G_OBJECT (dialog), "_load_samples", GUINT_TO_POINTER (load_samples)); // Is file path set? If not use default from preferences if (load_samples) { if (!path_sample_load) { SwamiRoot *root = swami_get_root (G_OBJECT (parent_hint)); g_object_get (G_OBJECT (root), "sample-path", &path_sample_load, NULL); // ++ alloc sample path } path = path_sample_load; } else { if (!path_patch_load) { SwamiRoot *root = swami_get_root (G_OBJECT (parent_hint)); g_object_get (root, "patch-path", &path_patch_load, NULL); // ++ alloc patch path } path = path_patch_load; } if (path && strlen (path)) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), path); if (parent_hint) g_object_ref (parent_hint); /* ++ ref for dialog */ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (swamigui_cb_load_files_response), parent_hint); gtk_widget_show (dialog); } /* loads the list of user selected files */ static void swamigui_cb_load_files_response (GtkWidget *dialog, gint response, gpointer user_data) { GObject *parent_hint = G_OBJECT (user_data); SwamiRoot *root = swami_get_root (G_OBJECT (parent_hint)); GSList *file_names, *p; gboolean patch_loaded = FALSE, samples_loaded = FALSE; char *fname; GError *err = NULL; GtkRecentManager *manager; char *file_uri; GType type; gboolean load_samples, paste_possible; IpatchPaste *paste; IpatchList *biglist; GtkRecentData recent_data; char *groups[2] = { SWAMIGUI_ROOT_INSTRUMENT_FILES_GROUP, NULL }; IpatchItem *patch; if (response != GTK_RESPONSE_ACCEPT && response != GTK_RESPONSE_APPLY) { if (parent_hint) g_object_unref (parent_hint); gtk_widget_destroy (dialog); return; } load_samples = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (dialog), "_load_samples")); paste = ipatch_paste_new (); /* ++ ref paste object */ biglist = ipatch_list_new (); /* ++ ref list */ /* "Add" or "OK" button clicked */ file_names = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog)); /* loop over file names */ for (p = file_names; p; p = g_slist_next (p)) { fname = (char *)(p->data); // Identify file type type = ipatch_file_identify_name (fname, &err); if (type == G_TYPE_NONE) { g_critical (_("Failed to identify file '%s': %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); continue; } if (!load_samples && ipatch_find_converter (type, IPATCH_TYPE_BASE) != 0) { patch_loaded = TRUE; // Set patch path regardless if successful if (!swami_root_patch_load (root, fname, &patch, &err)) // ++ ref patch object { /* error occurred - log it */ g_critical (_("Failed to load file '%s': %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); continue; } if ((file_uri = g_filename_to_uri (fname, NULL, NULL))) // ++ alloc { manager = gtk_recent_manager_get_default (); recent_data.display_name = NULL; recent_data.description = NULL; // ++ alloc mime type recent_data.mime_type = ipatch_base_type_get_mime_type (G_OBJECT_TYPE (patch)); if (!recent_data.mime_type) recent_data.mime_type = g_strdup ("application/octet-stream"); recent_data.app_name = g_strdup (g_get_application_name ()); // ++ alloc recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL); // ++ alloc recent_data.groups = groups; recent_data.is_private = FALSE; // Add full info to set group of instrument files, to filter out sample files from the recent menu, etc. if (!gtk_recent_manager_add_full (manager, file_uri, &recent_data)) g_warning ("Error while adding file name to recent manager."); g_free (recent_data.mime_type); // -- free mime type g_free (recent_data.app_name); // -- free application name g_free (recent_data.app_exec); // -- free app exec command g_free (file_uri); // -- free file uri } g_object_unref (patch); // -- free } else if (g_type_is_a (type, IPATCH_TYPE_SND_FILE)) { paste_possible = FALSE; if (!IPATCH_IS_ITEM (parent_hint) || !swamigui_load_sample_helper (fname, IPATCH_ITEM (parent_hint), paste, biglist, &paste_possible)) { if (!paste_possible && !samples_loaded) { GtkWidget *msg = gtk_message_dialog_new (GTK_WINDOW (SWAMIGUI_ROOT (root)->main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Please select location in tree view to load samples into.")); gtk_dialog_run (GTK_DIALOG (msg)); gtk_widget_destroy (msg); } } samples_loaded = TRUE; // Set sample path regardless if successful } else { if (load_samples) g_critical (_("File '%s' is not a supported sample file"), fname); else g_critical (_("File '%s' is not a supported file type"), fname); } } if (samples_loaded) { /* finish the paste operation */ if (ipatch_paste_finish (paste, &err)) { /* select all samples which were added */ biglist->items = g_list_reverse (biglist->items); /* put in right order */ g_object_set (swamigui_root, "selection", biglist, NULL); } else { g_critical (_("Failed to finish load of samples (paste operation): %s"), ipatch_gerror_message (err)); g_clear_error (&err); } /* !! free old load path and take over allocation of new path */ g_free (path_sample_load); path_sample_load = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); } g_object_unref (biglist); /* -- unref list */ g_object_unref (paste); /* -- unref paste object */ if (patch_loaded) { /* !! free old load path and take over allocation of new path */ g_free (path_patch_load); path_patch_load = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); } /* free the file name list and strings */ for (p = file_names; p; p = g_slist_delete_link (p, p)) g_free (p->data); /* destroy dialog if "OK" button was clicked */ if (response == GTK_RESPONSE_ACCEPT) { if (parent_hint) g_object_unref (parent_hint); gtk_widget_destroy (dialog); } } static gboolean swamigui_load_sample_helper (const char *fname, IpatchItem *parent_hint, IpatchPaste *paste, IpatchList *biglist, gboolean *paste_possible) { IpatchFile *file; IpatchList *list; GList *lp; int size, max_size; GError *err = NULL; *paste_possible = TRUE; file = ipatch_file_identify_new (fname, &err); // ++ ref file if (!file) { g_critical (_("Failed to identify and open file '%s': %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); return (FALSE); } g_object_get (swami_root, "sample-max-size", &max_size, NULL); if (max_size != 0) // 0 means unlimited import size { size = ipatch_file_get_size (file, &err); if (size == -1) { g_warning (_("Failed to get sample file '%s' size: %s"), fname, ipatch_gerror_message (err)); g_clear_error (&err); } else if (size > max_size * 1024 * 1024) { g_critical (_("Sample file '%s' of %d bytes exceeds max sample setting of %dMB"), fname, size, max_size); return (FALSE); } } /* determine if IpatchSampleFile can be pasted to destination.. */ if (!ipatch_is_paste_possible (IPATCH_ITEM (parent_hint), IPATCH_ITEM (file))) { g_object_unref (file); // -- unref file *paste_possible = FALSE; return (FALSE); } /* paste sample file to destination */ if (!ipatch_paste_objects (paste, IPATCH_ITEM (parent_hint), IPATCH_ITEM (file), &err)) { /* object paste failed */ g_critical (_("Failed to load object of type '%s' to '%s': %s"), g_type_name (IPATCH_TYPE_SND_FILE), g_type_name (G_OBJECT_TYPE (parent_hint)), ipatch_gerror_message (err)); g_clear_error (&err); g_object_unref (file); // -- unref file return (FALSE); } g_object_unref (file); // -- unref file list = ipatch_paste_get_add_list (paste); /* ++ ref added object list */ if (list) { /* biglist takes over items of list */ for (lp = list->items; lp; lp = g_list_delete_link (lp, lp)) biglist->items = g_list_prepend (biglist->items, lp->data); list->items = NULL; g_object_unref (list); /* -- unref list */ } return (TRUE); } /** * swamigui_close_files: * @item_list: List of items to close (usually only #IpatchBase derived * objects make sense). * * User interface to close files. */ void swamigui_close_files (IpatchList *item_list) { GtkWidget *dialog; IpatchIter iter; IpatchItem *item; gboolean patch_found = FALSE; gboolean changed; GError *err = NULL; /* see if there are any patch items to close and if they have been changed */ ipatch_list_init_iter (item_list, &iter); item = ipatch_item_first (&iter); while (item) { if (IPATCH_IS_BASE (item)) /* only IpatchBase derived patches */ { patch_found = TRUE; g_object_get (item, "changed", &changed, NULL); if (changed) break; } item = ipatch_item_next (&iter); } if (!patch_found) return; /* no patches to close, return */ /* if no items changed, then go ahead and close files */ if (!item) { if (!ipatch_close_base_list (item_list, &err)) { g_warning (_("Failed to close file(s): %s"), ipatch_gerror_message (err)); g_clear_error (&err); } return; } /* item(s) have been changed, pop user interactive dialog */ dialog = swamigui_multi_save_new (_("Close files"), _("Save changed files before closing?"), SWAMIGUI_MULTI_SAVE_CLOSE_MODE); swamigui_multi_save_set_selection (SWAMIGUI_MULTI_SAVE (dialog), item_list); gtk_widget_show (dialog); } /** * swamigui_save_files: * @item_list: List of items to save. * @saveas: TRUE forces popup of dialog even if all files have previously * been saved (forces "Save As"). * * Save files user interface. If @saveas is %FALSE and all selected files * have already been saved before, then they are saved. If only one file has * not yet been saved then the normal save as file dialog is shown. If * multiple files have not been saved or @saveas is %TRUE then the multi-file * save dialog is used. */ void swamigui_save_files (IpatchList *item_list, gboolean saveas) { GtkWidget *dialog; IpatchItem *item, *base; char *filename; gboolean popup = FALSE, match = FALSE; gboolean saved, changed; GError *err = NULL; int savecount = 0, failcount = 0; GList *p; /* see if any items have been changed */ for (p = item_list->items; p; p = p->next) { item = (IpatchItem *)(p->data); base = ipatch_item_get_base (item); /* ++ ref base object */ if (base) /* only save IpatchBase items or children thereof */ { match = TRUE; /* found a patch base object */ g_object_get (base, "saved", &saved, "changed", &changed, NULL); if (!saved) popup = TRUE; /* never been saved, force dialog */ g_object_unref (base); /* -- unref base */ } } if (!match) return; /* return, if there are no items to save */ popup |= saveas; /* force dialog popup, "Save As"? */ /* no dialog required? (all items previously saved and !saveas) */ if (!popup) { for (p = item_list->items; p; p = p->next) { item = (IpatchItem *)(p->data); base = ipatch_item_get_base (item); /* ++ ref base object */ if (!base) continue; g_object_get (base, "file-name", &filename, NULL); /* ++ alloc filename */ if (!swami_root_patch_save (IPATCH_ITEM (base), filename, &err)) { g_critical (_("Failed to save file '%s': %s"), filename, ipatch_gerror_message (err)); g_clear_error (&err); failcount++; } else savecount++; g_free (filename); /* -- free filename */ g_object_unref (base); /* -- unref base object */ } if (!failcount) swamigui_statusbar_printf (swamigui_root->statusbar, _("Saved %d file(s)"), savecount); else swamigui_statusbar_printf (swamigui_root->statusbar, _("Saved %d file(s), %d FAILED"), savecount, failcount); return; } /* save-as was requested or a file has not yet been saved */ dialog = swamigui_multi_save_new (_("Save files"), _("Select files to save"), 0); swamigui_multi_save_set_selection (SWAMIGUI_MULTI_SAVE (dialog), item_list); gtk_widget_show (dialog); } /** * swamigui_delete_items: * @item_list: List of items to delete * * Delete patch items */ void swamigui_delete_items (IpatchList *item_list) { IpatchItem *parent = NULL; gboolean same_parent = TRUE; IpatchItem *item; IpatchIter iter; IpatchList *list; ipatch_list_init_iter (item_list, &iter); for (item = ipatch_item_first (&iter); item; item = ipatch_item_next (&iter)) { if (IPATCH_IS_ITEM (item) && !IPATCH_IS_BASE (item)) { if (same_parent) { if (parent) { if (parent != ipatch_item_peek_parent (item)) same_parent = FALSE; } else parent = ipatch_item_get_parent (item); /* ++ ref parent */ } ipatch_item_remove (item); } } /* If all items had same parent and it wasn't the patch object, make it the * new selection */ if (same_parent && parent && !IPATCH_IS_BASE (parent)) { list = ipatch_list_new (); /* ++ ref list */ list->items = g_list_append (list->items, g_object_ref (parent)); swamigui_tree_set_selection (SWAMIGUI_TREE (swamigui_root->tree), list); g_object_unref (list); /* -- unref list */ } if (parent) g_object_unref (parent); /* -- unref parent */ } /** * swamigui_wtbl_load_patch: * @item: Patch to load into wavetable. * * Load a patch item */ void swamigui_wtbl_load_patch (IpatchItem *item) { SwamiRoot *root; GObject *wavetbl; GError *err = NULL; /* IpatchBase derived objects only */ if (!IPATCH_IS_BASE (item)) return; root = swami_get_root (G_OBJECT (item)); if (!root) return; wavetbl = swami_object_get_by_type (G_OBJECT (root), "SwamiWavetbl"); if (wavetbl) { if (!swami_wavetbl_load_patch (SWAMI_WAVETBL (wavetbl), item, &err)) { g_critical (_("Patch load failed: %s"), ipatch_gerror_message (err)); g_clear_error (&err); } } } /** * swamigui_new_item: * @parent_hint: The parent of the new item or a hint item. An example of * a hint item is a SWAMIGUI_TREE_PRESET_MELODIC item which would allow the * real IpatchSF2Preset parent to be found, and would also indicate that * the new zone should be in the melodic branch. Can (and should be) NULL for * toplevel patch objects (IpatchSF2, etc). * @type: GType of an #IpatchItem derived type to create. * * Create a new patch item. */ void swamigui_new_item (IpatchItem *parent_hint, GType type) { IpatchItem *new_item, *real_parent = NULL; IpatchVirtualContainerConformFunc conform_func; IpatchList *list; g_return_if_fail (!parent_hint || IPATCH_IS_ITEM (parent_hint)); if (!parent_hint) parent_hint = IPATCH_ITEM (swami_root->patch_root); new_item = g_object_new (type, NULL); /* ++ ref new item */ /* parent hint is a virtual container? */ if (IPATCH_IS_VIRTUAL_CONTAINER (parent_hint)) { /* get virtual container conform function if any */ ipatch_type_get (G_OBJECT_TYPE (parent_hint), "virtual-child-conform-func", &conform_func, NULL); real_parent = ipatch_item_get_parent (parent_hint); /* ++ ref parent */ g_return_if_fail (real_parent != NULL); parent_hint = real_parent; /* force the new item to conform to virtual container parent_hint */ if (conform_func) conform_func ((GObject *)new_item); } /* add and make unique (if appropriate) */ ipatch_container_add_unique (IPATCH_CONTAINER (parent_hint), new_item); /* -- unref real parent object if parent_hint was a virtual container */ if (real_parent) g_object_unref (real_parent); /* update selection to be new item */ list = ipatch_list_new (); /* ++ ref new list */ list->items = g_list_append (list->items, new_item); /* !! list takes over ref */ g_object_set (swamigui_root, "selection", list, NULL); g_object_unref (list); /* -- unref new list */ } /** * swamigui_goto_link_item: * @item: Patch item * @tree: Swami tree object * * Goto an item's linked item in a #SwamiguiTree object. * Moves the view and selects the item in a #SwamiguiTree that is linked * by @item. */ void swamigui_goto_link_item (IpatchItem *item, SwamiguiTree *tree) { GObject *link = NULL; g_return_if_fail (IPATCH_IS_ITEM (item)); g_return_if_fail (SWAMIGUI_IS_TREE (tree)); g_object_get (item, "link-item", &link, NULL); /* ++ ref link */ if (link) { swamigui_tree_spotlight_item (tree, link); g_object_unref (link); /* -- unref from g_object_get */ } } /** * swamigui_export_samples: * @samples: List of objects (non #IpatchSample interface items are ignored) * * Export one or more samples (object with #IpatchSample interface) to a file * or directory. */ void swamigui_export_samples (IpatchList *samples) { gboolean found_sample = FALSE; GtkWindow *main_window; GtkWidget *dialog; GtkWidget *hbox; GtkWidget *label; GtkWidget *combo; GEnumClass *format_enum; GtkListStore *format_store; GtkCellRenderer *renderer; gboolean multi; GtkTreeIter iter; IpatchSample *sample = NULL; int sel, def_index = 0; guint i; GList *p; g_return_if_fail (IPATCH_IS_LIST (samples)); for (p = samples->items; p; p = p->next) { if (IPATCH_IS_SAMPLE (p->data)) { if (found_sample) break; sample = p->data; found_sample = TRUE; } } if (!found_sample) return; multi = (p != NULL); /* Multi sample selection? */ /* ++ ref main window */ g_object_get (swamigui_root, "main-window", &main_window, NULL); /* if only 1 item found, create a file save dialog, otherwise create a folder selection dialog */ dialog = gtk_file_chooser_dialog_new (_("Export samples"), main_window, multi ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); g_object_unref (main_window); /* -- unref main window */ /* set default response */ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); /* if sample load path isn't set, use default from config */ if (!path_sample_export) g_object_get (swamigui_root, "sample-path", &path_sample_export, NULL); if (path_sample_export && strlen (path_sample_export)) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), path_sample_export); if (!multi) /* Single sample export? */ { char *name, *filename; /* Set the file name to be that of the sample's title */ g_object_get (sample, "title", &name, NULL); /* ++ alloc name */ filename = g_strconcat (name, ".wav", NULL); /* ++ alloc filename */ g_free (name); /* -- free name */ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename); g_free (filename); /* -- free file name */ } } g_object_set_data_full (G_OBJECT (dialog), "samples", g_object_ref (samples), (GDestroyNotify)g_object_unref); g_object_set_data (G_OBJECT (dialog), "multi", GINT_TO_POINTER (multi)); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (swamigui_cb_export_samples_response), NULL); /* Create file format selector combo */ hbox = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox); gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new (_("File format")); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); /* ++ ref new store for file format combo box */ format_store = gtk_list_store_new (FILE_FORMAT_COL_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (format_store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", FILE_FORMAT_COL_TEXT, NULL); if (!last_sample_format) { g_object_get (swamigui_root, "sample-format", &last_sample_format, NULL); if (!last_sample_format) last_sample_format = g_strdup ("wav"); } sel = -1; /* selected index */ /* Populate file formats */ format_enum = g_type_class_ref (g_type_from_name ("IpatchSndFileFormat")); /* ++ ref class */ if (format_enum) { for (i = 0; i < format_enum->n_values; i++) { gtk_list_store_append (format_store, &iter); gtk_list_store_set (format_store, &iter, FILE_FORMAT_COL_TEXT, format_enum->values[i].value_nick, FILE_FORMAT_COL_NAME, format_enum->values[i].value_name, FILE_FORMAT_COL_VALUE, format_enum->values[i].value, -1); if (last_sample_format && strcmp (last_sample_format, format_enum->values[i].value_name) == 0) sel = i; if (format_enum->values[i].value == IPATCH_SND_FILE_DEFAULT_FORMAT) def_index = i; } g_type_class_unref (format_enum); /* -- unref enum class */ } gtk_combo_box_set_active (GTK_COMBO_BOX (combo), sel != -1 ? sel : def_index); g_object_unref (format_store); /* -- unref the list store */ gtk_widget_show (combo); gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (dialog), "combo", combo); gtk_widget_show (dialog); } static void swamigui_cb_export_samples_response (GtkWidget *dialog, gint response, gpointer user_data) { IpatchList *samples; IpatchSample *sample; GtkWidget *combo; gboolean multi; char *filepath; /* file name or directory name (single/multi) */ char *filename; GError *err = NULL; GtkTreeModel *format_model; GtkTreeIter iter; char *format_name; int format_value = IPATCH_SND_FILE_DEFAULT_FORMAT; GList *p; if (response != GTK_RESPONSE_ACCEPT) { gtk_widget_destroy (dialog); return; } samples = IPATCH_LIST (g_object_get_data (G_OBJECT (dialog), "samples")); multi = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), "multi")); combo = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "combo")); filepath = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) { format_model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); gtk_tree_model_get (format_model, &iter, FILE_FORMAT_COL_NAME, &format_name, /* ++ alloc */ FILE_FORMAT_COL_VALUE, &format_value, -1); } /* update last sample format */ if (last_sample_format) g_free (last_sample_format); last_sample_format = format_name; /* !! last_sample_format takes over alloc */ for (p = samples->items; p; p = p->next) { if (!IPATCH_IS_SAMPLE (p->data)) continue; sample = IPATCH_SAMPLE (p->data); /* compose file name */ if (multi) { char *name, *temp; g_object_get (sample, "title", &name, NULL); temp = g_strconcat (name, ".", format_name, NULL); g_free (name); filename = g_build_filename (filepath, temp, NULL); g_free (temp); } else filename = g_strdup (filepath); if (!ipatch_sample_save_to_file (sample, filename, format_value, -1, &err)) { g_critical (_("Failed to save sample '%s': %s"), filename, ipatch_gerror_message (err)); continue; } g_free (filename); } g_free (filepath); gtk_widget_destroy (dialog); } /** * swamigui_copy_items: * @items: List of items to copy or %NULL to clear * * Set the item clipboard to a given list of items. */ void swamigui_copy_items (IpatchList *items) { g_return_if_fail (!items || IPATCH_IS_LIST (items)); if (item_clipboard) { g_object_unref (item_clipboard); item_clipboard = NULL; } if (items) item_clipboard = ipatch_list_duplicate (items); } /* structure used to remember user decisions */ typedef struct { SwamiguiPasteDecision all; /* choice for all items or 0 for none */ GList *types; /* per type choices (RememberTypeChoice) */ } RememberChoices; /* per item type choice structure */ typedef struct { GType type; /* GType of this choice */ SwamiguiPasteDecision choice; /* choice */ } RememberTypeChoice; /** * swamigui_paste_items: * @dstitem: Destination item for paste * @items: List of source items to paste to destination item or %NULL * to use item clipboard * * Paste items user interface routine */ void swamigui_paste_items (IpatchItem *dstitem, GList *items) { IpatchPaste *paste; /* paste instance */ IpatchItem *src; GError *err = NULL; GList *p; /* use clipboard if no items given */ if (!items && item_clipboard) items = item_clipboard->items; paste = ipatch_paste_new (); /* ++ ref new paste instance */ for (p = items; p; p = p->next) /* loop on source items */ { src = IPATCH_ITEM (p->data); if (ipatch_is_paste_possible (dstitem, src)) /* paste possible? */ { /* add paste operation to instance */ if (!ipatch_paste_objects (paste, dstitem, src, &err)) { g_critical (_("Failed to paste item of type %s to %s: %s"), G_OBJECT_TYPE_NAME (src), G_OBJECT_TYPE_NAME (dstitem), ipatch_gerror_message (err)); g_clear_error (&err); } } } /* complete the paste operations */ if (!ipatch_paste_finish (paste, &err)) { g_critical (_("Failed to execute paste operation: %s"), ipatch_gerror_message (err)); g_clear_error (&err); } g_object_unref (paste); /* -- unref paste instance */ } /** * swamigui_get_clipboard_items: * * Get the current item clipboard list used for copy/paste operations. * * Returns: Current clipboard item list or NULL. Caller should not modify the list * but owns a reference and should unref the list object when finished using it. */ IpatchList * swamigui_get_clipboard_items (void) { if (!item_clipboard) return (NULL); return (g_object_ref (item_clipboard)); } swami-2.2.0/src/swamigui/patch_funcs.h000066400000000000000000000033041361104770400177200ustar00rootroot00000000000000/* * patch_funcs.h - General instrument patch functions header * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __PATCH_FUNCS_H__ #define __PATCH_FUNCS_H__ #include #include #include #include #include "SwamiguiRoot.h" #include "SwamiguiTree.h" void swamigui_load_files (GObject *parent_hint, gboolean load_samples); void swamigui_save_files (IpatchList *item_list, gboolean saveas); void swamigui_close_files (IpatchList *item_list); void swamigui_delete_items (IpatchList *item_list); void swamigui_wtbl_load_patch (IpatchItem *item); void swamigui_new_item (IpatchItem *parent_hint, GType type); void swamigui_goto_link_item (IpatchItem *item, SwamiguiTree *tree); void swamigui_export_samples (IpatchList *samples); void swamigui_copy_items (IpatchList *items); void swamigui_paste_items (IpatchItem *dstitem, GList *items); void swamigui_get_clipboard_items (void); #endif swami-2.2.0/src/swamigui/splash.c000066400000000000000000000072671361104770400167240ustar00rootroot00000000000000/* * splash.c - Swami startup splash image functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include "config.h" #include #include #include "splash.h" #include #include "util.h" static void cb_win_destroy (GtkWidget *win); static gboolean cb_button_press (GtkWidget *widg, GdkEventButton *ev); static GtkWidget *splash_win = NULL; static gboolean splash_timeout_active = FALSE; static guint splash_timeout_h; /** * swamigui_splash_display: * @timeout: Timeout in milliseconds or 0 to wait for button click * * Display the Swami splash startup image. If @timeout is %TRUE then the * splash image will be destroyed after a timeout period. */ void swamigui_splash_display (guint timeout) { GtkWidget *image; GdkPixbuf *pixbuf; gchar *resdir, *filename; if (splash_win) /* Only one instance at a time :) */ { swamigui_splash_kill (); /* Kill current instance of splash */ return; } /* ++ alloc resdir */ resdir = swamigui_util_get_resource_path (SWAMIGUI_RESOURCE_PATH_IMAGES); /* ++ alloc filename */ filename = g_build_filename (resdir, "splash.png", NULL); g_free (resdir); /* -- free resdir */ pixbuf = gdk_pixbuf_new_from_file (filename, NULL); /* ++ ref new pixbuf */ g_free (filename); /* -- free filename */ if (!pixbuf) return; /* fail silently if splash image load fails */ /* splash popup window */ splash_win = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_type_hint (GTK_WINDOW (splash_win), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN); gtk_window_set_resizable (GTK_WINDOW (splash_win), FALSE); gtk_signal_connect (GTK_OBJECT (splash_win), "destroy", GTK_SIGNAL_FUNC (cb_win_destroy), NULL); gtk_signal_connect (GTK_OBJECT (splash_win), "button-press-event", GTK_SIGNAL_FUNC (cb_button_press), NULL); gtk_widget_add_events (splash_win, GDK_BUTTON_PRESS_MASK); image = gtk_image_new_from_pixbuf (pixbuf); gtk_container_add (GTK_CONTAINER (splash_win), image); gtk_widget_show (image); gtk_window_set_position (GTK_WINDOW (splash_win), GTK_WIN_POS_CENTER); gtk_widget_show (splash_win); g_object_unref (pixbuf); /* -- unref pixbuf creator's ref */ if (timeout) { splash_timeout_active = TRUE; splash_timeout_h = g_timeout_add (timeout, (GSourceFunc)swamigui_splash_kill, NULL); } } /** * swamigui_splash_kill: * * Kills a currently displayed splash image. * * Returns: Always returns %FALSE, since it is used as a GSourceFunc for the * timeout. */ gboolean /* so it can be used as timeout GSourceFunc */ swamigui_splash_kill (void) { if (splash_win) { if (splash_timeout_active) gtk_timeout_remove (splash_timeout_h); gtk_widget_destroy (splash_win); } splash_timeout_active = FALSE; return (FALSE); } static void cb_win_destroy (GtkWidget *win) { splash_win = NULL; } static gboolean cb_button_press (GtkWidget *widg, GdkEventButton *ev) { swamigui_splash_kill (); return (FALSE); } swami-2.2.0/src/swamigui/splash.h000066400000000000000000000020211361104770400167100ustar00rootroot00000000000000/* * splash.h - Header file for splash intro routines * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SPLASH_H__ #define __SWAMIGUI_SPLASH_H__ #include void swamigui_splash_display (guint timeout); gboolean swamigui_splash_kill (void); #endif swami-2.2.0/src/swamigui/swami-2.ui000066400000000000000000007212331361104770400171000ustar00rootroot00000000000000 True False center-on-parent normal Swami Copyright (C) 1999-2014 Element Green <element@elementsofsound.org> http://www.swamiproject.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License only. 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 or point your web browser to http://www.gnu.org. Element Green <element@elementsofsound.org> Contributors: BALATON Zoltan <balaton@users.sourceforge.net> (Spectralis support, Mac OS X integration) True False True False False True end 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False <big><b>FluidSynth</b></big> True 90 False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 1 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Gain False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0 True False False False 1 True True 0 Volume True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 1 True False False False 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 1 0 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 4 8 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Damp True 3 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Width True 2 3 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Room True 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Level True True False True False 1 2 1 2 True False True False 2 3 1 2 True False True False 3 4 1 2 True False True False 1 2 Reverb True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 3 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 1 0 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 5 8 2 True False 12 4 5 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Type True 4 5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Depth True 3 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Freq True 2 3 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Count True 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Level True True False True False 1 2 True False True False 1 2 1 2 True False True False 2 3 1 2 True False True False 3 4 1 2 Chorus True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 4 True False 3 2 4 2 True False 0 Buffer Count 2 3 GTK_FILL True False 0 Device GTK_FILL True False 0 Buffer Size 1 2 GTK_FILL True False StoreBufferCount 1 2 2 3 True False StoreBufferSize 1 2 1 2 True False StoreAlsaDevice 1 2 True False 3 2 4 2 True False 0 Buffer Count 2 3 GTK_FILL True False 0 Device GTK_FILL True False 0 Buffer Size 1 2 GTK_FILL True False StoreBufferCount 1 2 2 3 True False StoreBufferSize 1 2 1 2 True False StoreDsoundDevice 1 2 True False 3 2 4 2 True False 0 Auto connect 2 3 GTK_FILL True True False 0 True 1 2 2 3 GTK_FILL True True False False True True adjustment3 1 2 1 2 True False 0 Input channels 1 2 GTK_FILL True True False False True True adjustment4 1 2 True False 0 Output channels GTK_FILL True False 3 2 4 2 True False 0 Buffer Count 2 3 GTK_FILL True False 0 Device GTK_FILL True False 0 Buffer Size 1 2 GTK_FILL True False StoreBufferCount 1 2 2 3 True False StoreBufferSize 1 2 1 2 True False StoreOssDevice 1 2 True False 2 2 4 2 True False 0 Device GTK_FILL True False 0 MIDI Channels 1 2 GTK_FILL True True StoreMidiChannels 0 1 2 1 2 True False StoreAlsaSeqDevice 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 4 2 2 4 2 True False True Maximum number of simultaneous voices 0 Polyphony GTK_FILL True True False False True True adjustment5 1 2 True False 0 Sample Rate 1 2 GTK_FILL True False SampleRateStore 1 2 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK General False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False True True 0 True False True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Audio Driver</b> True False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False True True 0 True False True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Midi Driver</b> True False False 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Drivers 1 False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 2 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.0099999997764825821 Confirm quit GTK_FILL True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Default new instrument file type 1 2 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Prompt for quit confirmation? 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Default file type 1 2 GTK_FILL True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.0099999997764825821 Instruments path word-char 2 3 GTK_FILL True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Default instrument file path select-folder 1 2 2 3 Show tips True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True 3 4 GTK_FILL Show splash image True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True 1 2 3 4 False True False 2 True True True True True True 0 True False 2 True New True True False True True True 0 Delete True True False True True True 1 False False 1 True True 0 True False True False 0 True False 4 0 True False 2 True False True True 0 True False Source False False 0 True False True False 0 True False 4 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 2 True True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 1 True False Destination True True 0 False False 1 True False 0 True False 4 0 True False True False 1 0 True False 4 True False 1 Amount center False False 0 True True False False True True adjustment15 1 True True True 1 False False 0 True False 2 True False True True 0 False False 1 True False Amount Source False False 2 False False 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK never True True False False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 8 8 8 8 True True False False True True 1 False 4 True False 5 5 2 2 True False 0 Name center GTK_FILL True False 0 Author center 1 2 GTK_FILL True False 0 Copyright center 2 3 GTK_FILL True True False False True True 1 5 True True False False True True 1 5 1 2 True True False False True True 1 5 2 3 True False 0 Date center 3 4 GTK_FILL True True False False True True 1 2 3 4 Current Date True True False True 2 3 3 4 True True False False True True 4 5 3 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 8 True False 0 Product center 3 4 3 4 GTK_FILL 24 Bit Samples True True False Activating this will cause the SoundFont to be saved with 24 bit samples. True 5 4 5 GTK_FILL False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.5 in True False 4 in True True word True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Comment True True 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 0 in True False 4 4 3 4 2 True False 0 center 2 3 True False 0 center 2 3 1 2 True False 0 center 2 3 2 3 True False 0 Software center 3 4 GTK_FILL True False 0 ROM version center 2 3 GTK_FILL True False 0 Sound engine center 1 2 GTK_FILL True False 0 SoundFont version center GTK_FILL True False 0 center 2 3 3 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Information</b> True False False 2 default hw:0 hw:1 default True False 2 4 2 True False Device GTK_FILL True False StoreAlsaRawDevice 1 2 default 1 2 3 4 64 128 256 512 1024 2048 4096 8192 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 256 /dev/dsp /dev/dsp1 /dev/midi /dev/midi1 True False 2 4 2 True False Device GTK_FILL True False StoreOssMidiDevice 1 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 8 Lower True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Select lower key bindings True True False False 0 Upper True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Select upper key bindings True True RadioLower False False 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Reset selected octave to default key bindings True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 gtk-revert-to-saved False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Reset to defaults False False 1 True True 2 False False 0 True True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 1 False 0 Press the key to bind or ESC to stop. False False 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True True True True Add a key binding True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 gtk-add False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Add False False 1 True True 0 True True True True Change key binding True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 gtk-edit False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Change False False 1 True True 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Delete selected key bindings True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 gtk-delete False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Delete False False 1 True True 2 False False 3 100 1 10 10 100 1 1 10 127 10 1 65535 1 1 10 127 1 10 127 1 10 False 4 4 3 4 2 True False 0 Root Note center GTK_FILL True False 0 Exclusive Class center 1 2 GTK_FILL True False 0 Fixed Note center 2 3 GTK_FILL True False 0 Fixed Velocity center 3 4 GTK_FILL True True False False True True adjustment11 1 True 1 2 GTK_FILL True True False False True True adjustment12 1 True 1 2 1 2 True True False False True True adjustment13 1 True 1 2 2 3 GTK_FILL True True False False True True adjustment14 1 True 1 2 3 4 GTK_FILL True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 2 3 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 2 3 1 2 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 2 3 2 3 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 2 3 3 4 False 4 5 2 4 2 True False 0 Root Note center 1 2 GTK_FILL True False 0 Exclusive Class center 2 3 True False 0 Fixed Note center 3 4 GTK_FILL True False 0 Fixed Velocity center 4 5 GTK_FILL True False 0 Name center GTK_FILL True True False False True True 1 2 True False 0 0 True False True True False False True True adjustment17 1 True False False 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 False False 1 1 2 1 2 GTK_FILL True False 0 0 True False True True False False True True adjustment18 1 True False False 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 False False 1 1 2 2 3 GTK_FILL True False 0 0 True False True True False False True True adjustment19 1 True False False 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 False False 1 1 2 3 4 GTK_FILL True False 0 0 True False True True False False True True adjustment20 1 True False False 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Toggle value assignment True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply 1 False False 1 1 2 4 5 GTK_FILL False 4 3 2 4 2 True False 0 0 True False 4 True True False False True True adjustment21 1 True True True 0 Percussion True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True True 1 1 2 1 2 True False 0 Name center GTK_FILL True False 0 Bank center 1 2 GTK_FILL True False 0 Program center 2 3 GTK_FILL True False 0 0 True True False False True True 1 2 GTK_FILL True False 0 0 True True False False True True adjustment22 1 True 1 2 2 3 False 4 4 2 4 2 True False 0 Name center GTK_FILL True False 0 Sample rate center 1 2 GTK_FILL True False 0 0 True True Name of sample False False True True 1 2 True False 0 Fine tuning center 3 4 GTK_FILL True False 0 0 True False 4 True True Fine tuning in cents False False True True adjustment16 1 True True True 0 True False cents center False False 1 1 2 3 4 True False 0 Root note center 2 3 GTK_FILL True False 0 0 True False False False True True 1 2 2 3 True False 0 0 True False SampleRateStore 1 2 1 2 False 4 2 2 4 2 True False 0 0 True False 1 2 1 2 True False 0 Name center GTK_FILL True False 0 Category center 1 2 GTK_FILL True False 0 0 True True False False True True 1 2 GTK_FILL True False True False True False True False 2 True False Name False False 0 False True False gtk-save True True False Save current script True False True False gtk-new True True False Create new script True False True False gtk-execute True True False Execute current script True False True False Shell mode True True False Execute a line when ENTER is pressed True True False False False 0 True True True False 0 out True True True False <b>Script Editor</b> True False True True False 0 out True True 2 in True True False char False True False <b>Python Console</b> True False True True True 1 -32768 32767 1 10 True False 6 2 2 2 True False 0 Max import audio size 1 2 GTK_FILL True False 0 Audio swap RAM size 2 3 GTK_FILL True False 0 Max unused audio swap 3 4 GTK_FILL True False 0 Samples path GTK_FILL True False 1 2 True True Maximum import audio sample size (in megabytes, 0 for unlimited) False False True True 1 2 1 2 True True Maximum RAM to use for temporary audio data (in megabytes) False False True True 1 2 2 3 True True Maximum unused space in audio swap file before compaction (in megabytes) False False True True 1 2 3 4 True False 0 Max unused audio cache 4 5 True False 0 Oldest unused cached audio 5 6 True True Maximum unused cached audio sample size (in megabytes) False False True True 1 2 4 5 True True Maximum age of unused cached audio samples (in seconds, 0 disables) False False True True 1 2 5 6 192000 96000 48000 44100 22050 11025 True False True False 2 2 True False 8 True False True True True False True False 2 True False gtk-missing-image True True 0 True False Notes True True 1 False False 0 True True True False True False 2 True False gtk-missing-image True True 0 True False Velocity True True 1 False False 1 False False 0 True False True False Octave True True 0 True False Lower keyboard MIDI octave False False False True True True True 1 True True False Join octaves True True True 2 True False Upper keyboard MIDI octave False False False True True True True 3 False False 2 True False True False Velocity True True 0 True False Lower keyboard MIDI velocity False False False True True True True 1 True True False True Synchronize upper and lower keyboard velocity True True True 2 True False Upper keyboard MIDI velocity False False False True True True True 3 False False 3 False False 0 True False True False 1 True False True True never never False False 0 True True never never True True 1 True False adjustment1 False False 2 True True 0 True False adjustment2 False False 1 True True 1 True False Swami Tips 340 180 True False True False 4 in True True False word True True 0 True False 2 4 Show tips on start up True True False True True False False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True gtk-media-previous True True False True False False 0 gtk-media-next True True False True False False 1 gtk-close True True False True False False 2 False False end 1 False False 4 1 -128 127 1 10 127 10 1 127 1 1 10 127 1 10 100 1 10 10 127 1 10 128 1 10 127 1 10 100 1 10 100 1 10 16 4096 16 1 10 100 1 1 10 1 100 1 1 10 1 2000 1 1 10 1 100 1 1 10 True False 4 4 True False True True 2 in True True False True False True False gtk-index True True 0 True False Results True True 1 False True False True False True False True False 5 2 4 2 True False 0 Group size 4 5 GTK_FILL True False 0 Group offset 3 4 GTK_FILL True True Minimum length of matching loops False False True True adjustment6 1 True 1 2 1 2 True False 0 Window size GTK_FILL True True Size of analysis window (number of samples around loop points to compare) False False True True adjustment7 1 True 1 2 True False 0 Min loop size 1 2 GTK_FILL True False 0 Max results 2 3 GTK_FILL True True Maximum loop suggestion results False False True True adjustment8 1 True 1 2 2 3 True True Minimum loop group position difference (best result per group) False False True True adjustment9 1 True 1 2 3 4 True True Minimum loop group size difference False False True True adjustment10 1 True 1 2 4 5 False False 0 1 True False True False gtk-properties True True 0 True False Config True True 1 1 False True True 0 True False False False 1 True False 2 gtk-revert-to-saved True True True False Revert to original loop values True False False 0 True True True False False False 1 True False 0.00 secs False False 2 False False 2 swami-2.2.0/src/swamigui/swami_python.c000066400000000000000000000100011361104770400201270ustar00rootroot00000000000000/* * python.c - Python interpreter functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include "swami_python.h" static void default_redir_func (const char *output, gboolean is_stderr); static SwamiguiPythonOutputFunc output_func = default_redir_func; static PyObject * redir_stdout (PyObject *self, PyObject *args) { char *s; if (!PyArg_ParseTuple(args, "s", &s)) return 0; if (output_func) output_func (s, FALSE); return Py_BuildValue(""); } static PyObject * redir_stderr (PyObject *self, PyObject *args) { char *s; if (!PyArg_ParseTuple(args, "s", &s)) return 0; if (output_func) output_func (s, TRUE); return Py_BuildValue(""); } // Define methods available to Python static PyMethodDef ioMethods[] = { {"redir_stdout", redir_stdout, METH_VARARGS, "redir_stdout"}, {"redir_stderr", redir_stderr, METH_VARARGS, "redir_stderr"}, {NULL, NULL, 0, NULL} }; static gboolean python_is_initialized = FALSE; /* * Initialize python for use with Swami. Sets up python output redirection. * Usually called once and only once by swamigui_init(). */ void _swamigui_python_init (int argc, char **argv) { PyObject* swami_main_module; char *new_argv[argc]; int i; char *init_code = "class Sout:\n" " def write(self, s):\n" " swami_redirect.redir_stdout(s)\n" "\n" "class Eout:\n" " def write(self, s):\n" " swami_redirect.redir_stderr(s)\n" "\n" "import sys\n" "import swami_redirect\n" "sys.stdout = Sout()\n" "sys.stderr = Eout()\n" "sys.stdin = None\n"; if (python_is_initialized) return; Py_Initialize (); for (i = 1; i < argc; i++) new_argv[i] = argv[i]; new_argv[0] = ""; /* set "script name" to empty string */ PySys_SetArgv (argc, new_argv); swami_main_module = Py_InitModule("swami_redirect", ioMethods); if (PyRun_SimpleString (init_code) != 0) g_critical ("Failed to redirect Python output"); if (PyRun_SimpleString ("import ipatch, swami, swamigui\n") != 0) g_critical ("Failed to 'import ipatch, swami, swamigui' modules"); python_is_initialized = TRUE; } /** * swamigui_python_set_output_func: * @func: Python output callback function or %NULL to use default (no * redirection) * * Set the Python output callback function which gets called for any * output to stdout or stderr from the Python interpreter. */ void swamigui_python_set_output_func (SwamiguiPythonOutputFunc func) { output_func = func ? func : default_redir_func; } /* a default redirection function, which doesn't redirect at all :) */ static void default_redir_func (const char *output, gboolean is_stderr) { fputs (output, is_stderr ? stderr : stdout); } /** * swamigui_python_set_root: * * Runs a bit of Python code to set the swamigui.root variable to the global * swamigui_root object. */ void swamigui_python_set_root (void) { if (PyRun_SimpleString ("swamigui.root = swamigui.swamigui_get_root()\n") != 0) g_critical ("Failed to assign swamigui.root object"); } /** * swamigui_python_is_initialized: * * Check if Python sub system is initialized and ready for action. * * Returns: TRUE if initialized, FALSE otherwise */ gboolean swamigui_python_is_initialized (void) { return python_is_initialized; } swami-2.2.0/src/swamigui/swami_python.h000066400000000000000000000023121361104770400201420ustar00rootroot00000000000000/* * swami_python.h - Header file for Python interpreter functions * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_SWAMI_PYTHON_H__ #define __SWAMIGUI_SWAMI_PYTHON_H__ #include typedef void (*SwamiguiPythonOutputFunc)(const char *output, gboolean is_stderr); void swamigui_python_set_output_func (SwamiguiPythonOutputFunc func); void swamigui_python_set_root (void); gboolean swamigui_python_is_initialized (void); #endif swami-2.2.0/src/swamigui/swamigui.h000066400000000000000000000047671361104770400172660ustar00rootroot00000000000000/* * swamigui.h - Main public header file for Swami GUI * * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMI_GUI_H__ #define __SWAMI_GUI_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif swami-2.2.0/src/swamigui/tools/000077500000000000000000000000001361104770400164125ustar00rootroot00000000000000swami-2.2.0/src/swamigui/tools/cdump.c000066400000000000000000000041001361104770400176610ustar00rootroot00000000000000/* Swami * * This file taken from: * AbiSource Build Tools * Copyright (C) 1998 AbiSource, 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. */ #include #include long _getFileLength(const char* pszFileName) { long iLengthOfFile; FILE* fp = fopen(pszFileName, "rb"); if (!fp) { return -1; } if (0 != fseek(fp, 0, SEEK_END)) { fclose(fp); return -1; } iLengthOfFile = ftell(fp); fclose(fp); return iLengthOfFile; } long _readEntireFile(const char* pszFileName, unsigned char* pBytes, unsigned long iLen) { FILE* fp = fopen(pszFileName, "rb"); if (!fp) { return -1; } if (iLen != fread(pBytes, 1, iLen, fp)) { fclose(fp); return -1; } fclose(fp); return iLen; } void _dumpHexCBytes(FILE* fp, const unsigned char* pBytes, long iLen) { long i; for (i=0; i * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include #include #include #include "util.h" #include "SwamiguiRoot.h" #include "i18n.h" #ifdef __APPLE__ #include #endif #if 0 /* time interval (in milliseconds) to check if log view should be popped */ #define LOG_POPUP_CHECK_INTERVAL 200 static gboolean log_viewactive = FALSE; /* log window active? */ static gint log_poplevel = LogBad; static gboolean log_popview = FALSE; /* log view popup scheduled? */ static GtkWidget *log_view_widg = NULL; /* currently active error view widg */ #endif /* unique dialog system data */ typedef struct { GtkWidget *dialog; gchar *strkey; int key2; } UniqueDialogKey; gboolean unique_dialog_inited = FALSE; GArray *unique_dialog_array; static void swamigui_util_cb_waitfor_widget_destroyed (GtkWidget *widg, gpointer data); static void ui_dep_xml_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error); static void ui_dep_xml_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error); static void ui_dep_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error); // static gboolean log_check_popup (gpointer data); // static void log_view_cb_destroy (void); /* initialize various utility services (unique dialog, log view timer, etc) */ void swamigui_util_init (void) { unique_dialog_array = g_array_new (FALSE, FALSE, sizeof (UniqueDialogKey)); unique_dialog_inited = TRUE; // g_timeout_add (LOG_POPUP_CHECK_INTERVAL, (GSourceFunc)log_check_popup, NULL); } guint swamigui_util_unit_rgba_color_get_type (void) { static guint unit_type = 0; if (!unit_type) { IpatchUnitInfo *info; info = ipatch_unit_info_new (); info->value_type = G_TYPE_UINT; info->name = "rgba-color"; info->label = _("Color"); info->descr = _("RGBA color value (in the form 0xRRGGBBAA)"); unit_type = ipatch_unit_register (info); ipatch_unit_info_free (info); } return (unit_type); } /** * swamigui_util_canvas_line_set: * @line: #GnomeCanvasLine object or an item with a "points" property * @x1: First X coordinate of line * @y1: First Y coordinate of line * @x2: Second X coordinate of line * @y2: Second Y coordinate of line * * A convenience function to set a #GnomeCanvasLine to a single line segment. * Can also be used on other #GnomeCanvasItem types which have a "points" * property. */ void swamigui_util_canvas_line_set (GnomeCanvasItem *item, double x1, double y1, double x2, double y2) { GnomeCanvasPoints *points; points = gnome_canvas_points_new (2); points->coords[0] = x1; points->coords[1] = y1; points->coords[2] = x2; points->coords[3] = y2; g_object_set (item, "points", points, NULL); gnome_canvas_points_free (points); } /* Unique dialog system is for allowing unique non-modal dialogs for resources identified by a string key and an optional additional integer key, attempting to open up a second dialog for the same resource will cause the first dialog to be brought to the front of view and no additional dialog will be created */ /* looks up a unique dialog widget by its keys, returns the widget or NULL */ GtkWidget * swamigui_util_lookup_unique_dialog (gchar *strkey, gint key2) { UniqueDialogKey *udkeyp; gint i; for (i = unique_dialog_array->len - 1; i >= 0; i--) { udkeyp = &g_array_index (unique_dialog_array, UniqueDialogKey, i); if ((udkeyp->strkey == strkey || strcmp (udkeyp->strkey, strkey) == 0) && udkeyp->key2 == key2) return (udkeyp->dialog); } return (NULL); } /* register a unique dialog, if a dialog already exists with the same keys, then activate the existing dialog and return FALSE, otherwise register the new dialog and return TRUE */ gboolean swamigui_util_register_unique_dialog (GtkWidget *dialog, gchar *strkey, gint key2) { UniqueDialogKey udkey; GtkWidget *widg; if ((widg = swamigui_util_lookup_unique_dialog (strkey, key2))) { gtk_widget_activate (widg); return (FALSE); } gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (swamigui_root->main_window)); udkey.dialog = dialog; udkey.strkey = strkey; udkey.key2 = key2; g_array_append_val (unique_dialog_array, udkey); gtk_signal_connect (GTK_OBJECT (dialog), "destroy", (GtkSignalFunc)swamigui_util_unregister_unique_dialog, NULL); return (TRUE); } void swamigui_util_unregister_unique_dialog (GtkWidget *dialog) { UniqueDialogKey *udkeyp; gint i; for (i = unique_dialog_array->len - 1; i >= 0; i--) { udkeyp = &g_array_index (unique_dialog_array, UniqueDialogKey, i); if (udkeyp->dialog == dialog) break; } if (i >= 0) g_array_remove_index (unique_dialog_array, i); } /* activate (or raise) a unique dialog into view */ gboolean swamigui_util_activate_unique_dialog (gchar *strkey, gint key2) { GtkWidget *dialog; if ((dialog = swamigui_util_lookup_unique_dialog (strkey, key2))) { gdk_window_raise (GTK_WIDGET (dialog)->window); return (TRUE); } return (FALSE); } /* run gtk_main loop until the GtkObject data property "action" is != NULL, mates nicely with swamigui_util_quick_popup, returns value of "action". Useful for complex routines that require a lot of user dialog interaction. Rather than having to save state info and exit and return to a routine, a call to this routine can be made which will wait for the user's choice and return the index of the button (or other user specified value), -1 if the widget was destroyed or -2 if gtk_main_quit was called */ gpointer swamigui_util_waitfor_widget_action (GtkWidget *widg) { GQuark quark; gpointer val = NULL; gboolean destroyed = FALSE; guint sigid; /* initialize the action variable to NULL */ quark = gtk_object_data_force_id ("action"); gtk_object_set_data_by_id (GTK_OBJECT (widg), quark, NULL); /* already passing one variable to destroy signal handler, so bind this one as a GtkObject data item, will notify us if widget was destroyed */ gtk_object_set_data (GTK_OBJECT (widg), "_destroyed", &destroyed); /* val is set to "action" by swamigui_util_cb_waitfor_widget_destroyed if the widget we are waiting for gets killed */ sigid = gtk_signal_connect (GTK_OBJECT (widg), "destroy", GTK_SIGNAL_FUNC (swamigui_util_cb_waitfor_widget_destroyed), &val); do { if (gtk_main_iteration ()) /* run the gtk main loop, wait if no events */ val = GINT_TO_POINTER (-2); /* gtk_main_quit was called, return -2 */ else if (val == NULL) /* check the "action" data property */ val = gtk_object_get_data_by_id (GTK_OBJECT (widg), quark); } while (val == NULL); /* loop until "action" is set */ if (!destroyed) gtk_signal_disconnect (GTK_OBJECT (widg), sigid); return (val); } static void swamigui_util_cb_waitfor_widget_destroyed (GtkWidget *widg, gpointer data) { gpointer *val = data; gpointer action; gboolean *destroyed; action = gtk_object_get_data (GTK_OBJECT (widg), "action"); destroyed = gtk_object_get_data (GTK_OBJECT (widg), "_destroyed"); *destroyed = TRUE; if (action) *val = action; else *val = GINT_TO_POINTER (-1); } /* a callback for widgets (buttons, etc) within a "parent" widget used by swamigui_util_waitfor_widget_action, sets "action" to the specified "value" */ void swamigui_util_widget_action (GtkWidget *cbwidg, gpointer value) { GtkWidget *parent; parent = gtk_object_get_data (GTK_OBJECT (cbwidg), "parent"); gtk_object_set_data (GTK_OBJECT (parent), "action", value); } /* Structure used by XML parser when finding UI dependencies */ typedef struct { GHashTable *dephash; char *object_id; // Current toplevel object ID (if tracking for deps) gboolean in_prop; // TRUE if in dependent property element (model or adjustment) GPtrArray *deparray; // Array of strings of the object ID dependencies } UiDepBag; /** * swamigui_util_glade_create: * @name: Name of the GtkBuilder widget to create * * Creates a GtkBuilder widget, by @name, from the main Swami UI XML file. * Prints a warning if the named widget does not exist. * * Returns: Newly created GtkBuilder widget (which the caller owns a ref to) * or %NULL on error */ GtkWidget * swamigui_util_glade_create (const char *name) { static GHashTable *dephash = NULL; // Object dependency hash char **object_ids, **dep_ids; GtkBuilder *builder; GError *err = NULL; GtkWidget *widg; gchar *resdir, *filename; int count; resdir = swamigui_util_get_resource_path (SWAMIGUI_RESOURCE_PATH_UIXML); /* ++ alloc */ filename = g_build_filename (resdir, "swami-2.ui", NULL); /* ++ alloc */ g_free (resdir); /* -- free resdir */ /* One time creation of hash of object dependencies - Wish GtkBuilder did this */ if (!dephash) { GMarkupParser parser = { 0 }; GMarkupParseContext *context; UiDepBag depbag = { 0 }; char *uixml = NULL; gsize len; dephash = g_hash_table_new (g_str_hash, g_str_equal); depbag.dephash = dephash; depbag.deparray = g_ptr_array_new (); parser.start_element = ui_dep_xml_start_element; parser.end_element = ui_dep_xml_end_element; parser.text = ui_dep_xml_text; context = g_markup_parse_context_new (&parser, 0, &depbag, NULL); if (g_file_get_contents (filename, &uixml, &len, &err)) { if (!g_markup_parse_context_parse (context, uixml, len, &err) || !g_markup_parse_context_end_parse (context, &err)) { g_critical ("Failed to parse UI XML file '%s': %s", filename, err->message); g_clear_error (&err); } g_free (uixml); /* -- free XML content */ } else { g_critical ("Failed to load UI XML file '%s': %s", filename, err->message); g_clear_error (&err); } g_ptr_array_free (depbag.deparray, TRUE); g_markup_parse_context_free (context); } dep_ids = g_hash_table_lookup (dephash, name); if (dep_ids) for (count = 0; dep_ids[count]; count++); else count = 0; object_ids = g_new (char *, count + 2); /* ++ object_ids */ if (dep_ids) memcpy (object_ids, dep_ids, count * sizeof (char *)); object_ids[count] = (char *)name; object_ids[count + 1] = NULL; builder = gtk_builder_new (); /* ++ ref new builder */ if (gtk_builder_add_objects_from_file (builder, filename, object_ids, &err) == 0) { g_critical ("Failed to load UI interface '%s': %s", name, err->message); g_clear_error (&err); g_free (filename); /* -- free allocated filename */ g_object_unref (builder); /* -- unref builder */ g_free (object_ids); /* -- free object IDs */ return (NULL); } g_free (filename); /* -- free allocated filename */ g_free (object_ids); /* -- free object IDs */ gtk_builder_connect_signals (builder, NULL); widg = (GtkWidget *)g_object_ref (gtk_builder_get_object (builder, name)); /* ++ ref for caller */ g_object_unref (builder); /* -- unref builder */ return (widg); } /* UI object dependencies XML start element callback */ static void ui_dep_xml_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { UiDepBag *bag = user_data; const GSList *stack; int i; if (!bag->object_id) { stack = g_markup_parse_context_get_element_stack (context); if (stack && stack->next && !stack->next->next && strcmp (element_name, "object") == 0) { for (i = 0; attribute_names[i]; i++) if (strcmp (attribute_names[i], "id") == 0) { bag->object_id = g_strdup (attribute_values[i]); break; } } } else if (strcmp (element_name, "property") == 0) { for (i = 0; attribute_names[i]; i++) if (strcmp (attribute_names[i], "name") == 0) { if (strcmp (attribute_values[i], "model") == 0 || strcmp (attribute_values[i], "adjustment") == 0) bag->in_prop = TRUE; break; } } } /* Callback for XML end of element for UI dependency search */ static void ui_dep_xml_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { UiDepBag *bag = user_data; const GSList *stack; char **depids; if (bag->in_prop) { bag->in_prop = FALSE; return; } else if (bag->object_id) { stack = g_markup_parse_context_get_element_stack (context); if (stack && stack->next && !stack->next->next) { if (bag->deparray->len > 0) { depids = g_new (char *, bag->deparray->len + 1); memcpy (depids, bag->deparray->pdata, bag->deparray->len * sizeof (gpointer)); depids[bag->deparray->len] = NULL; // NULL terminated /* Hash takes over allocation of object_id and dependency object IDs (forever) */ g_hash_table_insert (bag->dephash, bag->object_id, depids); g_ptr_array_set_size (bag->deparray, 0); } else g_free (bag->object_id); bag->object_id = NULL; } } } /* Called for text values in GtkBuilder UI XML dependency search. */ static void ui_dep_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { UiDepBag *bag = user_data; if (!bag->in_prop) return; g_ptr_array_add (bag->deparray, g_strdup (text)); } /** * swamigui_util_glade_lookup: * @widget: A GtkBuilder generated widget or a child there of. * @name: Name of widget in same GtkBuilder interface as @widget to get. * * Find a GtkBuilder generated widget, by @name, via any other widget in * the same widget interface. A warning is printed if the widget is not found * to help with debugging, when a widget is expected. Use * swamigui_util_glade_lookup_nowarn() to check if the named * widget does not exist, and not display a warning. * * Returns: The widget or %NULL if not found. */ GtkWidget * swamigui_util_glade_lookup (GtkWidget *widget, const char *name) { GtkWidget *w; w = swamigui_util_glade_lookup_nowarn (widget, name); if (!w) g_warning ("libglade widget not found: %s", name); return (w); } typedef struct { GtkWidget *found; const char *name; GtkWidget *skip; } FindBag; /* Recursive function to walk a GtkContainer looking for a GtkBuilder widget by name */ static void swamigui_util_glade_lookup_container_foreach (GtkWidget *widget, gpointer data) { FindBag *findbag = data; const char *name; if (findbag->found || widget == findbag->skip) return; name = gtk_buildable_get_name (GTK_BUILDABLE (widget)); if (name && strcmp (name, findbag->name) == 0) { findbag->found = widget; return; } if (GTK_IS_CONTAINER (widget)) gtk_container_foreach (GTK_CONTAINER (widget), swamigui_util_glade_lookup_container_foreach, findbag); } /** * swamigui_util_glade_lookup_nowarn: * @widget: A GtkBuilder generated widget or a child there of. * @name: Name of widget in same GtkBuilder interface as @widget to get. * * Like swamigui_util_glade_lookup() but does not print a warning if named * widget is not found. * * Returns: The widget or %NULL if not found. */ GtkWidget * swamigui_util_glade_lookup_nowarn (GtkWidget *widget, const char *name) { FindBag findbag = { 0 }; g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); findbag.name = name; for (; widget != NULL; widget = gtk_widget_get_parent (widget)) { gtk_container_foreach (GTK_CONTAINER (widget), swamigui_util_glade_lookup_container_foreach, &findbag); findbag.skip = widget; } return (findbag.found); } int swamigui_util_option_menu_index (GtkWidget *opmenu) { GtkWidget *menu, *actv; g_return_val_if_fail (GTK_IS_OPTION_MENU (opmenu), 0); menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (opmenu)); actv = gtk_menu_get_active (GTK_MENU (menu)); return (g_list_index (GTK_MENU_SHELL (menu)->children, actv)); } #if 0 /* a callback for a glib timeout that periodically checks if the log view should be popped by the GTK thread (see swamigui_util_init) */ static gboolean log_check_popup (gpointer data) { if (log_popview) { log_popview = FALSE; log_view (NULL); } return (TRUE); } /* log_view - Display log view window */ void log_view (gchar * title) { GtkWidget *dialog; GtkWidget *hbox; GtkWidget *msgarea; GtkAdjustment *adj; GtkWidget *vscrollbar; GtkWidget *btn; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; /* need to lock test and set of log_viewactive */ g_static_mutex_lock (&mutex); if (log_viewactive) { g_static_mutex_unlock (&mutex); return; } log_viewactive = TRUE; g_static_mutex_unlock (&mutex); if (title) dialog = gtk_dialog_new (title); else dialog = gtk_dialog_new (_("Swami log")); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); msgarea = gtk_text_new (NULL, NULL); gtk_widget_set_default_size (msgarea, 400, 100); gtk_text_set_editable (GTK_TEXT (msgarea), FALSE); gtk_text_set_word_wrap (GTK_TEXT (msgarea), FALSE); /* have to lock on log read, another thread might be writing to it */ g_mutex_lock (log_mutex); gtk_text_insert (GTK_TEXT (msgarea), NULL, NULL, NULL, log_buf->str, -1); g_mutex_unlock (log_mutex); gtk_box_pack_start (GTK_BOX (hbox), msgarea, TRUE, TRUE, 0); gtk_widget_show (msgarea); adj = GTK_TEXT (msgarea)->vadj; /* get the message area's vert adj */ vscrollbar = gtk_vscrollbar_new (adj); gtk_box_pack_start (GTK_BOX (hbox), vscrollbar, FALSE, FALSE, 0); gtk_widget_show (vscrollbar); btn = gtk_button_new_with_label (_("OK")); gtk_signal_connect_object (GTK_OBJECT (btn), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dialog)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), btn, FALSE, FALSE, 0); gtk_widget_show (btn); gtk_signal_connect_object (GTK_OBJECT (dialog), "destroy", GTK_SIGNAL_FUNC (log_view_cb_destroy), NULL); gtk_widget_show (dialog); log_view_widg = NULL; } /* reset dialog active variables */ static void log_view_cb_destroy (void) { log_viewactive = FALSE; log_view_widg = NULL; } #endif /** * swamigui_util_str_crlf2lf: * @str: String to convert * * Convert all dos newlines ("\r\n") to unix newlines "\n" in a string * * Returns: New string with converted newlines, should be freed when done with */ char * swamigui_util_str_crlf2lf (char *str) { char *newstr, *s; newstr = g_new (char, strlen (str) + 1); s = newstr; while (*str != '\0') { if (*str != '\r' || *(str + 1) != '\n') *(s++) = *str; str++; } *s = '\0'; return (newstr); } /** * swamigui_util_str_lf2crlf: * @str: String to convert * * Convert all unix newlines "\n" to dos newlines ("\r\n") in a string * * Returns: New string with converted newlines, should be freed when done with */ char * swamigui_util_str_lf2crlf (char *str) { GString *gs; char *s; gs = g_string_sized_new (sizeof (str)); while (*str != '\0') { if (*str != '\n') gs = g_string_append_c (gs, *str); else gs = g_string_append (gs, "\r\n"); str++; } s = gs->str; g_string_free (gs, FALSE); /* character segment is not free'd */ return (s); } /** * swamigui_util_substrcmp: * @sub: Partial string to search for in str * @str: String to search for sub string in * * Search for a sub string in a string * * Returns: %TRUE if "sub" is found in "str", %FALSE otherwise */ int swamigui_util_substrcmp (char *sub, char *str) { char *s, *s2; if (!*sub) return (TRUE); /* null string, matches */ while (*str) { if (tolower (*str) == tolower (*sub)) { s = sub + 1; s2 = str + 1; while (*s && *s2) { if (tolower (*s) != tolower (*s2)) break; s++; s2++; } if (!*s) return (TRUE); } str++; } return (FALSE); } gchar * swamigui_util_get_resource_path (SwamiResourcePath kind) { static gchar *res_root; /* Init res_root if not done yet */ if (!res_root) { #if defined (G_OS_WIN32) res_root = g_win32_get_package_installation_directory_of_module (NULL); #elif defined (__APPLE__) CFURLRef ResDirURL; gchar buf[PATH_MAX]; /* Use bundled resources if we are in an app bundle */ ResDirURL = CFBundleCopyResourcesDirectoryURL (CFBundleGetMainBundle()); if (CFURLGetFileSystemRepresentation (ResDirURL, TRUE, (UInt8 *)buf, PATH_MAX) && g_str_has_suffix (buf, ".app/Contents/Resources")) res_root = g_strdup (buf); CFRelease (ResDirURL); #endif #ifdef SOURCE_BUILD g_free (res_root); /* drop previous value if any and replace with src dir */ res_root = g_build_filename (SOURCE_DIR, "/src/swamigui", NULL); #endif } /* default/fallback if all else fails: use default paths */ if (!res_root) res_root = g_strdup (""); switch (kind) { case SWAMIGUI_RESOURCE_PATH_ROOT: return (*res_root ? g_strdup (res_root) : NULL); case SWAMIGUI_RESOURCE_PATH_UIXML: return (*res_root ? g_strdup (res_root) : g_strdup (UIXML_DIR)); case SWAMIGUI_RESOURCE_PATH_IMAGES: return (*res_root ? g_build_filename (res_root, "images", NULL) : g_strdup (IMAGES_DIR)); default: return NULL; } } swami-2.2.0/src/swamigui/util.h000066400000000000000000000051701361104770400164030ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #ifndef __SWAMIGUI_UTIL_H__ #define __SWAMIGUI_UTIL_H__ #include #include #include /* size in bytes of buffers used for converting audio formats */ #define SWAMIGUI_SAMPLE_TRANSFORM_SIZE (64 * 1024) typedef void (*UtilQuickFunc) (gpointer userdata, GtkWidget *popup); /** A guint RGBA color unit type for GParamSpec "unit-type" IpatchParamProp */ #define SWAMIGUI_UNIT_RGBA_COLOR swamigui_util_unit_rgba_color_get_type() typedef enum { SWAMIGUI_RESOURCE_PATH_ROOT, SWAMIGUI_RESOURCE_PATH_UIXML, SWAMIGUI_RESOURCE_PATH_IMAGES } SwamiResourcePath; void swamigui_util_init (void); guint swamigui_util_unit_rgba_color_get_type (void); void swamigui_util_canvas_line_set (GnomeCanvasItem *item, double x1, double y1, double x2, double y2); GtkWidget *swamigui_util_quick_popup (gchar * msg, gchar * btn1, ...); GtkWidget *swamigui_util_lookup_unique_dialog (gchar *strkey, gint key2); gboolean swamigui_util_register_unique_dialog (GtkWidget *dialog, gchar *strkey, gint key2); void swamigui_util_unregister_unique_dialog (GtkWidget *dialog); gboolean swamigui_util_activate_unique_dialog (gchar *strkey, gint key2); gpointer swamigui_util_waitfor_widget_action (GtkWidget *widg); void swamigui_util_widget_action (GtkWidget *cbwidg, gpointer value); GtkWidget *swamigui_util_glade_create (const char *name); GtkWidget *swamigui_util_glade_lookup (GtkWidget *widget, const char *name); GtkWidget *swamigui_util_glade_lookup_nowarn (GtkWidget *widget, const char *name); int swamigui_util_option_menu_index (GtkWidget *opmenu); // void log_view (gchar * title); char *swamigui_util_str_crlf2lf (char *str); char *swamigui_util_str_lf2crlf (char *str); int swamigui_util_substrcmp (char *sub, char *str); gchar *swamigui_util_get_resource_path (SwamiResourcePath kind); #endif swami-2.2.0/src/swamigui/widgets/000077500000000000000000000000001361104770400167205ustar00rootroot00000000000000swami-2.2.0/src/swamigui/widgets/combo-box.c000066400000000000000000000563531361104770400207650ustar00rootroot00000000000000/* * * Ripped and slightly modified for Swami from libgal-0.19.2 * * gtk-combo-box.c - a customizable combobox * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Miguel de Icaza (miguel@gnu.org) * Adrian E Feiguin (feiguin@ifir.edu.ar) * Paolo Molnaro (lupus@debian.org). * Jon K Hellan (hellan@acm.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "combo-box.h" static GtkHBoxClass *combo_box_parent_class; static int combo_toggle_pressed (GtkToggleButton * tbutton, ComboBox * combo_box); static void combo_popup_tear_off (ComboBox * combo, gboolean set_position); static void combo_set_tearoff_state (ComboBox * combo, gboolean torn_off); static void combo_popup_reparent (GtkWidget * popup, GtkWidget * new_parent, gboolean unrealize); static gboolean cb_popup_delete (GtkWidget * w, GdkEventAny * event, ComboBox * combo); static void combo_tearoff_bg_copy (ComboBox * combo); enum { POP_DOWN_WIDGET, POP_DOWN_DONE, PRE_POP_DOWN, POST_POP_HIDE, LAST_SIGNAL }; static gint combo_box_signals[LAST_SIGNAL] = { 0, }; struct _ComboBoxPrivate { GtkWidget *pop_down_widget; GtkWidget *display_widget; /* * Internal widgets used to implement the ComboBox */ GtkWidget *frame; GtkWidget *arrow_button; GtkWidget *toplevel; /* Popup's toplevel when not torn off */ GtkWidget *tearoff_window; /* Popup's toplevel when torn off */ guint torn_off; GtkWidget *tearable; /* The tearoff "button" */ GtkWidget *popup; /* Popup */ /* * Closure for invoking the callbacks above */ void *closure; }; static void combo_box_finalize (GObject *object) { ComboBox *combo_box = COMBO_BOX (object); gtk_object_destroy (GTK_OBJECT (combo_box->priv->toplevel)); g_object_unref (G_OBJECT (combo_box->priv->toplevel)); if (combo_box->priv->tearoff_window) { gtk_object_destroy (GTK_OBJECT (combo_box->priv->tearoff_window)); g_object_unref (G_OBJECT (combo_box->priv->tearoff_window)); /* ?? */ } g_free (combo_box->priv); G_OBJECT_CLASS (combo_box_parent_class)->finalize (object); } void my_marshal_POINTER__NONE (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { typedef gpointer (*GMarshalFunc_POINTER__NONE) (gpointer data1, gpointer data2); register GMarshalFunc_POINTER__NONE callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2, retval; g_return_if_fail (n_param_values == 1); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_POINTER__NONE) (marshal_data ? marshal_data : cc->callback); retval = callback (data1, data2); g_value_set_pointer (return_value, retval); } static void combo_box_class_init (ComboBoxClass *klass) { combo_box_parent_class = gtk_type_class (gtk_hbox_get_type ()); G_OBJECT_CLASS (klass)->finalize = combo_box_finalize; combo_box_signals[POP_DOWN_WIDGET] = g_signal_new ("pop_down_widget", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ComboBoxClass, pop_down_widget), NULL, NULL, my_marshal_POINTER__NONE, GTK_TYPE_POINTER, 0); combo_box_signals[POP_DOWN_DONE] = g_signal_new ("pop_down_done", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ComboBoxClass, pop_down_done), NULL, NULL, gtk_marshal_BOOL__POINTER, GTK_TYPE_BOOL, 1, GTK_TYPE_OBJECT); combo_box_signals[PRE_POP_DOWN] = g_signal_new ("pre_pop_down", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ComboBoxClass, pre_pop_down), NULL, NULL, gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); combo_box_signals[POST_POP_HIDE] = g_signal_new ("post_pop_hide", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ComboBoxClass, post_pop_hide), NULL, NULL, gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); } static void deactivate_arrow (ComboBox * combo_box) { GtkToggleButton *arrow; arrow = GTK_TOGGLE_BUTTON (combo_box->priv->arrow_button); gtk_signal_handler_block_by_func (GTK_OBJECT (arrow), GTK_SIGNAL_FUNC (combo_toggle_pressed), combo_box); gtk_toggle_button_set_active (arrow, FALSE); gtk_signal_handler_unblock_by_func (GTK_OBJECT (arrow), GTK_SIGNAL_FUNC (combo_toggle_pressed), combo_box); } /** * combo_box_popup_hide_unconditional * @combo_box: Combo box * * Hide popup, whether or not it is torn off. */ static void combo_box_popup_hide_unconditional (ComboBox * combo_box) { gboolean popup_info_destroyed = FALSE; g_return_if_fail (combo_box != NULL); g_return_if_fail (IS_COMBO_BOX (combo_box)); gtk_widget_hide (combo_box->priv->toplevel); gtk_widget_hide (combo_box->priv->popup); if (combo_box->priv->torn_off) { GTK_TEAROFF_MENU_ITEM (combo_box->priv->tearable)->torn_off = FALSE; combo_set_tearoff_state (combo_box, FALSE); } gtk_grab_remove (combo_box->priv->toplevel); gdk_pointer_ungrab (GDK_CURRENT_TIME); g_object_ref (G_OBJECT (combo_box->priv->pop_down_widget)); gtk_signal_emit (GTK_OBJECT (combo_box), combo_box_signals[POP_DOWN_DONE], combo_box->priv->pop_down_widget, &popup_info_destroyed); if (popup_info_destroyed) { gtk_container_remove (GTK_CONTAINER (combo_box->priv->frame), combo_box->priv->pop_down_widget); combo_box->priv->pop_down_widget = NULL; } g_object_unref (G_OBJECT (combo_box->priv->pop_down_widget)); deactivate_arrow (combo_box); gtk_signal_emit (GTK_OBJECT (combo_box), combo_box_signals[POST_POP_HIDE]); } /** * combo_box_popup_hide: * @combo_box: Combo box * * Hide popup, but not when it is torn off. * This is the external interface - for subclasses and apps which expect a * regular combo which doesn't do tearoffs. */ void combo_box_popup_hide (ComboBox * combo_box) { if (!combo_box->priv->torn_off) combo_box_popup_hide_unconditional (combo_box); else if (GTK_WIDGET_VISIBLE (combo_box->priv->toplevel)) { /* Both popup and tearoff window present. Get rid of just the popup shell. */ combo_popup_tear_off (combo_box, FALSE); deactivate_arrow (combo_box); } } /* * Find best location for displaying */ void combo_box_get_pos (ComboBox * combo_box, int *x, int *y) { GtkWidget *wcombo = GTK_WIDGET (combo_box); int ph, pw; gdk_window_get_origin (wcombo->window, x, y); *y += wcombo->allocation.height + wcombo->allocation.y; *x += wcombo->allocation.x; ph = combo_box->priv->popup->allocation.height; pw = combo_box->priv->popup->allocation.width; if ((*y + ph) > gdk_screen_height ()) *y = gdk_screen_height () - ph; if ((*x + pw) > gdk_screen_width ()) *x = gdk_screen_width () - pw; } static void combo_box_popup_display (ComboBox * combo_box) { int x, y; g_return_if_fail (combo_box != NULL); g_return_if_fail (IS_COMBO_BOX (combo_box)); /* * If we have no widget to display on the popdown, * create it */ if (!combo_box->priv->pop_down_widget) { GtkWidget *pw = NULL; gtk_signal_emit (GTK_OBJECT (combo_box), combo_box_signals[POP_DOWN_WIDGET], &pw); g_assert (pw != NULL); combo_box->priv->pop_down_widget = pw; gtk_container_add (GTK_CONTAINER (combo_box->priv->frame), pw); } gtk_signal_emit (GTK_OBJECT (combo_box), combo_box_signals[PRE_POP_DOWN]); if (combo_box->priv->torn_off) { /* To give the illusion that tearoff still displays the * popup, we copy the image in the popup window to the * background. Thus, it won't be blank after reparenting */ combo_tearoff_bg_copy (combo_box); /* We force an unrealize here so that we don't trigger * redrawing/ clearing code - we just want to reveal our * backing pixmap. */ combo_popup_reparent (combo_box->priv->popup, combo_box->priv->toplevel, TRUE); } combo_box_get_pos (combo_box, &x, &y); gtk_widget_set_uposition (combo_box->priv->toplevel, x, y); gtk_widget_realize (combo_box->priv->popup); gtk_widget_show (combo_box->priv->popup); gtk_widget_realize (combo_box->priv->toplevel); gtk_widget_show (combo_box->priv->toplevel); gtk_grab_add (combo_box->priv->toplevel); gdk_pointer_grab (combo_box->priv->toplevel->window, TRUE, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, NULL, NULL, GDK_CURRENT_TIME); } static int combo_toggle_pressed (GtkToggleButton * tbutton, ComboBox * combo_box) { if (tbutton->active) combo_box_popup_display (combo_box); else combo_box_popup_hide_unconditional (combo_box); return TRUE; } static gint combo_box_button_press (GtkWidget * widget, GdkEventButton * event, ComboBox * combo_box) { GtkWidget *child; child = gtk_get_event_widget ((GdkEvent *) event); if (child != widget) { while (child) { if (child == widget) return FALSE; child = child->parent; } } combo_box_popup_hide (combo_box); return TRUE; } /** * combo_box_key_press * @widget: Widget * @event: Event * @combo_box: Combo box * * Key press handler which dismisses popup on escape. * Popup is dismissed whether or not popup is torn off. */ static gint combo_box_key_press (GtkWidget * widget, GdkEventKey * event, ComboBox * combo_box) { if (event->keyval == GDK_Escape) { combo_box_popup_hide_unconditional (combo_box); return TRUE; } else return FALSE; } static void cb_state_change (GtkWidget * widget, GtkStateType old_state, ComboBox * combo_box) { GtkStateType const new_state = GTK_WIDGET_STATE (widget); gtk_widget_set_state (combo_box->priv->display_widget, new_state); } static void combo_box_init (ComboBox * combo_box) { GtkWidget *arrow; GdkCursor *cursor; combo_box->priv = g_new0 (ComboBoxPrivate, 1); /* * Create the arrow */ combo_box->priv->arrow_button = gtk_toggle_button_new (); GTK_WIDGET_UNSET_FLAGS (combo_box->priv->arrow_button, GTK_CAN_FOCUS); arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (combo_box->priv->arrow_button), arrow); gtk_box_pack_end (GTK_BOX (combo_box), combo_box->priv->arrow_button, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (combo_box->priv->arrow_button), "toggled", GTK_SIGNAL_FUNC (combo_toggle_pressed), combo_box); gtk_widget_show_all (combo_box->priv->arrow_button); /* * prelight the display widget when mousing over the arrow. */ gtk_signal_connect (GTK_OBJECT (combo_box->priv->arrow_button), "state-changed", GTK_SIGNAL_FUNC (cb_state_change), combo_box); /* * The pop-down container */ combo_box->priv->toplevel = gtk_window_new (GTK_WINDOW_POPUP); gtk_widget_ref (combo_box->priv->toplevel); gtk_object_sink (GTK_OBJECT (combo_box->priv->toplevel)); gtk_window_set_policy (GTK_WINDOW (combo_box->priv->toplevel), FALSE, TRUE, FALSE); combo_box->priv->popup = gtk_event_box_new (); gtk_container_add (GTK_CONTAINER (combo_box->priv->toplevel), combo_box->priv->popup); gtk_widget_show (combo_box->priv->popup); gtk_widget_realize (combo_box->priv->popup); cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW); gdk_window_set_cursor (combo_box->priv->popup->window, cursor); gdk_cursor_destroy (cursor); combo_box->priv->torn_off = FALSE; combo_box->priv->tearoff_window = NULL; combo_box->priv->frame = gtk_frame_new (NULL); gtk_container_add (GTK_CONTAINER (combo_box->priv->popup), combo_box->priv->frame); gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->frame), GTK_SHADOW_OUT); gtk_signal_connect (GTK_OBJECT (combo_box->priv->toplevel), "button_press_event", GTK_SIGNAL_FUNC (combo_box_button_press), combo_box); gtk_signal_connect (GTK_OBJECT (combo_box->priv->toplevel), "key_press_event", GTK_SIGNAL_FUNC (combo_box_key_press), combo_box); } GType combo_box_get_type (void) { static GType type = 0; if (!type) { GTypeInfo info = { sizeof (ComboBoxClass), NULL, NULL, (GClassInitFunc) combo_box_class_init, NULL, NULL, sizeof (ComboBox), 0, (GInstanceInitFunc) combo_box_init, }; type = g_type_register_static (gtk_hbox_get_type (), "MyComboBox", &info, 0); } return type; } /** * combo_box_set_display: * @combo_box: the Combo Box to modify * @display_widget: The widget to be displayed * Sets the displayed widget for the @combo_box to be @display_widget */ void combo_box_set_display (ComboBox * combo_box, GtkWidget * display_widget) { g_return_if_fail (combo_box != NULL); g_return_if_fail (IS_COMBO_BOX (combo_box)); g_return_if_fail (display_widget != NULL); g_return_if_fail (GTK_IS_WIDGET (display_widget)); if (combo_box->priv->display_widget && combo_box->priv->display_widget != display_widget) gtk_container_remove (GTK_CONTAINER (combo_box), combo_box->priv->display_widget); combo_box->priv->display_widget = display_widget; gtk_box_pack_start (GTK_BOX (combo_box), display_widget, TRUE, TRUE, 0); } static gboolean cb_tearable_enter_leave (GtkWidget * w, GdkEventCrossing * event, gpointer data) { gboolean const flag = GPOINTER_TO_INT (data); gtk_widget_set_state (w, flag ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL); return FALSE; } /** * combo_popup_tear_off * @combo: Combo box * @set_position: Set to position of popup shell if true * * Tear off the popup * * FIXME: * Gtk popup menus are toplevel windows, not dialogs. I think this is wrong, * and make the popups dialogs. But may be there should be a way to make * them toplevel. We can do this after creating: * GTK_WINDOW (tearoff)->type = GTK_WINDOW_TOPLEVEL; */ static void combo_popup_tear_off (ComboBox * combo, gboolean set_position) { int x, y; if (!combo->priv->tearoff_window) { GtkWidget *tearoff; gchar *title; tearoff = gtk_window_new (GTK_WINDOW_POPUP); gtk_widget_ref (tearoff); gtk_object_sink (GTK_OBJECT (tearoff)); combo->priv->tearoff_window = tearoff; gtk_widget_set_app_paintable (tearoff, TRUE); gtk_signal_connect (GTK_OBJECT (tearoff), "key_press_event", GTK_SIGNAL_FUNC (combo_box_key_press), GTK_OBJECT (combo)); gtk_widget_realize (tearoff); title = gtk_object_get_data (GTK_OBJECT (combo), "combo-title"); if (title) gdk_window_set_title (tearoff->window, title); gtk_window_set_policy (GTK_WINDOW (tearoff), FALSE, TRUE, FALSE); gtk_window_set_transient_for (GTK_WINDOW (tearoff), GTK_WINDOW (gtk_widget_get_toplevel GTK_WIDGET (combo))); } if (GTK_WIDGET_VISIBLE (combo->priv->popup)) { gtk_widget_hide (combo->priv->toplevel); gtk_grab_remove (combo->priv->toplevel); gdk_pointer_ungrab (GDK_CURRENT_TIME); } combo_popup_reparent (combo->priv->popup, combo->priv->tearoff_window, FALSE); /* It may have got confused about size */ gtk_widget_queue_resize (GTK_WIDGET (combo->priv->popup)); if (set_position) { combo_box_get_pos (combo, &x, &y); gtk_widget_set_uposition (combo->priv->tearoff_window, x, y); } gtk_widget_show (GTK_WIDGET (combo->priv->popup)); gtk_widget_show (combo->priv->tearoff_window); } /** * combo_set_tearoff_state * @combo_box: Combo box * @torn_off: TRUE: Tear off. FALSE: Pop down and reattach * * Set the tearoff state of the popup * * Compare with gtk_menu_set_tearoff_state in gtk/gtkmenu.c */ static void combo_set_tearoff_state (ComboBox * combo, gboolean torn_off) { g_return_if_fail (combo != NULL); g_return_if_fail (IS_COMBO_BOX (combo)); if (combo->priv->torn_off != torn_off) { combo->priv->torn_off = torn_off; if (combo->priv->torn_off) { combo_popup_tear_off (combo, TRUE); deactivate_arrow (combo); } else { gtk_widget_hide (combo->priv->tearoff_window); combo_popup_reparent (combo->priv->popup, combo->priv->toplevel, FALSE); } } } /** * combo_tearoff_bg_copy * @combo_box: Combo box * * Copy popup window image to the tearoff window. */ static void combo_tearoff_bg_copy (ComboBox * combo) { GdkPixmap *pixmap; GdkGC *gc; GdkGCValues gc_values; GtkWidget *widget = combo->priv->popup; if (combo->priv->torn_off) { gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS; gc = gdk_gc_new_with_values (widget->window, &gc_values, GDK_GC_SUBWINDOW); pixmap = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, -1); gdk_draw_pixmap (pixmap, gc, widget->window, 0, 0, 0, 0, -1, -1); gdk_gc_unref (gc); gtk_widget_set_usize (combo->priv->tearoff_window, widget->allocation.width, widget->allocation.height); gdk_window_set_back_pixmap (combo->priv->tearoff_window->window, pixmap, FALSE); gdk_pixmap_unref (pixmap); } } /** * combo_popup_reparent * @popup: Popup * @new_parent: New parent * @unrealize: Unrealize popup if TRUE. * * Reparent the popup, taking care of the refcounting * * Compare with gtk_menu_reparent in gtk/gtkmenu.c */ static void combo_popup_reparent (GtkWidget * popup, GtkWidget * new_parent, gboolean unrealize) { GtkObject *object = GTK_OBJECT (popup); gboolean was_floating = GTK_OBJECT_FLOATING (object); g_object_ref (G_OBJECT (object)); gtk_object_sink (object); if (unrealize) { g_object_ref (G_OBJECT (object)); gtk_container_remove (GTK_CONTAINER (popup->parent), popup); gtk_container_add (GTK_CONTAINER (new_parent), popup); g_object_unref (G_OBJECT (object)); } else gtk_widget_reparent (GTK_WIDGET (popup), new_parent); gtk_widget_set_usize (new_parent, -1, -1); if (was_floating) GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING); else g_object_unref (G_OBJECT (object)); } /** * cb_tearable_button_release * @w: Widget * @event: Event * @combo: Combo box * * Toggle tearoff state. */ static gboolean cb_tearable_button_release (GtkWidget * w, GdkEventButton * event, ComboBox * combo) { GtkTearoffMenuItem *tearable; g_return_val_if_fail (w != NULL, FALSE); g_return_val_if_fail (GTK_IS_TEAROFF_MENU_ITEM (w), FALSE); tearable = GTK_TEAROFF_MENU_ITEM (w); tearable->torn_off = !tearable->torn_off; if (!combo->priv->torn_off) { gboolean need_connect; need_connect = (!combo->priv->tearoff_window); combo_set_tearoff_state (combo, TRUE); if (need_connect) gtk_signal_connect (GTK_OBJECT (combo->priv->tearoff_window), "delete_event", GTK_SIGNAL_FUNC (cb_popup_delete), combo); } else combo_box_popup_hide_unconditional (combo); return TRUE; } static gboolean cb_popup_delete (GtkWidget * w, GdkEventAny * event, ComboBox * combo) { combo_box_popup_hide_unconditional (combo); return TRUE; } void combo_box_construct (ComboBox * combo_box, GtkWidget * display_widget, GtkWidget * pop_down_widget) { GtkWidget *tearable; GtkWidget *vbox; g_return_if_fail (combo_box != NULL); g_return_if_fail (IS_COMBO_BOX (combo_box)); g_return_if_fail (display_widget != NULL); g_return_if_fail (GTK_IS_WIDGET (display_widget)); GTK_BOX (combo_box)->spacing = 0; GTK_BOX (combo_box)->homogeneous = FALSE; combo_box->priv->pop_down_widget = pop_down_widget; combo_box->priv->display_widget = NULL; vbox = gtk_vbox_new (FALSE, 5); tearable = gtk_tearoff_menu_item_new (); gtk_signal_connect (GTK_OBJECT (tearable), "enter-notify-event", GTK_SIGNAL_FUNC (cb_tearable_enter_leave), GINT_TO_POINTER (TRUE)); gtk_signal_connect (GTK_OBJECT (tearable), "leave-notify-event", GTK_SIGNAL_FUNC (cb_tearable_enter_leave), GINT_TO_POINTER (FALSE)); gtk_signal_connect (GTK_OBJECT (tearable), "button-release-event", GTK_SIGNAL_FUNC (cb_tearable_button_release), (gpointer) combo_box); gtk_box_pack_start (GTK_BOX (vbox), tearable, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), pop_down_widget, TRUE, TRUE, 0); combo_box->priv->tearable = tearable; /* * Finish setup */ combo_box_set_display (combo_box, display_widget); gtk_container_add (GTK_CONTAINER (combo_box->priv->frame), vbox); gtk_widget_show_all (combo_box->priv->frame); } GtkWidget * combo_box_new (GtkWidget * display_widget, GtkWidget * optional_popdown) { ComboBox *combo_box; g_return_val_if_fail (display_widget != NULL, NULL); g_return_val_if_fail (GTK_IS_WIDGET (display_widget), NULL); combo_box = gtk_type_new (combo_box_get_type ()); combo_box_construct (combo_box, display_widget, optional_popdown); return GTK_WIDGET (combo_box); } void combo_box_set_arrow_relief (ComboBox * cc, GtkReliefStyle relief) { g_return_if_fail (cc != NULL); g_return_if_fail (IS_COMBO_BOX (cc)); gtk_button_set_relief (GTK_BUTTON (cc->priv->arrow_button), relief); } /** * combo_box_set_title * @combo: Combo box * @title: Title * * Set a title to display over the tearoff window. * * FIXME: * * This should really change the title even when the popup is already torn off. * I guess the tearoff window could attach a listener to title change or * something. But I don't think we need the functionality, so I didn't bother * to investigate. */ void combo_box_set_title (ComboBox * combo, const gchar * title) { g_return_if_fail (combo != NULL); g_return_if_fail (IS_COMBO_BOX (combo)); gtk_object_set_data_full (GTK_OBJECT (combo), "combo-title", g_strdup (title), (GtkDestroyNotify) g_free); } /** * combo_box_set_arrow_sensitive * @combo: Combo box * @sensitive: Sensitivity value * * Toggle the sensitivity of the arrow button */ void combo_box_set_arrow_sensitive (ComboBox * combo, gboolean sensitive) { g_return_if_fail (combo != NULL); gtk_widget_set_sensitive (combo->priv->arrow_button, sensitive); } /** * combo_box_set_tearable: * @combo: Combo box * @tearable: whether to allow the @combo to be tearable * * controls whether the combo box's pop up widget can be torn off. */ void combo_box_set_tearable (ComboBox * combo, gboolean tearable) { g_return_if_fail (combo != NULL); g_return_if_fail (IS_COMBO_BOX (combo)); if (tearable) { gtk_widget_show (combo->priv->tearable); } else { combo_set_tearoff_state (combo, FALSE); gtk_widget_hide (combo->priv->tearable); } } swami-2.2.0/src/swamigui/widgets/combo-box.h000066400000000000000000000053001361104770400207540ustar00rootroot00000000000000/* * Ripped and slightly modified for Swami from libgal-0.19.2 * * gtk-combo-box.h - a customizable combobox * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Miguel de Icaza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _COMBO_BOX_H_ #define _COMBO_BOX_H_ #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define COMBO_BOX_TYPE (combo_box_get_type ()) #define COMBO_BOX(obj) GTK_CHECK_CAST (obj, combo_box_get_type (), ComboBox) #define COMBO_BOX_CLASS(klass) \ GTK_CHECK_CLASS_CAST (klass, combo_box_get_type (), ComboBoxClass) #define IS_COMBO_BOX(obj) GTK_CHECK_TYPE (obj, combo_box_get_type ()) typedef struct _ComboBox ComboBox; typedef struct _ComboBoxPrivate ComboBoxPrivate; typedef struct _ComboBoxClass ComboBoxClass; struct _ComboBox { GtkHBox hbox; ComboBoxPrivate *priv; }; struct _ComboBoxClass { GtkHBoxClass parent_class; GtkWidget *(*pop_down_widget) (ComboBox *cbox); /* * invoked when the popup has been hidden, if the signal * returns TRUE, it means it should be killed from the */ gboolean *(*pop_down_done) (ComboBox *cbox, GtkWidget *); /* * Notification signals. */ void (*pre_pop_down) (ComboBox *cbox); void (*post_pop_hide) (ComboBox *cbox); }; GtkType combo_box_get_type (void); void combo_box_construct (ComboBox *combo_box, GtkWidget *display_widget, GtkWidget *optional_pop_down_widget); void combo_box_get_pos (ComboBox *combo_box, int *x, int *y); GtkWidget *combo_box_new (GtkWidget *display_widget, GtkWidget *optional_pop_down_widget); void combo_box_popup_hide (ComboBox *combo_box); void combo_box_set_display (ComboBox *combo_box, GtkWidget *display_widget); void combo_box_set_title (ComboBox *combo, const gchar *title); void combo_box_set_tearable (ComboBox *combo, gboolean tearable); void combo_box_set_arrow_sensitive (ComboBox *combo, gboolean sensitive); void combo_box_set_arrow_relief (ComboBox *cc, GtkReliefStyle relief); #ifdef __cplusplus }; #endif /* __cplusplus */ #endif /* _COMBO_BOX_H_ */ swami-2.2.0/src/swamigui/widgets/icon-combo.c000066400000000000000000000150131361104770400211110ustar00rootroot00000000000000/* * Ripped and modified for Swami from libgal-0.19.2 * * widget-pixmap-combo.c - A pixmap selector combo box * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Jody Goldberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include "combo-box.h" #include "icon-combo.h" /* from Swami src/swamigui */ #include "i18n.h" #define ICON_PREVIEW_WIDTH 15 #define ICON_PREVIEW_HEIGHT 15 enum { CHANGED, LAST_SIGNAL }; static void icon_combo_select_icon_index (IconCombo * ic, int index); static guint icon_combo_signals[LAST_SIGNAL] = { 0, }; static GObjectClass *icon_combo_parent_class; /***************************************************************************/ static void icon_combo_finalize (GObject *object) { IconCombo *ic = ICON_COMBO (object); // g_object_unref (GTK_OBJECT (ic->tool_tip)); g_free (ic->icons); (*icon_combo_parent_class->finalize) (object); } static void icon_combo_class_init (IconComboClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = icon_combo_finalize; icon_combo_parent_class = g_type_class_peek_parent (klass); icon_combo_signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (IconComboClass, changed), NULL, NULL, g_cclosure_marshal_VOID__INT, GTK_TYPE_NONE, 1, G_TYPE_INT); } GType icon_combo_get_type (void) { static GType type = 0; if (!type) { GTypeInfo info = { sizeof (IconComboClass), NULL, NULL, (GClassInitFunc) icon_combo_class_init, NULL, NULL, sizeof (IconCombo), 0, (GInstanceInitFunc) NULL, }; type = g_type_register_static (COMBO_BOX_TYPE, "IconCombo", &info, 0); } return (type); } static void emit_change (GtkWidget *button, IconCombo *ic) { g_return_if_fail (ic != NULL); g_return_if_fail (0 <= ic->last_index); g_return_if_fail (ic->last_index < ic->num_elements); gtk_signal_emit (GTK_OBJECT (ic), icon_combo_signals[CHANGED], ic->elements[ic->last_index].id); } static void icon_clicked (GtkWidget *button, IconCombo *ic) { int index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index")); icon_combo_select_icon_index (ic, index); emit_change (button, ic); combo_box_popup_hide (COMBO_BOX (ic)); } static void icon_table_setup (IconCombo *ic) { int row, col, index = 0; ic->combo_table = gtk_table_new (ic->cols, ic->rows, 0); ic->tool_tip = gtk_tooltips_new (); ic->icons = g_malloc (sizeof (GtkImage *) * ic->cols * ic->rows); for (row = 0; row < ic->rows; row++) { for (col = 0; col < ic->cols; ++col, ++index) { IconComboElement const *element = ic->elements + index; GtkWidget *button; if (element->stock_id == NULL) { /* exit both loops */ row = ic->rows; break; } ic->icons[index] = gtk_image_new_from_stock (element->stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR); button = gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (ic->icons[index])); gtk_tooltips_set_tip (ic->tool_tip, button, _(element->untranslated_tooltip), NULL); gtk_table_attach (GTK_TABLE (ic->combo_table), button, col, col + 1, row + 1, row + 2, GTK_FILL, GTK_FILL, 1, 1); g_signal_connect (button, "clicked", G_CALLBACK (icon_clicked), ic); g_object_set_data (G_OBJECT (button), "index", GINT_TO_POINTER (index)); } } ic->num_elements = index; gtk_widget_show_all (ic->combo_table); } static void icon_combo_construct (IconCombo *ic, IconComboElement const *elements, int ncols, int nrows) { g_return_if_fail (ic != NULL); g_return_if_fail (IS_ICON_COMBO (ic)); /* Our table selector */ ic->cols = ncols; ic->rows = nrows; ic->elements = elements; icon_table_setup (ic); ic->preview_button = gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (ic->preview_button), GTK_RELIEF_NONE); ic->preview_icon = gtk_image_new_from_stock (elements[0].stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_container_add (GTK_CONTAINER (ic->preview_button), GTK_WIDGET (ic->preview_icon)); // gtk_widget_set_usize (GTK_WIDGET (ic->preview_icon), 24, 24); g_signal_connect (ic->preview_button, "clicked", G_CALLBACK (emit_change), ic); gtk_widget_show_all (ic->preview_button); combo_box_construct (COMBO_BOX (ic), ic->preview_button, ic->combo_table); } GtkWidget * icon_combo_new (IconComboElement const *elements, int ncols, int nrows) { IconCombo *ic; g_return_val_if_fail (elements != NULL, NULL); g_return_val_if_fail (elements != NULL, NULL); g_return_val_if_fail (ncols > 0, NULL); g_return_val_if_fail (nrows > 0, NULL); ic = g_object_new (ICON_COMBO_TYPE, NULL); icon_combo_construct (ic, elements, ncols, nrows); return (GTK_WIDGET (ic)); } /* select a icon by its index */ static void icon_combo_select_icon_index (IconCombo *ic, int index) { g_return_if_fail (ic != NULL); g_return_if_fail (IS_ICON_COMBO (ic)); g_return_if_fail (0 <= index); g_return_if_fail (index < ic->num_elements); ic->last_index = index; gtk_container_remove (GTK_CONTAINER (ic->preview_button), ic->preview_icon); ic->preview_icon = gtk_image_new_from_stock (ic->elements[index].stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_widget_show (ic->preview_icon); gtk_container_add (GTK_CONTAINER (ic->preview_button), ic->preview_icon); } /* select a icon by its unique integer ID */ void icon_combo_select_icon (IconCombo *ic, int id) { int i; g_return_if_fail (ic != NULL); g_return_if_fail (IS_ICON_COMBO (ic)); g_return_if_fail (ic->num_elements > 0); for (i = 0; i < ic->num_elements; i++) if (ic->elements[i].id == id) break; if (i >= ic->num_elements) i = 0; icon_combo_select_icon_index (ic, i); } swami-2.2.0/src/swamigui/widgets/icon-combo.h000066400000000000000000000042401361104770400211160ustar00rootroot00000000000000/* * Ripped and modified for Swami from libgal-0.19.2 * * widget-pixmap-combo.h - A icon selector combo box * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Jody Goldberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef __ICON_COMBO_H__ #define __ICON_COMBO_H__ #include #include #include #include #include "combo-box.h" #define ICON_COMBO_TYPE (icon_combo_get_type ()) #define ICON_COMBO(obj) (GTK_CHECK_CAST((obj), ICON_COMBO_TYPE, IconCombo)) #define ICON_COMBO_CLASS(k) (GTK_CHECK_CLASS_CAST(k), ICON_COMBO_TYPE) #define IS_ICON_COMBO(obj) (GTK_CHECK_TYPE((obj), ICON_COMBO_TYPE)) typedef struct { char const *untranslated_tooltip; char *stock_id; /* icon stock ID */ int id; } IconComboElement; typedef struct { ComboBox combo_box; /* Static information */ IconComboElement const *elements; int cols, rows; int num_elements; /* State info */ int last_index; /* Interface elements */ GtkWidget *combo_table, *preview_button; GtkWidget *preview_icon; GtkTooltips *tool_tip; GtkWidget **icons; /* icon widgets */ } IconCombo; GType icon_combo_get_type (void); GtkWidget *icon_combo_new (IconComboElement const *elements, int ncols, int nrows); void icon_combo_select_icon (IconCombo *combo, int id); typedef struct { ComboBoxClass parent_class; /* Signals emited by this widget */ void (* changed) (IconCombo *icon_combo, int id); } IconComboClass; #endif /* __ICON_COMBO_H__ */ swami-2.2.0/src/swamish/000077500000000000000000000000001361104770400151005ustar00rootroot00000000000000swami-2.2.0/src/swamish/swamish.c000066400000000000000000000165041361104770400167250ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include typedef struct _SwamishCmd SwamishCmd; typedef void (*SwamishCmdCallback)(SwamishCmd *command, char **args, int count); struct _SwamishCmd { char *command; /* text of command */ SwamishCmdCallback callback; /* command callback function */ char *syntax; /* syntax description of command */ char *descr; /* description of command */ char *help; /* detailed help on command */ }; static void swamish_cmd_ls (SwamishCmd *command, char **args, int count); static void swamish_cmd_pwd (SwamishCmd *command, char **args, int count); static void swamish_cmd_quit (SwamishCmd *command, char **args, int count); static SwamishCmd swamish_commands[] = { { "cd", NULL, N_("cd PATH"), N_("Change current object"), N_("Change the current object\n" "The `PATH' parameter is the directory or object to change to.") }, { "close", NULL, N_("close PATH [PATH2]..."), N_("Close instrument files"), N_("Close one or more files.\n" "`PATH' is a path to an instrument file.") }, { "cp", NULL, N_("cp SRC [SRC2]... DEST"), N_("Copy objects"), N_("Copy one or more objects to a destination.\n" "`SRC' and `DEST' are paths to objects or directories.") }, { "get", NULL, N_("get PATH [PATH2]... [NAME]..."), N_("Get object properties"), N_("Get an instrument object's property values.\n" "`PATH' is the path to an instrument object.\n" "Property names can be specified, all are listed if not given.") }, { "help", NULL, N_("help"), N_("Get help"), N_("When you don't know what to do, get some help.") }, { "ls", swamish_cmd_ls, N_("ls [PATH]..."), N_("List directory or object contents"), N_("List directory or instrument object children.\n" "The optional parameters can be objects and/or directories.\n" "If no parameters are given, the current directory is displayed.") }, { "new", NULL, N_("new [TYPE]"), N_("Create a new instrument object"), N_("Create a new instrument object within the current path.\n" "`TYPE' is the type of object to create.\n" "Available types are displayed if not specified.") }, { "pwd", swamish_cmd_pwd, N_("pwd"), N_("Print current object path"), N_("Displays the current directory or object path.") }, { "quit", swamish_cmd_quit, N_("quit"), N_("Quit"), N_("Exit the Swami Shell") }, { "rm", NULL, N_("rm PATH [PATH2]..."), N_("Remove files or objects"), N_("Remove one or more objects or files.\n" "`PATH' is a path to a directory or object.") }, { "save", NULL, N_("save PATH [PATH2]..."), N_("Save instrument files"), N_("Save one or more instrument files.\n" "`PATH' is a path to an instrument file.") }, { "saveas", NULL, N_("saveas PATH NEWPATH"), N_("Save instrument file as another file"), N_("Save an instrument file to a different name.\n" "`PATH' is a path to an instrument file.\n" "`NEWPATH' is a new file path to save to.") }, { "set", NULL, N_("set PATH [PATH2]... NAME=VALUE..."), N_("Set object properties"), N_("Set properties of an instrument object.\n" "`PATH' is the path to an instrument object.\n" "One or more property `NAME=VALUE' pairs may be given.") }, }; gboolean exit_swamish = FALSE; // Set to TRUE to exit char *current_dir = NULL; // Current directory of current path char *current_obj = NULL; // Current obj of path or NULL (appended to dir) rl_compentry_func_t *complete_command () { } char * get_cmd (void) { char *line; line = readline("swami> "); if (line && *line) /* add line to history if it has any text */ add_history(line); return (line); } int main(void) { char *line; SwamishCmd *cmdinfo; int i; current_dir = g_get_current_dir (); do { line = get_cmd(); for (i = 0; i < G_N_ELEMENTS (swamish_commands); i++) { cmdinfo = &swamish_commands[i]; // Command matches? if (strcmp (cmdinfo->command, line) == 0) break; } free (line); // Was there a match and has a callback? if (i < G_N_ELEMENTS (swamish_commands)) { if (cmdinfo->callback) cmdinfo->callback (cmdinfo, NULL, 0); } else printf ("Unknown command\n"); } while (!exit_swamish); printf("See ya!\n"); return 0; } /* get a file listing for a directory (excluding '.' and '..' entries), returns NULL terminated array of strings which should be freed with g_strfreev() */ char ** get_path_contents (char *path, GError **err) { GDir *dh; const char *fname; GPtrArray *file_array; char **retptr; g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (!err || !*err, NULL); dh = g_dir_open (path, 0, err); if (!dh) return (NULL); file_array = g_ptr_array_new (); while ((fname = g_dir_read_name (dh))) g_ptr_array_add (file_array, g_strdup (fname)); g_ptr_array_sort (file_array, (GCompareFunc)strcmp); /* sort the array */ g_ptr_array_add (file_array, NULL); /* NULL terminate */ g_dir_close (dh); retptr = (char **)(file_array->pdata); g_ptr_array_free (file_array, FALSE); return (retptr); } #if 0 static gboolean parse_path (const char *path, char **ret_dir, char **ret_obj) { char *fullpath = NULL; g_return_val_if_fail (path != NULL && *path, FALSE); if (!g_path_is_absolute (path)) { fullpath = g_build_filename (current_dir, path, NULL); path = fullpath; } if (fullpath) g_free (fullpath); return (TRUE); } static void swamish_cmd_cd (SwamishCmd *command, char **args, int count) { } #endif static void swamish_cmd_ls (SwamishCmd *command, char **args, int count) { char **files, **s; GError *err = NULL; if (!current_obj) // Is current path a directory? { files = get_path_contents (current_dir, &err); if (!files) { g_critical ("Error while getting directory listing: %s\n", ipatch_gerror_message (err)); g_clear_error (&err); return; } } else return; s = files; while (*s) { printf ("%s\n", *s); s++; } g_strfreev (files); } static void swamish_cmd_pwd (SwamishCmd *command, char **args, int count) { char *path; path = g_build_filename (current_dir, current_obj, NULL); printf ("%s\n", path); g_free (path); } static void swamish_cmd_quit (SwamishCmd *command, char **args, int count) { exit_swamish = TRUE; } swami-2.2.0/swami.anjuta000066400000000000000000000030341361104770400151620ustar00rootroot00000000000000 swami-2.2.0/swami.appdata.xml000066400000000000000000000026301361104770400161120ustar00rootroot00000000000000 swami.desktop CC0-1.0 GPL-2.0

SWAMI (Sampled Waveforms And Musical Instruments) is a collection of free software for editing, managing and playing musical instruments for MIDI music composition.

Software features:

  • Uses FluidSynth for audio synthesis
  • 24 bit SoundFont support
  • Loop point cross section view
  • Real time effect modulators
  • Multi file browsing and editing interface
  • Supports several instrument formats
  • Loop point finder
  • Audio sample tuner
http://www.swamiproject.org/Swami-screenshot1.png http://www.swamiproject.org/Swami-screenshot2.png http://www.swamiproject.org/Swami-screenshot3.png http://www.swamiproject.org/Swami-screenshot4.png http://www.swamiproject.org element@elementsofsound.org
swami-2.2.0/swami.desktop000066400000000000000000000010311361104770400153440ustar00rootroot00000000000000[Desktop Entry] Encoding=UTF-8 Name=Swami Instrument Editor Name[ru]=Редактор сэмплов Swami GenericName=Instrument Editor GenericName[ru]=Редактор сэмплов Comment=Create, play and organize MIDI instruments and sounds Comment[ru]=Создание, воспроизведение и организация звуковых сэмплов Exec=swami %F Icon=swami StartupNotify=true Terminal=false Type=Application Categories=GTK;Application;AudioVideo;Audio;Midi;Music; MimeType=audio/dls;audio/x-soundfont; swami-2.2.0/swami.ico000066400000000000000000000226761361104770400144670ustar00rootroot0000000000000000 %(0` "",,--(++*,,#))""   ))''!--"))$$ /Wcoސ XXOOxho&&" &&kh<<mm))gOOh!v۔DDw""## SS88 !!}FF[Dcce $$%%$$ 55uuttRR!!##$$""qee(Krr..!!$$%%""!!##%%## NN2'N88++0%%Z$$%%##!! ""%%%%""++A++$ aa Z{  ++))2##f$$''''''''%%!!&&P((-))++ 'k4S ++((&''Oܪ%%))**''""**7++""$$v }$$++$!!e!!''))%%--===!! ZS@@%%''""33E ;V, |~~""&& 77e L4-݇ %% ((1f) ++5}%% --9TT4t5$$++0z%% **=&&La:++ )),xݪ%%!!,,@$$-].. ++*xש$$!!++B##" C;))%%!!~~k(gi) X55$$!!6d33%%""JR_zz --(%%""**H225r)h ++*%%""))J++33ik &&))2%%""**O++ ++.. ((,,((-,,KŶ &&##++_((3,,++++ ++=O++ ++..',,: gʨ$$((&& ö **P))8&&(''""$$$$++))%--? $$((**))%% ݏ&&K((-''++  ##++/""$$%%''**,,++))''$$""))D++$ ++,,4##%%$$$$##&&))++++((&&%%%%%%## o)),&&$$##,,4 $$%%"" $$))((##""$$%%##++0&&++''.!!$$$$¿ ~,,cî$$##((ɏ""$$$$ ((- $$#r %%##!!r((9**+((3,,K++6((9%%Z $$$$ }((&'' ,,))>$$$$''T++*##"",, @^sll&&($$&&(( ++6z$$$$Y$$ '' ,,)##%% &&X((&  ((Ekg(('(($$ $$)),""i$$##~++0 ,,%%L~%%""++* (('' **)&&&$$**+ v!!%%!! `$$$$ ))%##$$))8$$,,#**7bX))2))$$(())2$$$$)),'' ++6ܮ$$""""b''! ""'''n{yng..! $$$$((L!!%% $$G$$&& ^""$$**7 ""xx q P!!++""++/$$##"" ))%$$##))% $$**+qڜ$$''''""w&&"  (( n##$$&&( $$%%0Ь$$""((L)) ))2n&&****%%q''' !!++B!!$$))2""''; $$ **7++$$''.o%%((((##v++$  ,,4۲$$ ++;!!$$M""$$)),''  ''!}##""yn''$$ **+$$""$$N!!''l##$$))% (())2ryt&&($$ ++$$$"" a''&&t##$$''!$$ ##++*h{V))%++++$$(( $$##p&&''w""""##$$"",,,,$$$$'' ++$$""""|&&##q|}a!!33$$$$!!d|}z!! ''5_|{f''. ""--.`z{]9  ))]YhS   ++YVVxj))  &&..&&((  ((&&&&(( ???x>??????8<????x<???swami-2.2.0/swami.png000066400000000000000000000107071361104770400144710ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<DIDAThyU?wNg4I#D@6 &(#4I@G< ȜG(9EAG"HрBT5 !4YuR{ I; swnխ{s" ?!J C^"Lȶ6\[q3v%/q`ӣhjYX{;CB9ǾE{;~6KCS_ֳzݽջ@)k{x`N az>E@ x@ʸ5^wLBPl_*NK@){OTj~mxRiձZ(NDZ{,YPCF8G*9&R@azQ&3xz# F+UC֭Bo}|8eGT*"&xR)0,z27p(ICw<8"m sZ;:B "9iZV*25fY ̛Ga<Յ,D/\D8йpBk RŪiJ"߶cǵ"8U3J]ϭhlM֒P5LAD[\ܨT=ps RWU*T7 k F A6v >9yNdN 9L$s%##?*QrBZis!ZOT0@Aֆmi2KZ㱷=L Y;aϛ3y3Do'ɔJ8Yf F8cRaL &"ȭPmO!)%Z,e!H]Se>Q ARs%[@D5E! AFx3{+311fcQA@X|xbL7CX4t"ɵut FAC]/sW~V kCxΕf<$6lxydLoߕJ}8*T*44\%;Gɐ*;4tZO97i)qRSBaQׂ#ym$P.?SLO<$o9$vQKo 7F+ߖ2'xQ*uDgVt7ĕCR#SAl' _Ap r6L@(d]$7E,Bd|!R3|\({PTiwyNq֓k=kR)(UW,vFdcv$;I])kl\3)60e E84n&l}WIZ郃?9RٶLfZzZ2Ai'q$N *oHZ[WRNZ3~=!flڀyz$7)EL*:Ry}`Rn~Ik݊e*װ6 kkn CB0$zM`rsGx\).^6@̛V R!5$Qkf 5[Nv)hlEƨfs PYCass,W÷_7 ` '+Ep"p1[cgS jo3&_2]+ #pHAPms;菉vv+́/`2*7׆ZB +WriW9͛yB) $U^j\iӈ#q{]vu5t)/,wDs|mO"Aㆆy?U%P,s3tk&C[g5d>g`JJu|q'?Iq\B 6~b!` dF#bJM` pKg'w3CJ*B/!:q!x*'zz%<8Ia"s CC}pL_McǃDGpRƞ~p ˩vXÏ/_x!ij=(1YG35{_ C^ظA%Djas2*#ưzxN 9žn]i)6a-0G[M( cX?ez{r8/?f6ˡv}Z|8N̶֫:y^9D ˖q1,E޶Q1B%%_:<v)_p wW5Cog߿~R(ϛBJ>K7I*>Ge9Vl9\`J;Ga<N>WRX̥B\.IyECäּGg ]~qkql_'EWfm7ed8V*,U H#Ԕgɒ %xJGƚRccv7:{QkFzWc㲕o-_s|L).>0~ H|9oYxnP-sexע5kv,y^zJ5Z,y z֋Qrj(1 ǽ8}Nz3N462Crҥ<=4)9α?Gp]RG}HDڦlYp1ieՇH\T68Ŝ|I`T{@~./;@JO!}>th=!4ƌ$ہ>N8xN9r %T*c=x^{O>Gdϒɀ<"%euс~i o-Hـs!Iϡ>ʔ) D5EJꫯf} FHوs!F,8 Sx+xd|97K;BH.wN<-s YrO4 AsXcT,n]* E|vQ>P/&P#͞M&^" 2RQhfbOt|$ a&Zz IENDB`swami-2.2.0/swami.svg000066400000000000000000000377211361104770400145110ustar00rootroot00000000000000 image/svg+xml swami-2.2.0/swami.xml000066400000000000000000000012641361104770400145030ustar00rootroot00000000000000 Downloadable Sounds SoundFont