From 102e899c331bd2ed9902467a077164e209c918f9 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 12 Aug 2008 00:20:16 +0000 Subject: VSTUI X11 port and embeddable GTK wrapper. Build mdaSpecMeter and GUI. git-svn-id: http://svn.drobilla.net/lad/mda-lv2@1340 a436a847-0d15-0410-975c-d299462d15a1 --- Makefile | 34 +- lv2_ui.h | 372 +++++ lvz/AEffEditor.hpp | 30 + lvz/AudioEffect.hpp | 1 + lvz/audioeffectx.h | 40 +- lvz/gendata.cpp | 100 +- lvz/gui_wrapper.cpp | 211 +++ lvz/wrapper.cpp | 47 +- src/mdaSpecMeter.cpp | 482 ++++++ src/mdaSpecMeter.h | 19 +- src/mdaSpecMeterGUI.cpp | 390 ++--- src/mdaSpecMeterGUI.h | 36 +- src/mdaspecmeter.cpp | 416 ----- vstgui/TODO | 19 + vstgui/vstcontrols.cpp | 3651 +++++++++++++++++++++++++++++++++++++++++++ vstgui/vstcontrols.h | 994 ++++++++++++ vstgui/vstgui.cpp | 3996 +++++++++++++++++++++++++++++++++++++++++++++++ vstgui/vstgui.h | 1105 +++++++++++++ vstgui/vstkeycode.h | 104 ++ 19 files changed, 11376 insertions(+), 671 deletions(-) create mode 100644 lv2_ui.h create mode 100644 lvz/AEffEditor.hpp create mode 120000 lvz/AudioEffect.hpp create mode 100644 lvz/gui_wrapper.cpp create mode 100644 src/mdaSpecMeter.cpp delete mode 100644 src/mdaspecmeter.cpp create mode 100644 vstgui/TODO create mode 100644 vstgui/vstcontrols.cpp create mode 100644 vstgui/vstcontrols.h create mode 100644 vstgui/vstgui.cpp create mode 100644 vstgui/vstgui.h create mode 100644 vstgui/vstkeycode.h diff --git a/Makefile b/Makefile index 7a12f90..1627fd2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -#CFLAGS = -O0 -g -ansi -pedantic -Wall -Wextra -Wshadow -Woverloaded-virtual -Wno-unused -CFLAGS += -fPIC -DPIC -Ilvz -I. -DPLUGIN_URI_PREFIX=\"http://drobilla.net/ns/dev/mda-lv2/\" +#CFLAGS = -O0 -g -ansi -Wall -Wextra -Wno-unused # -pedantic -Woverloaded-virtual +CFLAGS += -fPIC -DPIC -Ilvz -Ivstgui -I. -DURI_PREFIX=\"http://drobilla.net/ns/dev/mda-lv2/\" SYSTEMNAME = $(shell uname -s) @@ -16,8 +16,10 @@ SYSTEM_INSTALL_DIR = /usr/lib/lv2/ LOCAL_INSTALL_DIR = /usr/local/lib/lv2/ endif +BUILD_GUI = ! `pkg-config --exists gtk+-2.0` +GUI_CFLAGS = $(CFLAGS) -Ivstgui `pkg-config --cflags gtk+-2.0 libpng` -all: lvz/gendata libs data +all: lvz/gendata libs data gui_libs bundle: mkdir -p ./mda.lv2 @@ -58,9 +60,16 @@ libs: bundle \ mda.lv2/mdaTracker.so \ mda.lv2/mdaTransient.so \ mda.lv2/mdaVocInput.so \ - mda.lv2/mdaVocoder.so + mda.lv2/mdaVocoder.so \ + mda.lv2/mdaSpecMeter.so -data: libs lvz/gendata +pixmaps: + cp src/mdaSpecMeter.png mda.lv2 + +gui_libs: bundle pixmaps \ + mda.lv2/mdaSpecMeterGUI.so + +data: libs gui_libs lvz/gendata cd ./mda.lv2 && ../lvz/gendata ./*.so > manifest.ttl install: @@ -73,6 +82,7 @@ install: install -d $(INSTALL_DIR)/mda.lv2; \ install -m 644 ./mda.lv2/*.ttl $(INSTALL_DIR)/mda.lv2; \ install -m 755 ./mda.lv2/*.so $(INSTALL_DIR)/mda.lv2; \ + install -m 755 ./mda.lv2/*.png $(INSTALL_DIR)/mda.lv2; \ fi install-user: @@ -94,12 +104,24 @@ src/%.cpp: src/%.h lvz/audioeffectx.h lvz/gendata: lvz/gendata.cpp lvz/audioeffectx.h $(CXX) $(CFLAGS) -ldl $< -o $@ +mda.lv2/%GUI.so: src/%GUI.cpp src/%.cpp lvz/gui_wrapper.cpp vstgui/vstgui.cpp vstgui/vstgui.h vstgui/vstcontrols.cpp vstgui/vstcontrols.h + if [ $(BUILD_GUI) ]; then \ + $(CXX) $(SHARED_LDFLAGS) $(GUI_CFLAGS) \ + -DUI_CLASS=`echo $@ | sed 's/mda.lv2\///' | sed 's/\..*//'` \ + -DPLUGIN_CLASS=`echo $@ | sed 's/mda.lv2\///' | sed 's/\..*//' | sed 's/GUI//'` \ + -DUI_HEADER=\"`echo $@ | sed 's/^mda.lv2/src/' | sed 's/\(.*\)\..*/\1/' | sed 's/$$/\.h/'`\" \ + -DPLUGIN_HEADER=\"`echo $@ | sed 's/^mda.lv2/src/' | sed 's/\(.*\)\..*/\1/' | sed 's/$$/\.h/' | sed 's/GUI//'`\" \ + -DUI_URI_SUFFIX=\"`echo $@ | sed 's/mda.lv2\///' | sed 's/^mda//' | sed 's/\..*//'`\" \ + -DPLUGIN_URI_SUFFIX=\"`echo $@ | sed 's/mda.lv2\///' | sed 's/^mda//' | sed 's/\..*//' | sed 's/GUI//'`\" \ + $^ -o $@; \ + fi + mda.lv2/%.so: src/%.cpp lvz/wrapper.cpp $(CXX) $(SHARED_LDFLAGS) $(CFLAGS) \ -DPLUGIN_CLASS=`echo $@ | sed 's/mda.lv2\///' | sed 's/\..*//'` \ -DPLUGIN_URI_SUFFIX=\"`echo $@ | sed 's/mda.lv2\///' | sed 's/^mda//' | sed 's/\..*//'`\" \ -DPLUGIN_HEADER=\"`echo $@ | sed 's/^mda.lv2/src/' | sed 's/\(.*\)\..*/\1/' | sed 's/$$/\.h/'`\" \ - $< lvz/wrapper.cpp -o $@ + $^ -o $@ clean: rm -f `find -name '*.o'` diff --git a/lv2_ui.h b/lv2_ui.h new file mode 100644 index 0000000..4be2c24 --- /dev/null +++ b/lv2_ui.h @@ -0,0 +1,372 @@ +/************************************************************************ + * + * In-process UI extension for LV2 + * + * Copyright (C) 2006-2008 Lars Luthman + * + * Based on lv2.h, which was + * + * Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, + * Stefan Westerfeld + * Copyright (C) 2006 Steve Harris, Dave Robillard. + * + * This header is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, + * or (at your option) any later version. + * + * This header is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA. + * + ***********************************************************************/ + +/** @file + This extension defines an interface that can be used in LV2 plugins and + hosts to create UIs for plugins. The UIs are plugins that reside in + shared object files in an LV2 bundle and are referenced in the RDF data + using the triples (Turtle shown) +
    
+    @@prefix uiext:  .
+        uiext:ui      .
+        a            uiext:GtkUI .
+      uiext:binary  .
+
+ where is the URI of the plugin, is + the URI of the plugin UI and is the relative URI to the shared + object file. While it is possible to have the plugin UI and the plugin in + the same shared object file it is probably a good idea to keep them + separate so that hosts that don't want UIs don't have to load the UI code. + A UI MUST specify its class in the RDF data, in this case uiext:GtkUI. The + class defines what type the UI is, e.g. what graphics toolkit it uses. + There are no UI classes defined in this extension, those are specified + separately (and anyone can define their own). + + (Note: the prefix above is used throughout this file for the same URI) + + It's entirely possible to have multiple UIs for the same plugin, or to have + the UI for a plugin in a different bundle from the actual plugin - this + way people other than the plugin author can write plugin UIs independently + without editing the original plugin bundle. + + Note that the process that loads the shared object file containing the UI + code and the process that loads the shared object file containing the + actual plugin implementation does not have to be the same. There are many + valid reasons for having the plugin and the UI in different processes, or + even on different machines. This means that you can _not_ use singletons + and global variables and expect them to refer to the same objects in the + UI and the actual plugin. The function callback interface defined in this + header is all you can expect to work. + + Since the LV2 specification itself allows for extensions that may add + new types of data and configuration parameters that plugin authors may + want to control with a UI, this extension allows for meta-extensions that + can extend the interface between the UI and the host. These extensions + mirror the extensions used for plugins - there are required and optional + "features" that you declare in the RDF data for the UI as +
    
+     uiext:requiredFeature  .
+     uiext:optionalFeature  .
+
+ These predicates have the same semantics as lv2:requiredFeature and + lv2:optionalFeature - if a UI is declaring a feature as required, the + host is NOT allowed to load it unless it supports that feature, and if it + does support a feature (required or optional) it MUST pass that feature's + URI and any additional data (specified by the meta-extension that defines + the feature) in a LV2_Feature struct (as defined in lv2.h) to the UI's + instantiate() function. + + These features may be used to specify how to pass data between the UI + and the plugin port buffers - see LV2UI_Write_Function for details. + + There are four features defined in this extension that hosts may want to + implement: + +
+    uiext:makeResident
+
+ If this feature is required by a UI the host MUST NEVER unload the shared + library containing the UI implementation during the lifetime of the host + process (e.g. never calling dlclose() on Linux). This feature may be + needed by e.g. a Gtk UI that registers its own Glib types using + g_type_register_static() - if it gets unloaded and then loaded again the + type registration will break, since there is no way to unregister the + types when the library is unloaded. The data pointer in the LV2_Feature + for this feature should always be set to NULL. + +
+    uiext:makeSONameResident
+
+ This feature is ELF specific - it should only be used by UIs that + use the ELF file format for the UI shared object files (e.g. on Linux). + If it is required by an UI the UI should also list a number of SO names + (shared object names) for libraries that the UI shared object + depends on and that may not be unloaded during the lifetime of the host + process, using the predicate @c uiext:residentSONames, like this: +
+     uiext:residentSONames "libgtkmm-2.4.so.1", "libfoo.so.0"
+
+ The host MUST then make sure that the shared libraries with the given ELF + SO names are not unloaded when the plugin UI is, but stay loaded during + the entire lifetime of the host process. On Linux this can be accomplished + by calling dlopen() on the shared library file with that SO name and never + calling a matching dlclose(). However, if a plugin UI requires the + @c uiext:makeSONameResident feature, it MUST ALWAYS be safe for the host to + just never unload the shared object containing the UI implementation, i.e. + act as if the UI required the @c uiext:makeResident feature instead. Thus + the host only needs to find the shared library files corresponding to the + given SO names if it wants to save RAM by unloading the UI shared object + file when it is no longer needed. The data pointer for the LV2_Feature for + this feature should always be set to NULL. + +
+    uiext:noUserResize
+
+ If an UI requires this feature it indicates that it does not make sense + to let the user resize the main widget, and the host should prevent that. + This feature may not make sense for all UI types. The data pointer for the + LV2_Feature for this feature should always be set to NULL. + +
+    uiext:fixedSize
+
+ If an UI requires this feature it indicates the same thing as + uiext:noUserResize, and additionally it means that the UI will not resize + the main widget on its own - it will always remain the same size (e.g. a + pixmap based GUI). This feature may not make sense for all UI types. + The data pointer for the LV2_Feature for this feature should always be set + to NULL. + + + UIs written to this specification do not need to be threadsafe - the + functions defined below may only be called in the same thread as the UI + main loop is running in. + + Note that this UI extension is NOT a lv2:Feature. There is no way for a + plugin to know whether the host that loads it supports UIs or not, and + the plugin must ALWAYS work without the UI (although it may be rather + useless unless it has been configured using the UI in a previous session). + + A UI does not have to be a graphical widget, it could just as well be a + server listening for OSC input or an interface to some sort of hardware + device, depending on the RDF class of the UI. +*/ + +#ifndef LV2_UI_H +#define LV2_UI_H + +#include + +#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** A pointer to some widget or other type of UI handle. + The actual type is defined by the type URI of the UI. + All the functionality provided by this extension is toolkit + independent, the host only needs to pass the necessary callbacks and + display the widget, if possible. Plugins may have several UIs, in various + toolkits. */ +typedef void* LV2UI_Widget; + + +/** This handle indicates a particular instance of a UI. + It is valid to compare this to NULL (0 for C++) but otherwise the + host MUST not attempt to interpret it. The UI plugin may use it to + reference internal instance data. */ +typedef void* LV2UI_Handle; + + +/** This handle indicates a particular plugin instance, provided by the host. + It is valid to compare this to NULL (0 for C++) but otherwise the + UI plugin MUST not attempt to interpret it. The host may use it to + reference internal plugin instance data. */ +typedef void* LV2UI_Controller; + + +/** This is the type of the host-provided function that the UI can use to + send data to a plugin's input ports. The @c buffer parameter must point + to a block of data, @c buffer_size bytes large. The contents of this buffer + and what the host should do with it depends on the value of the @c format + parameter. + + The @c format parameter should either be 0 or a numeric ID for a "Transfer + mechanism". Transfer mechanisms are Features and may be defined in + meta-extensions. They specify how to translate the data buffers passed + to this function to input data for the plugin ports. If a UI wishes to + write data to an input port, it must list a transfer mechanism Feature + for that port's class as an optional or required feature (depending on + whether the UI will work without being able to write to that port or not). + The only exception is when the UI wants to write single float values to + input ports of the class lv2:ControlPort, in which case @c buffer_size + should always be 4, the buffer should always contain a single IEEE-754 + float, and @c format should be 0. + + The numeric IDs for the transfer mechanisms are provided by a + URI-to-integer mapping function provided by the host, using the URI Map + feature with the map URI + "http://lv2plug.in/ns/extensions/ui". Thus a UI that requires transfer + mechanism features also requires the URI Map feature, but this is + implicit - the UI does not have to list the URI map feature as a required + or optional feature in it's RDF data. + + An UI MUST NOT pass a @c format parameter value (except 0) that has not + been returned by the host-provided URI mapping function for a + host-supported transfer mechanism feature URI. + + The UI MUST NOT try to write to a port for which there is no specified + transfer mechanism, or to an output port. The UI is responsible for + allocating the buffer and deallocating it after the call. +*/ +typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + +/** This struct contains the implementation of an UI. A pointer to an + object of this type is returned by the lv2ui_descriptor() function. +*/ +typedef struct _LV2UI_Descriptor { + + /** The URI for this UI (not for the plugin it controls). */ + const char* URI; + + /** Create a new UI object and return a handle to it. This function works + similarly to the instantiate() member in LV2_Descriptor. + + @param descriptor The descriptor for the UI that you want to instantiate. + @param plugin_uri The URI of the plugin that this UI will control. + @param bundle_path The path to the bundle containing the RDF data file + that references this shared object file, including the + trailing '/'. + @param write_function A function provided by the host that the UI can + use to send data to the plugin's input ports. + @param controller A handle for the plugin instance that should be passed + as the first parameter of @c write_function. + @param widget A pointer to an LV2UI_Widget. The UI will write a + widget pointer to this location (what type of widget + depends on the RDF class of the UI) that will be the + main UI widget. + @param features An array of LV2_Feature pointers. The host must pass + all feature URIs that it and the UI supports and any + additional data, just like in the LV2 plugin + instantiate() function. Note that UI features and plugin + features are NOT necessarily the same, they just share + the same data structure - this will probably not be the + same array as the one the plugin host passes to a + plugin. + */ + LV2UI_Handle (*instantiate)(const struct _LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features); + + + /** Destroy the UI object and the associated widget. The host must not try + to access the widget after calling this function. + */ + void (*cleanup)(LV2UI_Handle ui); + + /** Tell the UI that something interesting has happened at a plugin port. + What is interesting and how it is written to the buffer passed to this + function is defined by the @c format parameter, which has the same + meaning as in LV2UI_Write_Function. The only exception is ports of the + class lv2:ControlPort, for which this function should be called + when the port value changes (it does not have to be called for every + single change if the host's UI thread has problems keeping up with + the thread the plugin is running in), @c buffer_size should be 4 and the + buffer should contain a single IEEE-754 float. In this case the @c format + parameter should be 0. + + By default, the host should only call this function for input ports of + the lv2:ControlPort class. However, the default setting can be modified + by using the following URIs in the UI's RDF data: +
+      uiext:portNotification
+      uiext:noPortNotification
+      uiext:plugin
+      uiext:portIndex
+      
+ For example, if you want the UI with uri + for the plugin with URI + to get notified when the value of the + output control port with index 4 changes, you would use the following + in the RDF for your UI: +
+       uiext:portNotification [ uiext:plugin  ;
+                                                      uiext:portIndex 4 ] .
+      
+ and similarly with uiext:noPortNotification if you wanted + to prevent notifications for a port for which it would be on by default + otherwise. The UI is not allowed to request notifications for ports of + types for which no transfer mechanism is specified, if it does it should + be considered broken and the host should not load it. + + The @c buffer is only valid during the time of this function call, so if + the UI wants to keep it for later use it has to copy the contents to an + internal buffer. + + This member may be set to NULL if the UI is not interested in any + port events. + */ + void (*port_event)(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + /** Returns a data structure associated with an extension URI, for example + a struct containing additional function pointers. Avoid returning + function pointers directly since standard C++ has no valid way of + casting a void* to a function pointer. This member may be set to NULL + if the UI is not interested in supporting any extensions. This is similar + to the extension_data() member in LV2_Descriptor. + */ + const void* (*extension_data)(const char* uri); + +} LV2UI_Descriptor; + + + +/** A plugin UI programmer must include a function called "lv2ui_descriptor" + with the following function prototype within the shared object + file. This function will have C-style linkage (if you are using + C++ this is taken care of by the 'extern "C"' clause at the top of + the file). This function will be accessed by the UI host using the + @c dlsym() function and called to get a LV2UI_UIDescriptor for the + wanted plugin. + + Just like lv2_descriptor(), this function takes an index parameter. The + index should only be used for enumeration and not as any sort of ID number - + the host should just iterate from 0 and upwards until the function returns + NULL or a descriptor with an URI matching the one the host is looking for. +*/ +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index); + + +/** This is the type of the lv2ui_descriptor() function. */ +typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lvz/AEffEditor.hpp b/lvz/AEffEditor.hpp new file mode 100644 index 0000000..2d5152c --- /dev/null +++ b/lvz/AEffEditor.hpp @@ -0,0 +1,30 @@ +#ifndef __LVZ_AUDIOEFFECT_HPP +#define __LVZ_AUDIOEFFECT_HPP + +class AudioEffect; + +class AEffEditor { +public: + AEffEditor (AudioEffect* eff) + : effect(eff) + , URI("NULL") + , pluginURI("NULL") + {} + + virtual bool open(void* ptr) { return true; } + + virtual void idle() {} + + virtual const char* getURI() { return URI; } + virtual void setURI(const char* u) { URI = u; } + + virtual const char* getPluginURI() { return pluginURI; } + virtual void setPluginURI(const char* u) { pluginURI = u; } + +protected: + AudioEffect* effect; + const char* URI; + const char* pluginURI; +}; + +#endif // __LVZ_AUDIOEFFECT_HPP diff --git a/lvz/AudioEffect.hpp b/lvz/AudioEffect.hpp new file mode 120000 index 0000000..df90333 --- /dev/null +++ b/lvz/AudioEffect.hpp @@ -0,0 +1 @@ +audioeffectx.h \ No newline at end of file diff --git a/lvz/audioeffectx.h b/lvz/audioeffectx.h index 288d1cd..a1f975a 100644 --- a/lvz/audioeffectx.h +++ b/lvz/audioeffectx.h @@ -22,10 +22,29 @@ #include #include +// Some plugins seem to use these names... +#ifndef VstInt32 +# define VstInt32 LvzInt32 +# define VstInt16 LvzInt16 +#endif +#define VstEvents LvzEvents +#define VstMidiEvent LvzMidiEvent +#define VstPinProperty LvzPinProperty + typedef int16_t LvzInt16; typedef int32_t LvzInt32; typedef int (*audioMasterCallback)(int, int ver, int, int, int, int); +class AEffEditor; + +struct VstFileSelect { + int reserved; + char* returnPath; + size_t sizeReturnPath; + char** returnMultiplePaths; + long nbReturnPath; +}; + enum LvzPinFlags { kLvzPinIsActive = 1<<0, kLvzPinIsStereo = 1<<1 @@ -59,9 +78,16 @@ struct LvzEvents { class AudioEffect { public: + AudioEffect() : editor(NULL) {} virtual ~AudioEffect() {} + + void setEditor(AEffEditor* e) { editor = e; } + virtual void masterIdle() {} +protected: + AEffEditor* editor; }; + class AudioEffectX : public AudioEffect { public: AudioEffectX(audioMasterCallback audioMaster, LvzInt32 progs, LvzInt32 params) @@ -91,10 +117,11 @@ public: virtual void getParameterName(LvzInt32 index, char *label) = 0; virtual bool getProductString(char* text) = 0; - virtual void canMono() {} - virtual void canProcessReplacing() {} - virtual void isSynth() {} - virtual void wantEvents() {} + virtual bool canHostDo(const char* act) { return false; } + virtual void canMono() {} + virtual void canProcessReplacing() {} + virtual void isSynth() {} + virtual void wantEvents() {} virtual void setBlockSize(LvzInt32 size) {} virtual void setNumInputs(LvzInt32 num) { numInputs = num; } @@ -104,6 +131,11 @@ public: virtual void setURI(const char* uri) { URI = uri; } virtual void setUniqueID(const char* id) { uniqueID = id; } virtual void suspend() {} + virtual void beginEdit(VstInt32 index) {} + virtual void endEdit(VstInt32 index) {} + + virtual bool openFileSelector (VstFileSelect* sel) { return false; } + virtual bool closeFileSelector (VstFileSelect* sel) { return false; } protected: const char* URI; diff --git a/lvz/gendata.cpp b/lvz/gendata.cpp index 7f1d501..bf9c9ab 100644 --- a/lvz/gendata.cpp +++ b/lvz/gendata.cpp @@ -17,12 +17,14 @@ */ #include +#include #include #include #include #include #include #include "audioeffectx.h" +#include "AEffEditor.hpp" using namespace std; @@ -34,13 +36,16 @@ char name_buf[MAX_NAME_LENGTH]; struct Record { - Record(const string& u, const string& n) : uri(u), base_name(n) {} - string uri; + Record(const string& n) : base_name(n) {} string base_name; + typedef list UIs; + UIs uis; }; -typedef std::list Manifest; +typedef std::map Manifest; Manifest manifest; +typedef std::map GUIManifest; +GUIManifest gui_manifest; string @@ -85,8 +90,8 @@ symbolify(const char* name) void write_plugin(AudioEffectX* effect, const string& lib_file_name) { - string base_name = lib_file_name.substr(0, lib_file_name.find_last_of(".")); - string data_file_name = base_name + ".ttl"; + const string base_name = lib_file_name.substr(0, lib_file_name.find_last_of(".")); + const string data_file_name = base_name + ".ttl"; fstream os(data_file_name.c_str(), ios::out); effect->getProductString(name_buf); @@ -141,7 +146,33 @@ write_plugin(AudioEffectX* effect, const string& lib_file_name) os.close(); - manifest.push_back(Record(effect->getURI(), base_name)); + Manifest::iterator i = manifest.find(effect->getURI()); + if (i != manifest.end()) { + i->second.base_name = base_name; + } else { + manifest.insert(std::make_pair(effect->getURI(), Record(base_name))); + } +} + + +void +write_gui(AEffEditor* gui, const string& lib_file_name) +{ + const string base_name = lib_file_name.substr(0, lib_file_name.find_last_of(".")); + assert(gui_manifest.find(gui->getURI()) == gui_manifest.end()); + gui_manifest.insert(std::make_pair(gui->getURI(), Record(base_name))); + Manifest::iterator plugin_record = manifest.find(lib_file_name); + if (plugin_record != manifest.end()) { + plugin_record->second.uis.push_back(gui->getPluginURI()); + } + Manifest::iterator i = manifest.find(gui->getPluginURI()); + if (i != manifest.end()) { + i->second.uis.push_back(gui->getURI()); + } else { + Record r("ERRNOBASE"); + r.uis.push_back(gui->getURI()); + manifest.insert(std::make_pair(gui->getPluginURI(), r)); + } } @@ -149,11 +180,23 @@ void write_manifest(ostream& os) { os << "@prefix : ." << endl; - os << "@prefix rdfs: ." << endl << endl; + os << "@prefix rdfs: ." << endl; + os << "@prefix uiext: ." << endl << endl; for (Manifest::iterator i = manifest.begin(); i != manifest.end(); ++i) { - os << "<" << i->uri << "> a :Plugin ;" << endl; - os << "\trdfs:seeAlso <" << i->base_name << ".ttl> ;" << endl; - os << "\t:binary <" << i->base_name << ".so> ." << endl << endl; + Record& r = i->second; + os << "<" << i->first << "> a :Plugin ;" << endl; + os << "\trdfs:seeAlso <" << r.base_name << ".ttl> ;" << endl; + os << "\t:binary <" << r.base_name << ".so> "; + for (Record::UIs::iterator j = r.uis.begin(); j != r.uis.end(); ++j) + os << ";" << endl << "\tuiext:ui <" << *j << "> "; + os << "." << endl << endl; + } + + for (GUIManifest::iterator i = gui_manifest.begin(); i != gui_manifest.end(); ++i) { + Record& r = i->second; + os << "<" << i->first << "> a uiext:GtkUI ;" << endl; + os << "\trdfs:seeAlso <" << r.base_name << ".ttl> ;" << endl; + os << "\tuiext:binary <" << r.base_name << ".so> ." << endl << endl; } } @@ -171,33 +214,42 @@ main(int argc, char** argv) } typedef AudioEffectX* (*new_effect_func)(); + typedef AEffEditor* (*new_gui_func)(); typedef AudioEffectX* (*plugin_uri_func)(); - new_effect_func constructor = NULL; - AudioEffectX* effect = NULL; + new_effect_func constructor = NULL; + new_gui_func gui_constructor = NULL; + AudioEffectX* effect = NULL; + AEffEditor* gui = NULL; for (int i = 1; i < argc; ++i) { - void* handle = dlopen(argv[i], RTLD_NOW); + void* handle = dlopen(argv[i], RTLD_LAZY); if (handle == NULL) { - cerr << "ERROR: " << argv[i] << " is not a shared library, ignoring" << endl; + cerr << "ERROR: " << argv[i] << ": " << dlerror() << " (ignoring)" << endl; continue; } - constructor = (new_effect_func)dlsym(handle, "lvz_new_audioeffectx"); - if (constructor == NULL) { - dlclose(handle); - cerr << "ERROR: " << argv[i] << " is not an LVZ plugin library, ignoring" << endl; - continue; - } - - effect = constructor(); string lib_path = argv[i]; size_t last_slash = lib_path.find_last_of("/"); if (last_slash != string::npos) lib_path = lib_path.substr(last_slash + 1); - write_plugin(effect, lib_path); - + constructor = (new_effect_func)dlsym(handle, "lvz_new_audioeffectx"); + if (constructor != NULL) { + effect = constructor(); + write_plugin(effect, lib_path); + } + + gui_constructor = (new_gui_func)dlsym(handle, "lvz_new_aeffeditor"); + if (gui_constructor != NULL) { + gui = gui_constructor(); + write_gui(gui, lib_path); + } + + if (constructor == NULL && gui_constructor == NULL) { + cerr << "ERROR: " << argv[i] << ": not an LVZ plugin library, ignoring" << endl; + } + dlclose(handle); } diff --git a/lvz/gui_wrapper.cpp b/lvz/gui_wrapper.cpp new file mode 100644 index 0000000..ca2b894 --- /dev/null +++ b/lvz/gui_wrapper.cpp @@ -0,0 +1,211 @@ +/* LVZ - A C++ interface for writing LV2 plugins. + * Copyright (C) 2008 Dave Robillard + * + * This library 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 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef UI_CLASS +#error "This file requires UI_CLASS to be defined" +#endif +#ifndef URI_PREFIX +#error "This file requires URI_PREFIX to be defined" +#endif +#ifndef UI_URI_SUFFIX +#error "This file requires UI_URI_SUFFIX to be defined" +#endif + +#include +#include +#include +#include +#include "AEffEditor.hpp" +#include "lv2_ui.h" +#include UI_HEADER +#include PLUGIN_HEADER + +extern "C" { + +/* UI */ + +typedef struct { + bool stolen; + UI_CLASS* ui; + GtkSocket* socket; + Window x_window; + AudioEffectX* effect; +} MDAPluginUI; + + +static gboolean +mda_ui_idle(void* data) +{ + MDAPluginUI* ui = (MDAPluginUI*)data; + if (!ui->stolen) { + //gtk_socket_add_id(GTK_SOCKET(ui->socket), ui->x_window); + ui->x_window = (Window)gtk_socket_get_id(GTK_SOCKET(ui->socket)); + bool success = ui->ui->open((void*)ui->x_window); + if (!success) + fprintf(stderr, "FAILED TO OPEN GUI\n"); + ui->ui->getFrame()->draw(); + ui->stolen = true; + } + + ui->ui->idle(); + return true; +} + + +static LV2UI_Handle +mda_ui_instantiate(const struct _LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features) +{ + /* Some extensions need to be defined for this to work. Hosts must: + * 1) Pass a pointer to the plugin (VST is crap and has no UI separation) + * 2) Call idle on the UI periodically (VST is crap and uses polling) + * + * Thoughts: + * + * 1) This wrapper could be written explicitly in GTK and just get at + * the idle handler that way. Less burden on the host... + * + * 2) A better idea is to have a 'get plugin extension data' feature + * the UI (or anything else) can use to ask for any URI-identified + * piece of extension data (failing in this case if plugin is remote) + */ + + MDAPluginUI* ui = (MDAPluginUI*)malloc(sizeof(MDAPluginUI)); + ui->effect = NULL; + + typedef struct { const void* (*extension_data)(const char* uri); } LV2_ExtensionData; + + typedef const void* (*extension_data_func)(const char* uri); + typedef const AudioEffectX* (*get_effect_func)(LV2_Handle instance); + + LV2_Handle instance = NULL; + get_effect_func get_effect = NULL; + + + if (features != NULL) { + const LV2_Feature* feature = features[0]; + for (size_t i = 0; (feature = features[i]) != NULL; ++i) { + if (!strcmp(feature->URI, "http://lv2plug.in/ns/ext/dev/plugin-instance")) { + instance = (LV2_Handle)feature->data; + } else if (!strcmp(feature->URI, "http://lv2plug.in/ns/ext/dev/extension-data")) { + LV2_ExtensionData* ext_data = (LV2_ExtensionData*)feature->data; + extension_data_func func = (extension_data_func)feature->data; + get_effect = (get_effect_func)ext_data->extension_data( + "http://lv2plug.in/ns/ext/dev/vstgui"); + } + } + } + + if (instance && get_effect) { + ui->effect = (AudioEffectX*)get_effect(instance); + } else { + fprintf(stderr, "Host does not support required features, aborting.\n"); + return NULL; + } + + ui->ui = new UI_CLASS(ui->effect); + ui->ui->setBundlePath(bundle_path); + ui->stolen = false; + + ui->socket = GTK_SOCKET(gtk_socket_new()); + gtk_widget_show_all(GTK_WIDGET(ui->socket)); + + *widget = ui->socket; + g_timeout_add(30, mda_ui_idle, ui); + + return ui; +} + + +static void +mda_ui_cleanup(LV2UI_Handle instance) +{ + g_idle_remove_by_data(instance); +} + + +static void +mda_ui_port_event(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer) +{ + // VST UIs seem to not use this at all, it's all polling + // The shit the proprietary people come up (and get away) with... +} + + +static const void* +mda_ui_extension_data(const char* uri) +{ + return NULL; +} + + +/* Library */ + +static LV2UI_Descriptor *mda_ui_descriptor = NULL; + +static void +init_ui_descriptor() +{ + mda_ui_descriptor = (LV2UI_Descriptor*)malloc(sizeof(LV2UI_Descriptor)); + + mda_ui_descriptor->URI = URI_PREFIX UI_URI_SUFFIX; + mda_ui_descriptor->instantiate = mda_ui_instantiate; + mda_ui_descriptor->cleanup = mda_ui_cleanup; + mda_ui_descriptor->port_event = mda_ui_port_event; + mda_ui_descriptor->extension_data = mda_ui_extension_data; +} + + +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor* +lv2ui_descriptor(uint32_t index) +{ + if (!mda_ui_descriptor) + init_ui_descriptor(); + + switch (index) { + case 0: + return mda_ui_descriptor; + default: + return NULL; + } +} + + +LV2_SYMBOL_EXPORT +AEffEditor* +lvz_new_aeffeditor(AudioEffect* effect) +{ + UI_CLASS* ui = new UI_CLASS(effect); + ui->setURI(URI_PREFIX UI_URI_SUFFIX); + ui->setPluginURI(URI_PREFIX PLUGIN_URI_SUFFIX); + return ui; +} + + +} // extern "C" + diff --git a/lvz/wrapper.cpp b/lvz/wrapper.cpp index 4ab99df..82a6989 100644 --- a/lvz/wrapper.cpp +++ b/lvz/wrapper.cpp @@ -19,8 +19,8 @@ #ifndef PLUGIN_CLASS #error "This file requires PLUGIN_CLASS to be defined" #endif -#ifndef PLUGIN_URI_PREFIX -#error "This file requires PLUGIN_URI_PREFIX to be defined" +#ifndef URI_PREFIX +#error "This file requires URI_PREFIX to be defined" #endif #ifndef PLUGIN_URI_SUFFIX #error "This file requires PLUGIN_URI_SUFFIX to be defined" @@ -74,6 +74,7 @@ mda_connect_port(LV2_Handle instance, uint32_t port, void* data) static int master_callback(int, int ver, int, int, int, int) { + return 0; } @@ -84,7 +85,7 @@ mda_instantiate(const LV2_Descriptor* descriptor, const LV2_Feature*const* features) { PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback); - effect->setURI(PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX); + effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX); effect->setSampleRate(rate); uint32_t num_params = effect->getNumParameters(); @@ -97,7 +98,7 @@ mda_instantiate(const LV2_Descriptor* descriptor, if (num_params > 0) { plugin->controls = (float*)malloc(sizeof(float) * num_params); plugin->control_buffers = (float**)malloc(sizeof(float*) * num_params); - for (int32_t i = 0; i < num_params; ++i) { + for (uint32_t i = 0; i < num_params; ++i) { plugin->controls[i] = effect->getParameter(i); plugin->control_buffers[i] = NULL; } @@ -108,7 +109,7 @@ mda_instantiate(const LV2_Descriptor* descriptor, if (num_inputs > 0) { plugin->inputs = (float**)malloc(sizeof(float*) * num_inputs); - for (int32_t i = 0; i < num_inputs; ++i) + for (uint32_t i = 0; i < num_inputs; ++i) plugin->inputs[i] = NULL; } else { plugin->inputs = NULL; @@ -116,7 +117,7 @@ mda_instantiate(const LV2_Descriptor* descriptor, if (num_outputs > 0) { plugin->outputs = (float**)malloc(sizeof(float*) * num_outputs); - for (int32_t i = 0; i < num_outputs; ++i) + for (uint32_t i = 0; i < num_outputs; ++i) plugin->outputs[i] = NULL; } else { plugin->outputs = NULL; @@ -141,6 +142,26 @@ mda_run(LV2_Handle instance, uint32_t sample_count) plugin->effect->processReplacing(plugin->inputs, plugin->outputs, sample_count); } + + +static const AudioEffectX* +mda_get_audioeffectx(LV2_Handle instance) +{ + MDAPlugin* plugin = (MDAPlugin*)instance; + return plugin->effect; +} + + +static const void* +mda_extension_data(const char* uri) +{ + if (!strcmp(uri, "http://lv2plug.in/ns/ext/dev/vstgui")) { + // FIXME: shouldn't return function pointers directly + return (const void*)mda_get_audioeffectx; + } else { + return NULL; + } +} static void @@ -160,13 +181,14 @@ init_descriptor() { mda_descriptor = (LV2_Descriptor*)malloc(sizeof(LV2_Descriptor)); - mda_descriptor->URI = PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX; - mda_descriptor->activate = NULL; - mda_descriptor->cleanup = mda_cleanup; - mda_descriptor->connect_port = mda_connect_port; - mda_descriptor->deactivate = mda_deactivate; + mda_descriptor->URI = URI_PREFIX PLUGIN_URI_SUFFIX; mda_descriptor->instantiate = mda_instantiate; + mda_descriptor->connect_port = mda_connect_port; + mda_descriptor->activate = NULL; mda_descriptor->run = mda_run; + mda_descriptor->deactivate = mda_deactivate; + mda_descriptor->cleanup = mda_cleanup; + mda_descriptor->extension_data = mda_extension_data; } @@ -191,9 +213,10 @@ AudioEffectX* lvz_new_audioeffectx() { PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback); - effect->setURI(PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX); + effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX); return effect; } } // extern "C" + diff --git a/src/mdaSpecMeter.cpp b/src/mdaSpecMeter.cpp new file mode 100644 index 0000000..7aae786 --- /dev/null +++ b/src/mdaSpecMeter.cpp @@ -0,0 +1,482 @@ +// +// Plug-in: "MDA SpecMeter" +// +// Copyright(c)2002 Paul Kellett (maxim digital audio) +// + + +#include +#include +#include +#include + +#include "mdaSpecMeter.h" +//#include "mdaSpecMeterGUI.h" +//#include "AEffEditor.hpp" + +AudioEffect * +createEffectInstance (audioMasterCallback audioMaster) +{ + return new mdaSpecMeter (audioMaster); +} + +mdaSpecMeterProgram::mdaSpecMeterProgram () +{ + param[_PARAM0] = 1.0; + strcpy (name, "MDA SpecMeter"); +} + + +mdaSpecMeter::mdaSpecMeter (audioMasterCallback audioMaster):AudioEffectX (audioMaster, 1, + NPARAMS) +{ + //editor = new mdaSpecMeterGUI(this); + + programs = new mdaSpecMeterProgram[numPrograms]; + if (programs) { + setProgram (0); + } + + setNumInputs (2); + setNumOutputs (2); + DECLARE_LVZ_DEPRECATED (canMono) (); + setUniqueID ("mdaSpecMeter"); + canProcessReplacing (); + + //initialise... + K = counter = 0; + kmax = 2048; + topband = 11; + iK = 1.0f / (float) kmax; + den = 1.0e-8f; + + //buffer = new float[44100]; + + suspend (); +} + +bool +mdaSpecMeter::getProductString (char *text) +{ + strcpy (text, "MDA SpecMeter"); + return true; +} + +bool +mdaSpecMeter::getVendorString (char *text) +{ + strcpy (text, "mda"); + return true; +} + +bool +mdaSpecMeter::getEffectName (char *name) +{ + strcpy (name, "SpecMeter"); + return true; +} + +void +mdaSpecMeter::suspend () +{ + Lpeak = Rpeak = Lrms = Rrms = Corr = 0.0f; + lpeak = rpeak = lrms = rrms = corr = 0.0f; + Lhold = Rhold = 0.0f; + Lmin = Rmin = 0.0000001f; + for (long i = 0; i < 16; i++) { + band[0][i] = band[1][i] = 0.0f; + for (long j = 0; j < 6; j++) + lpp[j][i] = rpp[j][i] = 0.0f; + } + + //memset(buffer, 0, size * sizeof (float)); +} + +void +mdaSpecMeter::setSampleRate (float rate) +{ + AudioEffectX::setSampleRate (rate); + if (rate > 64000) { + topband = 12; + kmax = 4096; + } else { + topband = 11; + kmax = 2048; + } + iK = 1.0f / (float) kmax; +} + + +mdaSpecMeter::~mdaSpecMeter () +{ + //if(buffer) delete [] buffer; + if (programs) + delete[]programs; +} + + +void +mdaSpecMeter::setProgramName (char *name) +{ + strcpy (programs[curProgram].name, name); +} + +void +mdaSpecMeter::getProgramName (char *name) +{ + strcpy (name, programs[curProgram].name); +} + +float +mdaSpecMeter::getParameter (LvzInt32 index) +{ + return param[index]; +} + +void +mdaSpecMeter::setProgram (LvzInt32 program) +{ + mdaSpecMeterProgram *p = &programs[program]; + curProgram = program; + setProgramName (p->name); + for (long i = 0; i < NPARAMS; i++) + setParameter (i, p->param[i]); +} + +////////////////////////////////////////////////////////////////////////////////// + +void +mdaSpecMeter::setParameter (LvzInt32 index, float value) +{ + mdaSpecMeterProgram *p = &programs[curProgram]; + param[index] = p->param[index] = value; + + switch (index) { + case _PARAM0: + gain = (float) pow (10.0f, 2.0f * param[index] - 1.0f); + break; + + default: + break; + } + + //if(editor) editor->postUpdate(); +} + + +void +mdaSpecMeter::getParameterName (LvzInt32 index, char *label) +{ + switch (index) { + case _PARAM0: + strcpy (label, "Gain"); + break; + default: + strcpy (label, ""); + } +} + + +void +mdaSpecMeter::getParameterDisplay (LvzInt32 index, char *text) +{ + char string[16]; + + switch (index) { + case _PARAM0: + sprintf (string, "%.1f", 40.0f * param[index] - 20.0f); + break; + default: + sprintf (string, "%.0f", 0.0f * param[index]); + } + string[8] = 0; + strcpy (text, (char *) string); +} + + +void +mdaSpecMeter::getParameterLabel (LvzInt32 index, char *label) +{ + switch (index) { + case _PARAM0: + strcpy (label, "Gain"); + break; + default: + strcpy (label, ""); + } +} + +////////////////////////////////////////////////////////////////////////////////// + +void +mdaSpecMeter::process (float **inputs, float **outputs, LvzInt32 sampleFrames) +{ + float *in1 = inputs[0]; + float *in2 = inputs[1]; + float *out1 = outputs[0]; + float *out2 = outputs[1]; + + den = -den; + float l, r, p, q, iN = iK; + long k = K, j0 = topband, mask, j; + + while (--sampleFrames >= 0) { + l = *in1++; + r = *in2++; + *out1++ += l; + *out2++ += r; + + l += den; //anti-denormal + r += den; + + lrms += l * l; //RMS integrate + rrms += r * r; + + p = (float) fabs (l); + if (p > lpeak) + lpeak = p; //peak detect + q = (float) fabs (r); + if (q > rpeak) + rpeak = q; + /* + if(p > 1.0e-8f && p < lmin) lmin = p; //'trough' detect + if(q > 1.0e-8f && q < rmin) rmin = q; + */ + if ((l * r) > 0.0f) + corr += iN; //measure correlation + + j = j0; + mask = k << 1; + + do { //polyphase filter bank + p = lpp[0][j] + 0.208f * l; + lpp[0][j] = lpp[1][j]; + lpp[1][j] = l - 0.208f * p; + + q = lpp[2][j] + lpp[4][j] * 0.682f; + lpp[2][j] = lpp[3][j]; + lpp[3][j] = lpp[4][j] - 0.682f * q; + lpp[4][j] = l; + lpp[5][j] += (float) fabs (p - q); //top octave + l = p + q; //lower octaves + + p = rpp[0][j] + 0.208f * r; + rpp[0][j] = rpp[1][j]; + rpp[1][j] = r - 0.208f * p; + + q = rpp[2][j] + rpp[4][j] * 0.682f; + rpp[2][j] = rpp[3][j]; + rpp[3][j] = rpp[4][j] - 0.682f * q; + rpp[4][j] = r; + rpp[5][j] += (float) fabs (p - q); //top octave + r = p + q; //lower octaves + + j--; + mask >>= 1; + } while (mask & 1); + + if (++k == kmax) { + k = 0; + counter++; //editor waits for this to change + + if (lpeak == 0.0f) + Lpeak = Lrms = 0.0f; + else { ///add limits here! + if (lpeak > 2.0f) + lpeak = 2.0f; + if (lpeak >= Lpeak) { + Lpeak = lpeak; + Lhold = 2.0f * Lpeak; + } else { + Lhold *= 0.95f; + if (Lhold < Lpeak) + Lpeak = Lhold; + } + Lmin = lmin; + lmin *= 1.01f; + Lrms += 0.2f * (iN * lrms - Lrms); + } + + if (rpeak == 0.0f) + Rpeak = Rrms = 0.0f; + else { + if (rpeak > 2.0f) + rpeak = 2.0f; + if (rpeak >= Rpeak) { + Rpeak = rpeak; + Rhold = 2.0f * Rpeak; + } else { + Rhold *= 0.95f; + if (Rhold < Rpeak) + Rpeak = Rhold; + } + Rmin = rmin; + rmin *= 1.01f; + Rrms += 0.2f * (iN * rrms - Rrms); + } + + rpeak = lpeak = lrms = rrms = 0.0f; + Corr += 0.1f * (corr - Corr); //correlation + corr = SILENCE; + + float dec = 0.08f; + for (j = 0; j < 13; j++) { //spectrum output + band[0][j] += dec * (iN * lpp[5][j] - band[0][j]); + if (band[0][j] > 2.0f) + band[0][j] = 2.0f; + else if (band[0][j] < 0.014f) + band[0][j] = 0.014f; + + band[1][j] += dec * (iN * rpp[5][j] - band[1][j]); + if (band[1][j] > 2.0f) + band[1][j] = 2.0f; + else if (band[1][j] < 0.014f) + band[1][j] = 0.014f; + + rpp[5][j] = lpp[5][j] = SILENCE; + dec = dec * 1.1f; + } + } + } + + K = k; +} + +////////////////////////////////////////////////////////////////////////////////// + +void +mdaSpecMeter::processReplacing (float **inputs, float **outputs, + LvzInt32 sampleFrames) +{ + float *in1 = inputs[0]; + float *in2 = inputs[1]; + float *out1 = outputs[0]; + float *out2 = outputs[1]; + + den = -den; + float l, r, p, q, iN = iK; + long k = K, j0 = topband, mask, j; + + while (--sampleFrames >= 0) { + l = *in1++; + r = *in2++; + *out1++ = l; + *out2++ = r; + + l += den; //anti-denormal + r += den; + + lrms += l * l; //RMS integrate + rrms += r * r; + + p = (float) fabs (l); + if (p > lpeak) + lpeak = p; //peak detect + q = (float) fabs (r); + if (q > rpeak) + rpeak = q; + /* + if(p > 1.0e-8f && p < lmin) lmin = p; //'trough' detect + if(q > 1.0e-8f && q < rmin) rmin = q; + */ + if ((l * r) > 0.0f) + corr += iN; //measure correlation + + j = j0; + mask = k << 1; + + do { //polyphase filter bank + p = lpp[0][j] + 0.208f * l; + lpp[0][j] = lpp[1][j]; + lpp[1][j] = l - 0.208f * p; + + q = lpp[2][j] + lpp[4][j] * 0.682f; + lpp[2][j] = lpp[3][j]; + lpp[3][j] = lpp[4][j] - 0.682f * q; + lpp[4][j] = l; + lpp[5][j] += (float) fabs (p - q); //top octave + l = p + q; //lower octaves + + p = rpp[0][j] + 0.208f * r; + rpp[0][j] = rpp[1][j]; + rpp[1][j] = r - 0.208f * p; + + q = rpp[2][j] + rpp[4][j] * 0.682f; + rpp[2][j] = rpp[3][j]; + rpp[3][j] = rpp[4][j] - 0.682f * q; + rpp[4][j] = r; + rpp[5][j] += (float) fabs (p - q); //top octave + r = p + q; //lower octaves + + j--; + mask >>= 1; + } while (mask & 1); + + if (++k == kmax) { + k = 0; + //counter++; //editor waits for this to change + + if (lpeak == 0.0f) + Lpeak = Lrms = 0.0f; + else { ///add limits here! + if (lpeak > 2.0f) + lpeak = 2.0f; + if (lpeak >= Lpeak) { + Lpeak = lpeak; + Lhold = 2.0f * Lpeak; + } else { + Lhold *= 0.95f; + if (Lhold < Lpeak) + Lpeak = Lhold; + } + Lmin = lmin; + lmin *= 1.01f; + Lrms += 0.2f * (iN * lrms - Lrms); + } + + if (rpeak == 0.0f) + Rpeak = Rrms = 0.0f; + else { + if (rpeak > 2.0f) + rpeak = 2.0f; + if (rpeak >= Rpeak) { + Rpeak = rpeak; + Rhold = 2.0f * Rpeak; + } else { + Rhold *= 0.95f; + if (Rhold < Rpeak) + Rpeak = Rhold; + } + Rmin = rmin; + rmin *= 1.01f; + Rrms += 0.2f * (iN * rrms - Rrms); + } + + rpeak = lpeak = lrms = rrms = 0.0f; + Corr += 0.1f * (corr - Corr); //correlation + corr = SILENCE; + + float dec = 0.08f; + for (j = 0; j < 13; j++) { //spectrum output + band[0][j] += dec * (iN * lpp[5][j] - band[0][j]); + if (band[0][j] > 2.0f) + band[0][j] = 2.0f; + else if (band[0][j] < 0.014f) + band[0][j] = 0.014f; + + band[1][j] += dec * (iN * rpp[5][j] - band[1][j]); + if (band[1][j] > 2.0f) + band[1][j] = 2.0f; + else if (band[1][j] < 0.014f) + band[1][j] = 0.014f; + + rpp[5][j] = lpp[5][j] = SILENCE; + dec = dec * 1.1f; + } + + counter++; //editor waits for this to change + } + } + + K = k; +} diff --git a/src/mdaSpecMeter.h b/src/mdaSpecMeter.h index 31573ed..5ce88ee 100644 --- a/src/mdaSpecMeter.h +++ b/src/mdaSpecMeter.h @@ -1,21 +1,18 @@ -//see associated .cpp file for copyright and other info +// +// Plug-in: "MDA SpecMeter" +// +// Copyright(c)1999-2000 Paul Kellett (maxim digital audio) +// Copyright (C) 2008 Dave Robillard +// #include "audioeffectx.h" #include -#define NPROGS 4 //can hide decay settings in programs! fast...slow...peak hold #define SILENCE 0.00000001f - - -enum -{ - _PARAM0, //peak decay - _PARAM1, //RMS speed - _PARAM2, //spectrum speed - _PARAM3, //peak reset? - +enum { + _PARAM0, // gain NPARAMS }; diff --git a/src/mdaSpecMeterGUI.cpp b/src/mdaSpecMeterGUI.cpp index 08bc5a3..d36622d 100644 --- a/src/mdaSpecMeterGUI.cpp +++ b/src/mdaSpecMeterGUI.cpp @@ -1,179 +1,211 @@ -#include "mdaSpecMeterGUI.h" -#include "mdaSpecMeter.h" - -#include - - -mdaSpecMeterGUI::mdaSpecMeterGUI(AudioEffect *effect) : AEffGUIEditor(effect) -{ - background = new CBitmap(128); - - rect.left = 0; - rect.top = 0; - rect.right = (LvzInt16)background->getWidth(); - rect.bottom = (LvzInt16)background->getHeight(); -} - - -mdaSpecMeterGUI::~mdaSpecMeterGUI() -{ - delete background; -} - - -bool mdaSpecMeterGUI::open(void *ptr) -{ - AEffGUIEditor::open(ptr); - - CPoint offs(0, 0); - CRect size(0, 0, background->getWidth(), background->getHeight()); - frame = new CFrame(size, ptr, this); - - size.offset(0, 0); - draw = new CDraw(size, 0.0f, background); - frame->addView(draw); - - return true; -} - - -void mdaSpecMeterGUI::close() -{ - delete frame; - frame = 0; -} - - -void mdaSpecMeterGUI::idle() -{ - long xnow = ((mdaSpecMeter *)effect)->counter; - if(xnow != xtimer) - { - xtimer = xnow; - - //if(draw) temp = draw->temp; - //if(label) label->setLabel(xtimer); - - if(draw) //copy data from effect (this can't be the best way!) - { - draw->Lpeak = ((mdaSpecMeter *)effect)->Lpeak; - draw->Lmin = ((mdaSpecMeter *)effect)->Lmin; - draw->Lrms = ((mdaSpecMeter *)effect)->Lrms; - draw->Rpeak = ((mdaSpecMeter *)effect)->Rpeak; - draw->Rmin = ((mdaSpecMeter *)effect)->Rmin; - draw->Rrms = ((mdaSpecMeter *)effect)->Rrms; - draw->Corr = ((mdaSpecMeter *)effect)->Corr; - for(long i=0; i<13; i++) - { - draw->band[0][i] = ((mdaSpecMeter *)effect)->band[0][i]; - draw->band[1][i] = ((mdaSpecMeter *)effect)->band[1][i]; - } - draw->setDirty(true); //trigger redraw - } - } - - AEffGUIEditor::idle(); -} - - -CDraw::CDraw(CRect &size, float value, CBitmap *background) : CControl(size) -{ - bitmap = background; - - Lpeak = Lmin = Lrms = Rpeak = Rmin = Rrms = Corr = 0.0f; - for(long i=0; i<16; i++) band[0][i] = band[1][i] = 0.0f; - - setValue(value); -} - -CDraw::~CDraw() -{ - -} - -void CDraw::draw(CDrawContext *pContext) -{ - long r, p; - CRect block; - CRect rect(0, 0, bitmap->getWidth(), bitmap->getHeight()); - - bitmap->draw(pContext, rect); -/* - pContext->setFillColor(kGreenCColor); - - p = x2pix(Lmin); - block(p - 3, 10, p - 1, 18); - pContext->fillRect(block); - - p = x2pix(Rmin); - block(p - 3, 18, p - 1, 26); - pContext->fillRect(block); -*/ - pContext->setFillColor(kBlackCColor); - - r = x22pix(Lrms); if(r > 454) r = 454; - p = x2pix(Lpeak); if(p > 454) p = 454; - block = CRect(r - 1, 10, p - 1, 18); - pContext->fillRect(block); - block = CRect(p - 1, 10, 478, 18); - if(p < 454) pContext->fillRect(block); - - r = x22pix(Rrms); if(r > 454) r = 454; - p = x2pix(Rpeak); if(p > 454) p = 454; - block = CRect(r - 1, 18, p - 1, 26); - pContext->fillRect(block); - block = CRect(p - 1, 18, 478, 26); - if(p < 454) pContext->fillRect(block); - - //block(x2pix(Rpeak), 18, 478, 26); - //buf->fillRect(block); - - block = CRect(235, 42, 244, 134 - (long)(90 * Corr)); - pContext->fillRect(block); - - long i, x1=2, x2=256; //octave bands - float dB; - for(i=0; i<13; i++) - { - dB = band[0][i]; - block = CRect(x1, 42, x1+18, 49 - (long)(20.0 * log(dB))); - pContext->fillRect(block); - x1 += 17; - - dB = band[1][i]; - block = CRect(x2, 42, x2+18, 49 - (long)(20.0 * log(dB))); - pContext->fillRect(block); - x2 += 17; - } -} - - -long CDraw::x2pix(float x) -{ - float dB = x; - long p = 478; - - if(x > 0.00000005f) dB = 8.6858896f * (float)log(x); else dB = -146.0f; - if(dB < -20.0) - p = 293 + (long)(2.0f * dB); - else - p = 453 + (long)(10.0f * dB); - - return p; -} - - -long CDraw::x22pix(float x) //power version for squared summed -{ - float dB = x; - long p = 478; - - if(x > 0.00000005f) dB = 4.3429448f * (float)log(x); else dB = -146.0f; - if(dB < -20.0) - p = 293 + (long)(2.0f * dB); - else - if(dB < 0.0f) p = 453 + (long)(10.0f * dB); - - return p; -} - +#include "mdaSpecMeterGUI.h" +#include "mdaSpecMeter.h" +#include "mdaSpecMeter.xpm" +#include +#include +#include +#include +#include + +#include + +mdaSpecMeterGUI::mdaSpecMeterGUI(AudioEffect * effect) + : AEffGUIEditor(effect) + , background(NULL) +{ +} + + +mdaSpecMeterGUI::~mdaSpecMeterGUI() +{ + delete background; +} + + +bool +mdaSpecMeterGUI::open(void *ptr) +{ + if (background == NULL) { + background = new CBitmap(*this, "mdaSpecMeter.png"); + rect.right = (LvzInt16) background->getWidth(); + rect.bottom = (LvzInt16) background->getHeight(); + } + + AEffGUIEditor::open(ptr); + + CPoint offs(0, 0); + CRect size(0, 0, background->getWidth(), background->getHeight()); + frame = new CFrame(size, ptr, this); + + size.offset(0, 0); + draw = new CDraw(size, 0.0f, background); + frame->addView(draw); + + return true; +} + + +void +mdaSpecMeterGUI::close() +{ + delete frame; + frame = 0; +} + + +void +mdaSpecMeterGUI::idle() +{ + long xnow = ((mdaSpecMeter *) effect)->counter; + if (xnow != xtimer) { + xtimer = xnow; + + if (draw) { // copy data from effect (this can't be the best way!) + draw->Lpeak = ((mdaSpecMeter *) effect)->Lpeak; + draw->Lmin = ((mdaSpecMeter *) effect)->Lmin; + draw->Lrms = ((mdaSpecMeter *) effect)->Lrms; + draw->Rpeak = ((mdaSpecMeter *) effect)->Rpeak; + draw->Rmin = ((mdaSpecMeter *) effect)->Rmin; + draw->Rrms = ((mdaSpecMeter *) effect)->Rrms; + draw->Corr = ((mdaSpecMeter *) effect)->Corr; + for (long i = 0; i < 13; i++) { + draw->band[0][i] = ((mdaSpecMeter *) effect)->band[0][i]; + draw->band[1][i] = ((mdaSpecMeter *) effect)->band[1][i]; + } + draw->setDirty(true); // trigger redraw + } + } + + AEffGUIEditor::idle(); +} + + +CDraw::CDraw(CRect & size, float value, CBitmap * background): + CControl(size) +{ + bitmap = background; + + Lpeak = Lmin = Lrms = Rpeak = Rmin = Rrms = Corr = 0.0f; + for (long i = 0; i < 16; i++) + band[0][i] = band[1][i] = 0.0f; + + setValue(value); +} + +CDraw::~CDraw() +{ +} + +void +CDraw::draw(CDrawContext * pContext) +{ + long r, p; + CRect block; + CRect rect(0, 0, bitmap->getWidth(), bitmap->getHeight()); + + bitmap->draw(pContext, rect); + + /* + pContext->setFillColor(kGreenCColor); + + p = x2pix(Lmin); + block(p - 3, 10, p - 1, 18); + pContext->fillRect(block); + + p = x2pix(Rmin); + block(p - 3, 18, p - 1, 26); + pContext->fillRect(block); + */ + + pContext->setFillColor(kBlackCColor); + + r = x22pix(Lrms); + if (r > 454) + r = 454; + + p = x2pix(Lpeak); + if (p > 454) + p = 454; + + block = CRect(r - 1, 10, p - 1, 18); + pContext->fillRect(block); + + block = CRect(p - 1, 10, 478, 18); + if (p < 454) + pContext->fillRect(block); + + r = x22pix(Rrms); + if (r > 454) + r = 454; + + p = x2pix(Rpeak); + if (p > 454) + p = 454; + + block = CRect(r - 1, 18, p - 1, 26); + pContext->fillRect(block); + + block = CRect(p - 1, 18, 478, 26); + if (p < 454) + pContext->fillRect(block); + + //block(x2pix(Rpeak), 18, 478, 26); + //buf->fillRect(block); + + block = CRect(235, 42, 244, 134 - (long)(90 * Corr)); + pContext->fillRect(block); + + long i, x1 = 2, x2 = 256; // octave bands + for (i = 0; i < 13; i++) { + float dB = band[0][i]; + block = CRect(x1, 42, x1 + 18, 49 - (long)(20.0 * log(dB))); + pContext->fillRect(block); + x1 += 17; + + dB = band[1][i]; + block = CRect(x2, 42, x2 + 18, 49 - (long)(20.0 * log(dB))); + pContext->fillRect(block); + x2 += 17; + } +} + + +long +CDraw::x2pix(float x) +{ + float dB = x; + long p = 478; + + if (x > 0.00000005f) + dB = 8.6858896f * (float)log(x); + else + dB = -146.0f; + + if (dB < -20.0) + p = 293 + (long)(2.0f * dB); + else + p = 453 + (long)(10.0f * dB); + + return p; +} + + +long +CDraw::x22pix(float x) // power version for squared summed +{ + float dB = x; + long p = 478; + + if (x > 0.00000005f) + dB = 4.3429448f * (float)log(x); + else + dB = -146.0f; + + if (dB < -20.0) + p = 293 + (long)(2.0f * dB); + else if (dB < 0.0f) + p = 453 + (long)(10.0f * dB); + + return p; +} + diff --git a/src/mdaSpecMeterGUI.h b/src/mdaSpecMeterGUI.h index b4541f1..263a20b 100644 --- a/src/mdaSpecMeterGUI.h +++ b/src/mdaSpecMeterGUI.h @@ -1,45 +1,43 @@ #ifndef _mdaSpecMeterGUI_h_ #define _mdaSpecMeterGUI_h_ -#include "lvzgui.h" +#include "vstgui.h" -//a drawing control class CDraw : public CControl { public: - CDraw(CRect &size, float x, CBitmap *background); - ~CDraw(); + CDraw(CRect& size, float x, CBitmap* background); + ~CDraw(); - void draw(CDrawContext *pContext); + void draw(CDrawContext* pContext); long x2pix(float x); long x22pix(float x); - float Lpeak, Lrms, Lmin, Rpeak, Rrms, Rmin, Corr; - float band[2][16]; - long temp; + float Lpeak, Lrms, Lmin, Rpeak, Rrms, Rmin, Corr; + float band[2][16]; protected: - CBitmap *bitmap; + CBitmap* bitmap; }; class mdaSpecMeterGUI : public AEffGUIEditor { public: - mdaSpecMeterGUI(AudioEffect *effect); - ~mdaSpecMeterGUI(); - - bool open(void *ptr); - void idle(); - void close(); + mdaSpecMeterGUI(AudioEffect* effect); + ~mdaSpecMeterGUI(); + + bool open(void* ptr); + void idle(); + void close(); private: - CDraw *draw; - CBitmap *background; - long xtimer; + CDraw* draw; + CBitmap* background; + long xtimer; }; -#endif //_mdaSpecMeterGUI_h_ +#endif // _mdaSpecMeterGUI_h_ diff --git a/src/mdaspecmeter.cpp b/src/mdaspecmeter.cpp deleted file mode 100644 index 475a7c5..0000000 --- a/src/mdaspecmeter.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// -// Plug-in: "MDA Template" v1.0 -// -// Copyright(c)2002 Paul Kellett (maxim digital audio) -// - - -#include -#include -#include -#include - -#include "mdaSpecMeter.h" -#include "mdaSpecMeterGUI.h" -//#include "AEffEditor.hpp" - -AudioEffect *createEffectInstance(audioMasterCallback audioMaster) -{ - return new mdaSpecMeter(audioMaster); -} - -mdaSpecMeterProgram::mdaSpecMeterProgram() -{ - param[_PARAM0] = 0.5; - param[_PARAM1] = 0.5; - param[_PARAM2] = 0.75; - strcpy (name, "default"); -} - - -mdaSpecMeter::mdaSpecMeter(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, 1, NPARAMS) -{ - editor = new mdaSpecMeterGUI(this); - - programs = new mdaSpecMeterProgram[numPrograms]; - if(programs) - { - setProgram(0); - } - - setNumInputs(2); - setNumOutputs(2); - DECLARE_LVZ_DEPRECATED(canMono) (); - setUniqueID("mdaSpecMeter"); - canProcessReplacing(); - - //initialise... - K = counter = 0; - kmax = 2048; - topband = 11; - iK = 1.0f / (float)kmax; - den = 1.0e-8f; - - //buffer = new float[44100]; - - suspend(); -} - -bool mdaSpecMeter::getProductString(char* text) { strcpy(text, "MDA SpecMeter"); return true; } -bool mdaSpecMeter::getVendorString(char* text) { strcpy(text, "mda"); return true; } -bool mdaSpecMeter::getEffectName(char* name) { strcpy(name, "SpecMeter"); return true; } - -void mdaSpecMeter::suspend() -{ - Lpeak = Rpeak = Lrms = Rrms = Corr = 0.0f; - lpeak = rpeak = lrms = rrms = corr = 0.0f; - Lhold = Rhold = 0.0f; - Lmin = Rmin = 0.0000001f; - for(long i=0; i<16; i++) - { - band[0][i] = band[1][i] = 0.0f; - for(long j=0; j<6; j++) lpp[j][i] = rpp[j][i] = 0.0f; - } - - //memset(buffer, 0, size * sizeof (float)); -} - -void mdaSpecMeter::setSampleRate(float sampleRate) -{ - AudioEffectX::setSampleRate(sampleRate); - if(sampleRate > 64000) { topband = 12; kmax = 4096; } - else { topband = 11; kmax = 2048; } - iK = 1.0f / (float)kmax; -} - - -mdaSpecMeter::~mdaSpecMeter() -{ - //if(buffer) delete [] buffer; - if(programs) delete [] programs; -} - - -void mdaSpecMeter::setProgramName(char *name) { strcpy(programs[curProgram].name, name); } -void mdaSpecMeter::getProgramName(char *name) { strcpy(name, programs[curProgram].name); } -float mdaSpecMeter::getParameter(LvzInt32 index) { return param[index]; } - -void mdaSpecMeter::setProgram(LvzInt32 program) -{ - mdaSpecMeterProgram *p = &programs[program]; - curProgram = program; - setProgramName(p->name); - for(long i=0; iparam[i]); -} - -////////////////////////////////////////////////////////////////////////////////// - -void mdaSpecMeter::setParameter(LvzInt32 index, float value) -{ - mdaSpecMeterProgram *p = &programs[curProgram]; - param[index] = p->param[index] = value; - - switch(index) - { - case _PARAM0: gain = (float)pow(10.0f, 2.0f * param[index] - 1.0f); break; - - default: break; - } - - //if(editor) editor->postUpdate(); -} - - -void mdaSpecMeter::getParameterName(LvzInt32 index, char *label) -{ - switch (index) - { - case _PARAM0: strcpy (label, ""); break; - default : strcpy (label, ""); - } -} - - -void mdaSpecMeter::getParameterDisplay(LvzInt32 index, char *text) -{ - char string[16]; - - switch (index) - { - case _PARAM0: sprintf(string, "%.1f", 40.0f * param[index] - 20.0f); break; - case _PARAM1: strcpy (string, ""); break; - default : sprintf(string, "%.0f", 100.0f * param[index]); - } - string[8] = 0; - strcpy(text, (char *)string); -} - - -void mdaSpecMeter::getParameterLabel(LvzInt32 index, char *label) -{ - switch (index) - { - case _PARAM0: strcpy(label, ""); break; - default : strcpy(label, ""); - } -} - -////////////////////////////////////////////////////////////////////////////////// - -void mdaSpecMeter::process (float **inputs, float **outputs, LvzInt32 sampleFrames) -{ - float *in1 = inputs[0]; - float *in2 = inputs[1]; - float *out1 = outputs[0]; - float *out2 = outputs[1]; - - den = -den; - float l, r, p, q, iN = iK; - long k=K, j0=topband, mask, j; - - while(--sampleFrames >= 0) - { - l = *in1++; - r = *in2++; - *out1++ += l; - *out2++ += r; - - l += den; //anti-denormal - r += den; - - lrms += l * l; //RMS integrate - rrms += r * r; - - p = (float)fabs(l); if(p > lpeak) lpeak = p; //peak detect - q = (float)fabs(r); if(q > rpeak) rpeak = q; -/* - if(p > 1.0e-8f && p < lmin) lmin = p; //'trough' detect - if(q > 1.0e-8f && q < rmin) rmin = q; -*/ - if((l * r) > 0.0f) corr += iN; //measure correlation - - j = j0; - mask = k << 1; - - do //polyphase filter bank - { - p = lpp[0][j] + 0.208f * l; - lpp[0][j] = lpp[1][j]; - lpp[1][j] = l - 0.208f * p; - - q = lpp[2][j] + lpp[4][j] * 0.682f; - lpp[2][j] = lpp[3][j]; - lpp[3][j] = lpp[4][j] - 0.682f * q; - lpp[4][j] = l; - lpp[5][j] += (float)fabs(p - q); //top octave - l = p + q; //lower octaves - - p = rpp[0][j] + 0.208f * r; - rpp[0][j] = rpp[1][j]; - rpp[1][j] = r - 0.208f * p; - - q = rpp[2][j] + rpp[4][j] * 0.682f; - rpp[2][j] = rpp[3][j]; - rpp[3][j] = rpp[4][j] - 0.682f * q; - rpp[4][j] = r; - rpp[5][j] += (float)fabs(p - q); //top octave - r = p + q; //lower octaves - - j--; - mask >>= 1; - } while(mask & 1); - - if(++k == kmax) - { - k = 0; - counter++; //editor waits for this to change - - if(lpeak == 0.0f) Lpeak = Lrms = 0.0f; else ///add limits here! - { - if(lpeak > 2.0f) lpeak = 2.0f; - if(lpeak >= Lpeak) - { - Lpeak = lpeak; - Lhold = 2.0f * Lpeak; - } - else - { - Lhold *= 0.95f; - if(Lhold < Lpeak) Lpeak = Lhold; - } - Lmin = lmin; - lmin *= 1.01f; - Lrms += 0.2f * (iN * lrms - Lrms); - } - - if(rpeak == 0.0f) Rpeak = Rrms = 0.0f; else - { - if(rpeak > 2.0f) rpeak = 2.0f; - if(rpeak >= Rpeak) - { - Rpeak = rpeak; - Rhold = 2.0f * Rpeak; - } - else - { - Rhold *= 0.95f; - if(Rhold < Rpeak) Rpeak = Rhold; - } - Rmin = rmin; - rmin *= 1.01f; - Rrms += 0.2f * (iN * rrms - Rrms); - } - - rpeak = lpeak = lrms = rrms = 0.0f; - Corr += 0.1f * (corr - Corr); //correlation - corr = SILENCE; - - float dec = 0.08f; - for(j=0; j<13; j++) //spectrum output - { - band[0][j] += dec * (iN * lpp[5][j] - band[0][j]); - if(band[0][j] > 2.0f) band[0][j] = 2.0f; - else if(band[0][j] < 0.014f) band[0][j] = 0.014f; - - band[1][j] += dec * (iN * rpp[5][j] - band[1][j]); - if(band[1][j] > 2.0f) band[1][j] = 2.0f; - else if(band[1][j] < 0.014f) band[1][j] = 0.014f; - - rpp[5][j] = lpp[5][j] = SILENCE; - dec = dec * 1.1f; - } - } - } - - K = k; -} - -////////////////////////////////////////////////////////////////////////////////// - -void mdaSpecMeter::processReplacing (float **inputs, float **outputs, LvzInt32 sampleFrames) -{ - float *in1 = inputs[0]; - float *in2 = inputs[1]; - float *out1 = outputs[0]; - float *out2 = outputs[1]; - - den = -den; - float l, r, p, q, iN = iK; - long k=K, j0=topband, mask, j; - - while(--sampleFrames >= 0) - { - l = *in1++; - r = *in2++; - *out1++ = l; - *out2++ = r; - - l += den; //anti-denormal - r += den; - - lrms += l * l; //RMS integrate - rrms += r * r; - - p = (float)fabs(l); if(p > lpeak) lpeak = p; //peak detect - q = (float)fabs(r); if(q > rpeak) rpeak = q; -/* - if(p > 1.0e-8f && p < lmin) lmin = p; //'trough' detect - if(q > 1.0e-8f && q < rmin) rmin = q; -*/ - if((l * r) > 0.0f) corr += iN; //measure correlation - - j = j0; - mask = k << 1; - - do //polyphase filter bank - { - p = lpp[0][j] + 0.208f * l; - lpp[0][j] = lpp[1][j]; - lpp[1][j] = l - 0.208f * p; - - q = lpp[2][j] + lpp[4][j] * 0.682f; - lpp[2][j] = lpp[3][j]; - lpp[3][j] = lpp[4][j] - 0.682f * q; - lpp[4][j] = l; - lpp[5][j] += (float)fabs(p - q); //top octave - l = p + q; //lower octaves - - p = rpp[0][j] + 0.208f * r; - rpp[0][j] = rpp[1][j]; - rpp[1][j] = r - 0.208f * p; - - q = rpp[2][j] + rpp[4][j] * 0.682f; - rpp[2][j] = rpp[3][j]; - rpp[3][j] = rpp[4][j] - 0.682f * q; - rpp[4][j] = r; - rpp[5][j] += (float)fabs(p - q); //top octave - r = p + q; //lower octaves - - j--; - mask >>= 1; - } while(mask & 1); - - if(++k == kmax) - { - k = 0; - counter++; //editor waits for this to change - - if(lpeak == 0.0f) Lpeak = Lrms = 0.0f; else ///add limits here! - { - if(lpeak > 2.0f) lpeak = 2.0f; - if(lpeak >= Lpeak) - { - Lpeak = lpeak; - Lhold = 2.0f * Lpeak; - } - else - { - Lhold *= 0.95f; - if(Lhold < Lpeak) Lpeak = Lhold; - } - Lmin = lmin; - lmin *= 1.01f; - Lrms += 0.2f * (iN * lrms - Lrms); - } - - if(rpeak == 0.0f) Rpeak = Rrms = 0.0f; else - { - if(rpeak > 2.0f) rpeak = 2.0f; - if(rpeak >= Rpeak) - { - Rpeak = rpeak; - Rhold = 2.0f * Rpeak; - } - else - { - Rhold *= 0.95f; - if(Rhold < Rpeak) Rpeak = Rhold; - } - Rmin = rmin; - rmin *= 1.01f; - Rrms += 0.2f * (iN * rrms - Rrms); - } - - rpeak = lpeak = lrms = rrms = 0.0f; - Corr += 0.1f * (corr - Corr); //correlation - corr = SILENCE; - - float dec = 0.08f; - for(j=0; j<13; j++) //spectrum output - { - band[0][j] += dec * (iN * lpp[5][j] - band[0][j]); - if(band[0][j] > 2.0f) band[0][j] = 2.0f; - else if(band[0][j] < 0.014f) band[0][j] = 0.014f; - - band[1][j] += dec * (iN * rpp[5][j] - band[1][j]); - if(band[1][j] > 2.0f) band[1][j] = 2.0f; - else if(band[1][j] < 0.014f) band[1][j] = 0.014f; - - rpp[5][j] = lpp[5][j] = SILENCE; - dec = dec * 1.1f; - } - } - } - - K = k; -} diff --git a/vstgui/TODO b/vstgui/TODO new file mode 100644 index 0000000..27096e5 --- /dev/null +++ b/vstgui/TODO @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// VST Plug-Ins SDK Linux ONLY Port +// VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX: +// +// Version: 0.1 +// Author: kRAkEn/gORe +// Date: 2007/01/21 +//----------------------------------------------------------------------------- + +Todo List: + +- Fix multiple CParamDisplay objects not showed correctly (only the first is shown) +- Update other controls from a single control->update (), usually params displays not updating while mouse drag +- Fix fonts names and sizes (actually only fixed and courier) +- COffscreenContext not working (add an internal GC different from the frame GC) +- Fix COffscreenContext CopyFrom +- Turn back on CViewContainer::bOffscreenDraw (actually turned off to see something) +- Keep out MOTIF defines (now that IS the default) +- Make CBitmap work on every kind of GC (not pFrame only) diff --git a/vstgui/vstcontrols.cpp b/vstgui/vstcontrols.cpp new file mode 100644 index 0000000..0b0800c --- /dev/null +++ b/vstgui/vstcontrols.cpp @@ -0,0 +1,3651 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * + * Which was based on: + * ---------------------------------------------------------------------------- + * VSTGUI: Graphical User Interface Framework for VST plugins + * Version 3.0 $Date: 2005/08/12 12:45:00 $ + * ---------------------------------------------------------------------------- + * VSTGUI LICENSE + * 2004, Steinberg Media Technologies, All Rights Reserved + * ---------------------------------------------------------------------------- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Steinberg Media Technologies nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#ifndef __vstcontrols__ +#include "vstcontrols.h" +#endif + +#include "vstkeycode.h" + +namespace VSTGUI { + +// some external variables (vstgui.cpp) +extern long gStandardFontSize []; +extern const char *gStandardFontName []; + +//------------------------------------------------------------------------ +// CControl +//------------------------------------------------------------------------ +/*! @class CControl +This object manages the tag identification and the value of a control object. + +Note: +Since version 2.1, when an object uses the transparency for its background and draws on it (tranparency area) +or the transparency area changes during different draws (CMovieBitmap ,...), the background will be false (not updated), +you have to rewrite the draw function in order to redraw the background and then call the draw of the object. +*/ +CControl::CControl (const CRect &size, CControlListener *listener, long tag, + CBitmap *pBackground) +: CView (size), + listener (listener), tag (tag), oldValue (1), defaultValue (0.5f), + value (0), vmin (0), vmax (1.f), wheelInc (0.1f), lastTicks (-1) +{ + delta = 500; + + if (delta < 250) + delta = 250; + + setTransparency (false); + setMouseEnabled (true); + backOffset (0 ,0); + + setBackground (pBackground); +} + +//------------------------------------------------------------------------ +CControl::~CControl () +{ +} + +//------------------------------------------------------------------------ +void CControl::beginEdit () +{ + // begin of edit parameter + getFrame ()->setFocusView(this); + getFrame ()->beginEdit (tag); +} + +//------------------------------------------------------------------------ +void CControl::endEdit () +{ + // end of edit parameter + getFrame ()->endEdit (tag); +} + +//------------------------------------------------------------------------ +bool CControl::isDirty () const +{ + if (oldValue != value || CView::isDirty ()) + return true; + return false; +} + +//------------------------------------------------------------------------ +void CControl::setDirty (const bool val) +{ + CView::setDirty (val); + if (val) + { + if (value != -1.f) + oldValue = -1.f; + else + oldValue = 0.f; + } + else + oldValue = value; +} + +//------------------------------------------------------------------------ +void CControl::setBackOffset (CPoint &offset) +{ + backOffset = offset; +} + +//----------------------------------------------------------------------------- +void CControl::copyBackOffset () +{ + backOffset (size.left, size.top); +} + +//------------------------------------------------------------------------ +void CControl::bounceValue () +{ + if (value > vmax) + value = vmax; + else if (value < vmin) + value = vmin; +} + +//----------------------------------------------------------------------------- +bool CControl::checkDefaultValue (CDrawContext *pContext, long button) +{ + if (button == (kControl|kLButton)) + { + // begin of edit parameter + beginEdit (); + + value = getDefaultValue (); + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool CControl::isDoubleClick () +{ + long ticks = getFrame ()->getTicks (); + if (lastTicks <= 0) + { + lastTicks = ticks; + return false; + } + + if (lastTicks + delta > ticks) + lastTicks = 0; + else + { + lastTicks = ticks; + return false; + } + return true; +} + +//------------------------------------------------------------------------ +// COnOffButton +//------------------------------------------------------------------------ +/*! @class COnOffButton +Define a button with 2 positions. +The pixmap includes the 2 subpixmaps (i.e the rectangle used for the display of this button is half-height of the pixmap). +When its value changes, the listener is called. +*/ +COnOffButton::COnOffButton (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, long style) +: CControl (size, listener, tag, background) +, style (style) +{} + +//------------------------------------------------------------------------ +COnOffButton::~COnOffButton () +{} + +//------------------------------------------------------------------------ +void COnOffButton::draw (CDrawContext *pContext) +{ +// DBG ("COnOffButton::draw"); + + CCoord off; + + if (value && pBackground) + off = pBackground->getHeight () / 2; + else + off = 0; + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, CPoint (0, off)); + else + pBackground->draw (pContext, size, CPoint (0, off)); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void COnOffButton::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + if (!(button & kLButton)) + return; + + if (listener && (button & (kAlt | kShift | kControl | kApple))) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + value = ((long)value) ? 0.f : 1.f; + + if (listener && style == kPostListenerUpdate) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + } + + doIdleStuff (); + + if (listener && style == kPreListenerUpdate) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + } +} + + +//------------------------------------------------------------------------ +// CKnob +//------------------------------------------------------------------------ +/*! @class CKnob +Define a knob with a given background and foreground handle. +The handle describes a circle over the background (between -45deg and +225deg). +By clicking Alt+Left Mouse the default value is used. +By clicking Alt+Left Mouse the value changes with a vertical move (version 2.1) +*/ +CKnob::CKnob (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CBitmap *handle, const CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), pHandle (handle) +{ + if (pHandle) + { + pHandle->remember (); + inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f); + } + else + inset = 3; + + colorShadowHandle = kGreyCColor; + colorHandle = kWhiteCColor; + radius = (float)(size.right - size.left) / 2.f; + + rangeAngle = 1.f; + setStartAngle ((float)(5.f * kPI / 4.f)); + setRangeAngle ((float)(-3.f * kPI / 2.f)); + zoomFactor = 1.5f; + + setWantsFocus (true); +} + +//------------------------------------------------------------------------ +CKnob::~CKnob () +{ + if (pHandle) + pHandle->forget (); +} + +//------------------------------------------------------------------------ +void CKnob::draw (CDrawContext *pContext) +{ +// DBG ("CKnob::draw"); + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, offset); + else + pBackground->draw (pContext, size, offset); + } + drawHandle (pContext); + setDirty (false); +} + +//------------------------------------------------------------------------ +void CKnob::drawHandle (CDrawContext *pContext) +{ + CPoint where; + valueToPoint (where); + + if (pHandle) + { + long width = (long)pHandle->getWidth (); + long height = (long)pHandle->getHeight (); + where.offset (size.left - width / 2, size.top - height / 2); + + CRect handleSize (0, 0, width, height); + handleSize.offset (where.h, where.v); + pHandle->drawTransparent (pContext, handleSize); + } + else + { + CPoint origin (size.width () / 2, size.height () / 2); + + where.offset (size.left - 1, size.top); + origin.offset (size.left - 1, size.top); + pContext->setFrameColor (colorShadowHandle); + pContext->moveTo (where); + pContext->lineTo (origin); + + where.offset (1, -1); + origin.offset (1, -1); + pContext->setFrameColor (colorHandle); + pContext->moveTo (where); + pContext->lineTo (origin); + } +} + +//------------------------------------------------------------------------ +void CKnob::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + if (!(button & kLButton)) + return; + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + // check if default value wanted + if (checkDefaultValue (pContext, button)) + return; + + float old = oldValue; + CPoint firstPoint; + bool modeLinear = false; + float fEntryState = value; + float middle = (vmax - vmin) * 0.5f; + float range = 200.f; + float coef = (vmax - vmin) / range; + long oldButton = button; + + long mode = kCircularMode; + long newMode = getFrame ()->getKnobMode (); + if (kLinearMode == newMode) + { + if (!(button & kAlt)) + mode = newMode; + } + else if (button & kAlt) + mode = kLinearMode; + + if (mode == kLinearMode && (button & kLButton)) + { + if (button & kShift) + range *= zoomFactor; + firstPoint = where; + modeLinear = true; + coef = (vmax - vmin) / range; + } + else + { + CPoint where2 (where); + where2.offset (-size.left, -size.top); + old = valueFromPoint (where2); + } + + CPoint oldWhere (-1, -1); + + // begin of edit parameter + beginEdit (); + do + { + button = pContext->getMouseButtons (); + if (where != oldWhere) + { + oldWhere = where; + if (modeLinear) + { + CCoord diff = (firstPoint.v - where.v) + (where.h - firstPoint.h); + if (button != oldButton) + { + range = 200.f; + if (button & kShift) + range *= zoomFactor; + + float coef2 = (vmax - vmin) / range; + fEntryState += diff * (coef - coef2); + coef = coef2; + oldButton = button; + } + value = fEntryState + diff * coef; + bounceValue (); + } + else + { + where.offset (-size.left, -size.top); + value = valueFromPoint (where); + if (old - value > middle) + value = vmax; + else if (value - old > middle) + value = vmin; + else + old = value; + } + if (isDirty () && listener) + listener->valueChanged (pContext, this); + } + getMouseLocation (pContext, where); + doIdleStuff (); + + } while (button & kLButton); + + // end of edit parameter + endEdit (); +} + +//------------------------------------------------------------------------ +bool CKnob::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + if (!bMouseEnabled) + return false; + + long buttons = pContext->getMouseButtons (); + if (buttons & kShift) + value += 0.1f * distance * wheelInc; + else + value += distance * wheelInc; + bounceValue (); + + if (isDirty () && listener) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + } + return true; +} + +//------------------------------------------------------------------------ +long CKnob::onKeyDown (VstKeyCode& keyCode) +{ + switch (keyCode.virt) + { + case VKEY_UP : + case VKEY_RIGHT : + case VKEY_DOWN : + case VKEY_LEFT : + { + float distance = 1.f; + if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT) + distance = -distance; + + if (keyCode.modifier & MODIFIER_SHIFT) + value += 0.1f * distance * wheelInc; + else + value += distance * wheelInc; + bounceValue (); + + if (isDirty () && listener) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (0, this); + + // end of edit parameter + endEdit (); + } + } return 1; + } + return -1; +} + +//------------------------------------------------------------------------ +void CKnob::setStartAngle (float val) +{ + startAngle = val; + compute (); +} + +//------------------------------------------------------------------------ +void CKnob::setRangeAngle (float val) +{ + rangeAngle = val; + compute (); +} + +//------------------------------------------------------------------------ +void CKnob::compute () +{ + aCoef = (vmax - vmin) / rangeAngle; + bCoef = vmin - aCoef * startAngle; + halfAngle = ((float)k2PI - fabsf (rangeAngle)) * 0.5f; + setDirty (); +} + +//------------------------------------------------------------------------ +void CKnob::valueToPoint (CPoint &point) const +{ + float alpha = (value - bCoef) / aCoef; + point.h = (long)(radius + cosf (alpha) * (radius - inset) + 0.5f); + point.v = (long)(radius - sinf (alpha) * (radius - inset) + 0.5f); +} + +//------------------------------------------------------------------------ +float CKnob::valueFromPoint (CPoint &point) const +{ + float v; + float alpha = (float)atan2 (radius - point.v, point.h - radius); + if (alpha < 0.f) + alpha += (float)k2PI; + + float alpha2 = alpha - startAngle; + if (rangeAngle < 0) + { + alpha2 -= rangeAngle; + float alpha3 = alpha2; + if (alpha3 < 0.f) + alpha3 += (float)k2PI; + else if (alpha3 > k2PI) + alpha3 -= (float)k2PI; + if (alpha3 > halfAngle - rangeAngle) + v = vmax; + else if (alpha3 > -rangeAngle) + v = vmin; + else + { + if (alpha2 > halfAngle - rangeAngle) + alpha2 -= (float)k2PI; + else if (alpha2 < -halfAngle) + alpha2 += (float)k2PI; + v = aCoef * alpha2 + vmax; + } + } + else + { + float alpha3 = alpha2; + if (alpha3 < 0.f) + alpha3 += (float)k2PI; + else if (alpha3 > k2PI) + alpha3 -= (float)k2PI; + if (alpha3 > rangeAngle + halfAngle) + v = vmin; + else if (alpha3 > rangeAngle) + v = vmax; + else + { + if (alpha2 > rangeAngle + halfAngle) + alpha2 -= (float)k2PI; + else if (alpha2 < -halfAngle) + alpha2 += (float)k2PI; + v = aCoef * alpha2 + vmin; + } + } + + return v; +} + +//------------------------------------------------------------------------ +void CKnob::setColorShadowHandle (CColor color) +{ + colorShadowHandle = color; + setDirty (); +} + +//------------------------------------------------------------------------ +void CKnob::setColorHandle (CColor color) +{ + colorHandle = color; + setDirty (); +} + +//------------------------------------------------------------------------ +void CKnob::setHandleBitmap (CBitmap *bitmap) +{ + if (pHandle) + { + pHandle->forget (); + pHandle = 0; + } + + if (bitmap) + { + pHandle = bitmap; + pHandle->remember (); + inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f); + } +} + + +//------------------------------------------------------------------------ +// CParamDisplay +//------------------------------------------------------------------------ +/*! @class CParamDisplay +Define a rectangle view where a text-value can be displayed with a given font and color. +The user can specify its convert function (from float to char) by default the string format is "%2.2f". +The text-value is centered in the given rect. +*/ +CParamDisplay::CParamDisplay (const CRect &size, CBitmap *background, const long style) +: CControl (size, 0, 0, background), stringConvert (0), stringConvert2 (0), string2FloatConvert (0), + horiTxtAlign (kCenterText), style (style), bTextTransparencyEnabled (true) +{ + backOffset (0, 0); + + fontID = kNormalFont; + txtFace = kNormalFace; + fontColor = kWhiteCColor; + backColor = kBlackCColor; + frameColor = kBlackCColor; + shadowColor = kRedCColor; + userData = 0; + if (style & kNoDrawStyle) + setDirty (false); +} + +//------------------------------------------------------------------------ +CParamDisplay::~CParamDisplay () +{} + +//------------------------------------------------------------------------ +void CParamDisplay::setStyle (long val) +{ + if (style != val) + { + style = val; + setDirty (); + } +} + +//------------------------------------------------------------------------ +void CParamDisplay::draw (CDrawContext *pContext) +{ +// DBG ("CParamDisplay::draw"); + + char string[256]; + string[0] = 0; + + if (stringConvert2) + stringConvert2 (value, string, userData); + else if (stringConvert) + stringConvert (value, string); + else + sprintf (string, "%2.2f", value); + + drawText (pContext, string); +} + +//------------------------------------------------------------------------ +void CParamDisplay::drawText (CDrawContext *pContext, char *string, CBitmap *newBack) +{ + setDirty (false); + + if (style & kNoDrawStyle) + return; + + // draw the background + if (newBack) + { + if (bTransparencyEnabled) + newBack->drawTransparent (pContext, size, backOffset); + else + newBack->draw (pContext, size, backOffset); + } + else if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, backOffset); + else + pBackground->draw (pContext, size, backOffset); + } + else + { + if (!bTransparencyEnabled) + { + pContext->setFillColor (backColor); + pContext->fillRect (size); + + if (!(style & (k3DIn|k3DOut|kNoFrame))) + { + pContext->setFrameColor (frameColor); + pContext->drawRect (size); + } + } + } + // draw the frame for the 3D effect + if (style & (k3DIn|k3DOut)) + { + if (style & k3DIn) + pContext->setFrameColor (backColor); + else + pContext->setFrameColor (frameColor); + CPoint p; + pContext->moveTo (p (size.left, size.bottom)); + pContext->lineTo (p (size.left, size.top)); + pContext->lineTo (p (size.right + 1, size.top)); + + if (style & k3DIn) + pContext->setFrameColor (frameColor); + else + pContext->setFrameColor (backColor); + pContext->moveTo (p (size.right, size.top + 1)); + pContext->lineTo (p (size.right, size.bottom)); + pContext->lineTo (p (size.left, size.bottom)); + } + + if (!(style & kNoTextStyle) && string) + { + CRect oldClip; + pContext->getClipRect (oldClip); + CRect newClip (size); + newClip.bound (oldClip); + pContext->setClipRect (newClip); + pContext->setFont (fontID, 0, txtFace); + + // draw darker text (as shadow) + if (style & kShadowText) + { + CRect newSize (size); + newSize.offset (1, 1); + pContext->setFontColor (shadowColor); + pContext->drawString (string, newSize, !bTextTransparencyEnabled, horiTxtAlign); + } + pContext->setFontColor (fontColor); + pContext->drawString (string, size, !bTextTransparencyEnabled, horiTxtAlign); + pContext->setClipRect (oldClip); + } +} + +//------------------------------------------------------------------------ +void CParamDisplay::setFont (CFont newFontID) +{ + // to force the redraw + if (fontID != newFontID) + setDirty (); + fontID = newFontID; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setTxtFace (CTxtFace newTxtFace) +{ + // to force the redraw + if (txtFace != newTxtFace) + setDirty (); + txtFace = newTxtFace; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setFontColor (CColor color) +{ + // to force the redraw + if (fontColor != color) + setDirty (); + fontColor = color; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setBackColor (CColor color) +{ + // to force the redraw + if (backColor != color) + setDirty (); + backColor = color; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setFrameColor (CColor color) +{ + // to force the redraw + if (frameColor != color) + setDirty (); + frameColor = color; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setShadowColor (CColor color) +{ + // to force the redraw + if (shadowColor != color) + setDirty (); + shadowColor = color; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setHoriAlign (CHoriTxtAlign hAlign) +{ + // to force the redraw + if (horiTxtAlign != hAlign) + setDirty (); + horiTxtAlign = hAlign; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setStringConvert (void (*convert) (float value, char *string)) +{ + stringConvert = convert; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setStringConvert (void (*convert) (float value, char *string, + void *userDta), void *userData_) +{ + stringConvert2 = convert; + userData = userData_; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setString2FloatConvert (void (*convert) (char *string, float &output)) +{ + string2FloatConvert = convert; +} + +//------------------------------------------------------------------------ +// CTextLabel +//------------------------------------------------------------------------ +/*! @class CTextLabel +*/ +CTextLabel::CTextLabel (const CRect& size, const char* txt, CBitmap* background, const long style) +: CParamDisplay (size, background, style) +, text (0) +{ + setText (txt); +} + +//------------------------------------------------------------------------ +CTextLabel::~CTextLabel () +{ + freeText (); +} + +//------------------------------------------------------------------------ +void CTextLabel::freeText () +{ + if (text) + free (text); + text = 0; +} + +//------------------------------------------------------------------------ +void CTextLabel::setText (const char* txt) +{ + freeText (); + if (txt) + { + text = (char*)malloc (strlen (txt)+1); + strcpy (text, txt); + } +} + +//------------------------------------------------------------------------ +const char* CTextLabel::getText () const +{ + return text; +} + +//------------------------------------------------------------------------ +void CTextLabel::draw (CDrawContext *pContext) +{ +// DBG ("CTextLabel::draw"); + + drawText (pContext, text); + setDirty (false); +} + + +//------------------------------------------------------------------------ +// CTextEdit +//------------------------------------------------------------------------ +/*! @class CTextEdit +Define a rectangle view where a text-value can be displayed and edited with a given font and color. +The user can specify its convert function (from char to char). The text-value is centered in the given rect. +A pixmap can be used as background. +*/ +CTextEdit::CTextEdit (const CRect &size, CControlListener *listener, long tag, + const char *txt, CBitmap *background, const long style) +: CParamDisplay (size, background, style), platformFontColor (0), platformControl (0), + platformFont (0), editConvert (0), editConvert2 (0) +{ + this->listener = listener; + this->tag = tag; + + if (txt) + strcpy (text, txt); + else + strcpy (text, ""); + setWantsFocus (true); +} + +//------------------------------------------------------------------------ +CTextEdit::~CTextEdit () +{} + +//------------------------------------------------------------------------ +void CTextEdit::setText (char *txt) +{ + if (txt) + { + if (strcmp (text, txt)) + { + strcpy (text, txt); + + // to force the redraw + setDirty (); + } + } + else + { + if (strcmp (text, "")) + { + strcpy (text, ""); + + // to force the redraw + setDirty (); + } + } +} + +//------------------------------------------------------------------------ +void CTextEdit::getText (char *txt) const +{ + if (txt) + strcpy (txt, text); +} + +//------------------------------------------------------------------------ +void CTextEdit::draw (CDrawContext *pContext) +{ +// DBG ("CTextEdit::draw"); + + if (platformControl) + { + setDirty (false); + return; + } + + char string[256]; + string[0] = 0; + + if (editConvert2) + editConvert2 (text, string, userData); + else if (editConvert) + editConvert (text, string); + // Allow to display strings through the stringConvert + // callbacks inherited from CParamDisplay + else if (stringConvert2) + { + string[0] = 0; + stringConvert2 (value, string, userData); + strcpy(text, string); + } + else if (stringConvert) + { + string[0] = 0; + stringConvert (value, string); + strcpy(text, string); + } + else + sprintf (string, "%s", text); + + drawText (pContext, string); + setDirty (false); +} + +//------------------------------------------------------------------------ +void CTextEdit::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (button & kLButton) + { + if (getFrame ()->getFocusView () != this) + { + if (style & kDoubleClickStyle) + if (!isDoubleClick ()) + return; + + beginEdit(); + takeFocus (pContext); + } + } +} + +//------------------------------------------------------------------------ +// #include +extern XFontStruct *gFontStructs[]; + +//------------------------------------------------------------------------ +void CTextEdit::takeFocus (CDrawContext *pContext) +{ + bWasReturnPressed = false; + +/* + // we have to add the Text to the parent !! + Dimension posX, posY; + Widget widget = (Widget)(getFrame ()->getSystemWindow ()); + XtVaGetValues (widget, XmNx, &posX, XmNy, &posY, 0); + + Arg args[20]; + int n = 0; + XtSetArg (args[n], XmNx, size.left + posX); n++; + XtSetArg (args[n], XmNy, size.top + posY); n++; + XtSetArg (args[n], XmNwidth, size.width () + 1); n++; + XtSetArg (args[n], XmNheight, size.height () + 2); n++; + + XtSetArg (args[n], XmNvalue, text); n++; + + XtSetArg (args[n], XmNshadowType, XmSHADOW_IN); n++; + XtSetArg (args[n], XmNshadowThickness, 0); n++; + XtSetArg (args[n], XmNcursorPositionVisible, true); n++; + + XtSetArg (args[n], XmNmarginWidth, 0); n++; + XtSetArg (args[n], XmNmarginHeight, 0); n++; + XtSetArg (args[n], XmNresizeHeight, True); n++; + XtSetArg (args[n], XmNborderWidth, 0); n++; + XtSetArg (args[n], XmNeditMode, XmSINGLE_LINE_EDIT); n++; + + // get/set the current font + XmFontList fl = 0; + XFontStruct* fs = gFontStructs [fontID]; + if (fs) + { + XmFontListEntry entry = XmFontListEntryCreate (XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT, fs); + XmFontList fl = XmFontListAppendEntry (0, entry); + XtSetArg (args[n], XmNfontList, fl); n++; + } + + platformControl = XmCreateText (XtParent (widget), "Text", args, n); + XtManageChild ((Widget)platformControl); + if (fl) + XmFontListFree (fl); + XmTextSetSelection ((Widget)platformControl, 0, strlen (text), 0); + XmTextSetHighlight ((Widget)platformControl, 0, strlen (text), XmHIGHLIGHT_SELECTED); +*/ +} + +//------------------------------------------------------------------------ +void CTextEdit::looseFocus (CDrawContext *pContext) +{ + // Call this yet to avoid recursive call + endEdit(); + if (getFrame ()->getFocusView () == this) + getFrame ()->setFocusView (0); + + if (platformControl == 0) + return; + +/* + char oldText[256]; + strcpy (oldText, text); + + char *pNewText = XmTextGetString ((Widget)platformControl); + strcpy (text, pNewText); + XtFree (pNewText); + + XtUnmanageChild ((Widget)platformControl); + XtDestroyWidget ((Widget)platformControl); + + CPoint origOffset; + bool resetContextOffset = false; + if (!pContext) + { + // create a local context + pContext = getFrame ()->createDrawContext (); + if (getParentView ()) + { + resetContextOffset = true; + origOffset.x = pContext->offset.x; + origOffset.y = pContext->offset.y; + CView *view= getParentView (); + CRect rect2; + view->getViewSize (rect2); + CPoint offset; + view->localToFrame (offset); + rect2.offset (offset.x, offset.y); + pContext->offset.h = rect2.left; + pContext->offset.v = rect2.top; + } + } + else + pContext->remember (); + + // update dependency + bool change = false; + if (strcmp (oldText, text)) + { + change = true; + if (listener) + listener->valueChanged (pContext, this); + } + + platformControl = 0; + if (resetContextOffset) + { + pContext->offset.x = origOffset.x; + pContext->offset.y = origOffset.y; + } + pContext->forget (); + + if (change) + doIdleStuff (); + + CView* receiver = pParentView ? pParentView : pParentFrame; + if (receiver) + receiver->notify (this, "LooseFocus"); +*/ +} + +//------------------------------------------------------------------------ +void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string)) +{ + editConvert = convert; +} + +//------------------------------------------------------------------------ +void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string, + void *userDta), void *userData) +{ + editConvert2 = convert; + this->userData = userData; +} + +//------------------------------------------------------------------------ +// COptionMenuScheme +//------------------------------------------------------------------------ +/*! @class COptionMenuScheme +Used to define the appearance (font color, background color...) of a popup-menu. +To define the scheme of a menu, use the appropriate setScheme method (see COptionMenu). +@section coptionmenuscheme_new_in_3_0 New since 3.0 +You can also use the global variable gOptionMenuScheme to use one scheme on all menus. +@section coptionmenuscheme_note Note +If you want to use it on Mac OS X, you must set the macro MAC_ENABLE_MENU_SCHEME (needs Mac OS X 10.3 or higher) +*/ +COptionMenuScheme* gOptionMenuScheme = 0; + +//------------------------------------------------------------------------ +COptionMenuScheme::COptionMenuScheme () +{ + backgroundColor = kGreyCColor; + selectionColor = kBlueCColor; + textColor = kBlackCColor; + hiliteTextColor = kWhiteCColor; + disableTextColor = kWhiteCColor; + + font = kNormalFontSmall; +} + +//------------------------------------------------------------------------ +COptionMenuScheme::~COptionMenuScheme () +{ +} + +//------------------------------------------------------------------------ +void COptionMenuScheme::getItemSize (const char* text, CDrawContext* pContext, CPoint& size) +{ + if (!strcmp (text, kMenuSeparator)) // separator + { + // was: size.h = size.v = 6; + size.h = 6; + size.v = 18; + // separators must have same height, otherwise we have problems + // in multi-column menus :( + } + else + { + pContext->setFont (font); + size.h = pContext->getStringWidth (text) + 18; + size.v = 18; + } +} + +//------------------------------------------------------------------------ +void COptionMenuScheme::drawItemBack (CDrawContext* pContext, const CRect& rect, bool hilite) +{ + if (hilite) + pContext->setFillColor (selectionColor); + else + pContext->setFillColor (backgroundColor); + pContext->fillRect (rect); +} + +//------------------------------------------------------------------------ +void COptionMenuScheme::drawItem (const char* text, long itemId, long state, CDrawContext* pContext, const CRect& rect) +{ + bool hilite = (state & kSelected) != 0; + + drawItemBack (pContext, rect, hilite); + + if (!strcmp (text, kMenuSeparator)) + { + CCoord y = rect.top + rect.height () / 2; + + const CColor bc = { 0, 0, 0, 150}; + const CColor wc = { 255, 255, 255, 150}; + + pContext->setFrameColor (bc); + pContext->moveTo (CPoint (rect.left + 2, y - 1)); + pContext->lineTo (CPoint (rect.right - 2, y - 1)); + pContext->setFrameColor (wc); + pContext->moveTo (CPoint (rect.left + 2, y)); + pContext->lineTo (CPoint (rect.right - 2, y)); + return; + } + + CRect r; + if (state & kChecked) + { + r (6, 4, 14, 12); + r.offset (rect.left, rect.top); + if (hilite) + pContext->setFillColor (hiliteTextColor); + else + pContext->setFillColor (textColor); + pContext->fillEllipse (r); + } + + r = rect; + r.left += 18; + pContext->setFont (font); + if (state & kDisabled) + pContext->setFontColor (disableTextColor); + else + { + if (hilite) + pContext->setFontColor (hiliteTextColor); + else + pContext->setFontColor (textColor); + } + + // this needs to be done right, without changing the text pointer in anyway ;-) + char *ptr = (char*)strstr (text, "\t"); + if (ptr) + { + char modifier[32]; + strcpy (modifier, ptr + 1); + *ptr = 0; + pContext->drawString (text, r, false, kLeftText); + + *ptr = '\t'; + r.left = r.right - 50; + pContext->drawString (modifier, r, false, kLeftText); + } + else + pContext->drawString (text, r, false, kLeftText); +} + + +//------------------------------------------------------------------------ +// COptionMenu +//------------------------------------------------------------------------ +/*! @class COptionMenu +Define a rectangle view where a text-value can be displayed with a given font and color. +The text-value is centered in the given rect. +A pixmap can be used as background, a second pixmap can be used when the option menu is popuped. +There are 2 styles with or without a shadowed text. When a mouse click occurs, a popup menu is displayed. +*/ +COptionMenu::COptionMenu (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CBitmap *bgWhenClick, const long style) +: CParamDisplay (size, background, style), bgWhenClick (bgWhenClick), nbItemsPerColumn (-1), + prefixNumbers (0), scheme (0) +{ + this->listener = listener; + this->tag = tag; + + nbEntries = 0; + nbSubMenus = 0; + currentIndex = -1; + lastButton = kRButton; + platformControl = 0; + lastResult = -1; + lastMenu = 0; + + if (bgWhenClick) + bgWhenClick->remember (); + + nbSubMenuAllocated = nbAllocated = 0; + + check = 0; + entry = 0; + submenuEntry = 0; +} + +//------------------------------------------------------------------------ +COptionMenu::~COptionMenu () +{ + removeAllEntry (); + + if (bgWhenClick) + bgWhenClick->forget (); +} + +//------------------------------------------------------------------------ +void COptionMenu::setPrefixNumbers (long preCount) +{ + prefixNumbers = preCount; +} + +//----------------------------------------------------------------------------- +bool COptionMenu::allocateSubMenu (long nb) +{ + long newAllocated = nbSubMenuAllocated + nb; + + if (submenuEntry) + submenuEntry = (COptionMenu**)realloc (submenuEntry, newAllocated * sizeof (COptionMenu*)); + else + submenuEntry = (COptionMenu**)malloc (newAllocated * sizeof (COptionMenu*)); + + long i; + for (i = nbSubMenuAllocated; i < newAllocated; i++) + submenuEntry[i] = 0; + + nbSubMenuAllocated = newAllocated; + + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::allocateMenu (long nb) +{ + long newAllocated = nbAllocated + nb; + + if (check) + check = (bool*)realloc (check, newAllocated * sizeof (bool)); + else + check = (bool*)malloc (newAllocated * sizeof (bool)); + if (!check) + return false; + + if (entry) + entry = (char**)realloc (entry, newAllocated * sizeof (char*)); + else + entry = (char**)malloc (newAllocated * sizeof (char*)); + if (!entry) + { + free (check); + return false; + } + + long i; + for (i = nbAllocated; i < newAllocated; i++) + { + check[i] = false; + entry[i] = 0; + } + + nbAllocated = newAllocated; + + return true; +} + +//------------------------------------------------------------------------ +COptionMenu* COptionMenu::getSubMenu (long idx) const +{ + if (submenuEntry && idx < nbSubMenus) + return submenuEntry[idx]; + return 0; +} + +//------------------------------------------------------------------------ +bool COptionMenu::addEntry (COptionMenu *subMenu, char *txt) +{ + if (nbEntries >= MAX_ENTRY || !subMenu || !txt) + return false; + + if (nbEntries >= nbAllocated) + if (!allocateMenu (32)) + return false; + + entry[nbEntries] = (char*)malloc (256); + switch (prefixNumbers) + { + case 2: + sprintf (entry[nbEntries], "-M%1d %s", (int)(nbEntries + 1), txt); + break; + + case 3: + sprintf (entry[nbEntries], "-M%02d %s", (int)(nbEntries + 1), txt); + break; + + case 4: + sprintf (entry[nbEntries], "-M%03d %s", (int)(nbEntries + 1), txt); + break; + + default: + sprintf (entry[nbEntries], "-M%s", txt); + } + + + if (nbSubMenus >= nbSubMenuAllocated) + if (!allocateSubMenu (10)) + return false; + + submenuEntry[nbSubMenus++] = subMenu; + subMenu->remember (); + + nbEntries++; + + if (currentIndex < 0) + currentIndex = 0; + + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::addEntry (char *txt, long index) +{ + if (nbEntries >= MAX_ENTRY) + return false; + + if (nbEntries >= nbAllocated) + if (!allocateMenu (32)) + return false; + + entry[nbEntries] = (char*)malloc (256); + + long pos = nbEntries; + + // switch the entries for the insert + if (index >= 0) + { + for (long i = nbEntries; i > index; i--) + strcpy (entry[i], entry[i - 1]); + if (index >= nbEntries) + pos = nbEntries; + else + pos = index; + if (currentIndex >= index) + currentIndex++; + } + + *entry[pos] = 0; + if (txt) + { + switch (prefixNumbers) + { + case 2: + sprintf (entry[pos], "%1d %s", (int)(index + 1), txt); + break; + + case 3: + sprintf (entry[pos], "%02d %s", (int)(index + 1), txt); + break; + + case 4: + sprintf (entry[pos], "%03d %s", (int)(index + 1), txt); + break; + + default: + strncpy (entry[pos], txt, 256); + } + } + + nbEntries++; + + if (currentIndex < 0) + currentIndex = 0; + + return true; +} + +//------------------------------------------------------------------------ +long COptionMenu::getCurrent (char *txt, bool countSeparator) const +{ + if (currentIndex < 0) + return -1; + + long result = 0; + + if (countSeparator) + { + if (txt) + strcpy (txt, entry[currentIndex]); + result = currentIndex; + } + else + { + for (long i = 0; i < currentIndex; i++) + { + if (strcmp (entry[i], kMenuSeparator) && strncmp (entry[i], kMenuTitle, 2)) + result++; + } + if (txt) + strcpy (txt, entry[currentIndex]); + } + return result; +} + +//------------------------------------------------------------------------ +bool COptionMenu::setCurrent (long index, bool countSeparator) +{ + if (index < 0 || index >= nbEntries) + return false; + + if (countSeparator) + { + if (!strcmp (entry[index], kMenuSeparator) && strncmp (entry[index], kMenuTitle, 2)) + return false; + + currentIndex = index; + } + else + { + long newCurrent = 0; + long i = 0; + while (i <= index && newCurrent < nbEntries) + { + if (strcmp (entry[newCurrent], kMenuSeparator) && strncmp (entry[newCurrent], kMenuTitle, 2)) + i++; + newCurrent++; + } + currentIndex = newCurrent - 1; + } + if (style & (kMultipleCheckStyle & ~kCheckStyle)) + check[currentIndex] = !check[currentIndex]; + + // to force the redraw + setDirty (); + + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::getEntry (long index, char *txt) const +{ + if (index < 0 || index >= nbEntries) + return false; + + if (txt) + strcpy (txt, entry[index]); + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::setEntry (long index, char *txt) +{ + if (index < 0 || index >= nbEntries) + return false; + + if (txt) + strcpy (entry[index], txt); + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::removeEntry (long index) +{ + if (index < 0 || index >= nbEntries) + return false; + + nbEntries--; + + // switch the entries + for (long i = index; i < nbEntries; i++) + { + strcpy (entry[i], entry[i + 1]); + check[i] = check [i + 1]; + } + + if (currentIndex >= index) + currentIndex--; + + // delete the last one + free (entry[nbEntries]); + entry[nbEntries] = 0; + check[nbEntries] = false; + + if (nbEntries == 0) + currentIndex = -1; + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::removeAllEntry () +{ + long i; + for (i = 0; i < nbEntries; i++) + { + free (entry[i]); + entry[i] = 0; + check[i] = false; + } + + nbEntries = 0; + currentIndex = -1; + + for (i = 0; i < nbSubMenus; i++) + { + submenuEntry[i]->forget (); + submenuEntry[i] = 0; + } + nbSubMenus = 0; + + if (check) + free (check); + check = 0; + if (entry) + free (entry); + entry = 0; + if (submenuEntry) + free (submenuEntry); + submenuEntry = 0; + nbAllocated = 0; + nbSubMenuAllocated = 0; + + return true; +} + +//------------------------------------------------------------------------ +long COptionMenu::getIndex (char *txt) const +{ + if (!txt) + return -1; + + // search entries + for (long i = 0; i < nbEntries; i++) + if (!strcmp (entry[i], txt)) + return i; + + // not found + return -1; +} + +//------------------------------------------------------------------------ +bool COptionMenu::checkEntry (long index, bool state) +{ + if (index < 0 || index >= nbEntries) + return false; + + check[index] = state; + + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::checkEntryAlone (long index) +{ + if (index < 0 || index >= nbEntries) + return false; + for (long i = 0; i < nbEntries; i++) + check[i] = false; + check[index] = true; + + return true; +} + +//------------------------------------------------------------------------ +bool COptionMenu::isCheckEntry (long index) const +{ + if (index < 0 || index >= nbEntries) + return false; + + return check[index]; +} + +//------------------------------------------------------------------------ +void COptionMenu::draw (CDrawContext *pContext) +{ +// DBG ("COptionMenu::draw"); + + if (currentIndex >= 0 && nbEntries) + drawText (pContext, entry[currentIndex] + prefixNumbers); + else + drawText (pContext, NULL); +} + +//------------------------------------------------------------------------ +void COptionMenu::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled || !getFrame () || !pContext) + return; + + lastButton = (button != -1) ? button : pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (lastButton & (kLButton|kRButton|kApple)) + { + if (bgWhenClick) + { + char string[256]; + if (currentIndex >= 0) + sprintf (string, "%s", entry[currentIndex]); + else + string[0] = 0; + + drawText (pContext, string, bgWhenClick); + } + + beginEdit(); + takeFocus (pContext); + } +} + +//------------------------------------------------------------------------ +/* +#include +#include +#include +#include + +static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData); +static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData); + +//------------------------------------------------------------------------ +static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData) +{ + COptionMenu *optionMenu= (COptionMenu*)clientData; + optionMenu->looseFocus (); +} + +//------------------------------------------------------------------------ +static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData) +{ + COptionMenu *optionMenu= (COptionMenu*)clientData; + optionMenu->setCurrentSelected ((void*)item); +} +*/ + +//------------------------------------------------------------------------ +COptionMenu *COptionMenu::getLastItemMenu (long &idxInMenu) const +{ + idxInMenu = lastMenu ? (long)lastMenu->getValue (): -1; + return lastMenu; +} + +//------------------------------------------------------------------------ +COptionMenu *COptionMenu::getItemMenu (long idx, long &idxInMenu, long &offsetIdx) +{ + COptionMenu *menu = 0; + for (long i = 0; i < nbSubMenus; i++) + { + menu = submenuEntry[i]->getItemMenu (idx, idxInMenu, offsetIdx); + if (menu) + break; + } + return menu; +} + +//------------------------------------------------------------------------ +void COptionMenu::removeItems () +{ + for (long i = 0; i < nbSubMenus; i++) + submenuEntry[i]->removeItems (); +} + +//------------------------------------------------------------------------ +void *COptionMenu::appendItems (long &offsetIdx) +{ + //bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle); + return NULL; +} + +//------------------------------------------------------------------------ +void COptionMenu::setValue (float val) +{ + if ((long)val < 0 || (long)val >= nbEntries) + return; + + currentIndex = (long)val; + if (style & (kMultipleCheckStyle & ~kCheckStyle)) + check[currentIndex] = !check[currentIndex]; + CParamDisplay::setValue (val); + + // to force the redraw + setDirty (); +} + +//------------------------------------------------------------------------ +void COptionMenu::takeFocus (CDrawContext *pContext) +{ + if (!getFrame ()) + return; + +/* + + bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle); + lastResult = -1; + lastMenu = 0; + + Arg args[10]; + int n = 0; + + // get the position of the pParent + CRect rect; + getFrame ()->getSize (&rect); + + if (pContext) + { + rect.left += pContext->offset.h; + rect.top += pContext->offset.v; + } + + // create a popup menu + int offset; + if (style & kPopupStyle) + offset = (int)(rect.top + size.top); + else + offset = (int)(rect.top + size.bottom); + + XtSetArg (args[n], XmNx, rect.left + size.left); n++; + XtSetArg (args[n], XmNy, offset); n++; + XtSetArg (args[n], XmNmenuHistory, currentIndex); n++; + XtSetArg (args[n], XmNtraversalOn, true); n++; + + platformControl = (void*)XmCreatePopupMenu ((Widget)(getFrame ()->getSystemWindow ()), + "popup", args, n); + + XtAddCallback ((Widget)platformControl, XmNunmapCallback, _unmapCallback, this); + + // insert the menu items + for (long i = 0; i < nbEntries; i++) + { + if (!strcmp (entry[i], kMenuSeparator)) + { + itemWidget[i] = (void*)XtCreateManagedWidget ("separator", + xmSeparatorGadgetClass, (Widget)platformControl, 0, 0); + } + else + { + if (multipleCheck) + { + itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], + xmToggleButtonWidgetClass, (Widget)platformControl, + XmNset, check[i], XmNvisibleWhenOff, false, 0); + XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this); + } + else if (style & kCheckStyle) + { + itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], + xmToggleButtonWidgetClass, (Widget)platformControl, + XmNset, (i == currentIndex) ? true : false, XmNvisibleWhenOff, false, 0); + XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this); + } + else + { + itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], + xmPushButtonWidgetClass, (Widget)platformControl, 0); + XtAddCallback ((Widget)itemWidget[i], XmNactivateCallback, _activateCallback, this); + } + } + } + + XtManageChild ((Widget)platformControl); + getFrame ()->setFocusView (0); + endEdit(); +*/ +} + +//------------------------------------------------------------------------ +void COptionMenu::looseFocus (CDrawContext *pContext) +{ + if (platformControl == 0) + return; + +/* + for (long i = 0; i < nbEntries; i++) + if (itemWidget[i]) + XtDestroyWidget ((Widget)itemWidget[i]); + + if (platformControl) + { + XtUnmanageChild ((Widget)platformControl); + XtDestroyWidget ((Widget)platformControl); + } +*/ + platformControl = 0; +} + +//------------------------------------------------------------------------ +void COptionMenu::setCurrentSelected (void *itemSelected) +{ + // retrieve the current index + if (itemSelected != 0) + { + for (long i = 0; i < nbEntries; i++) + if (itemWidget[i] == itemSelected) + { + currentIndex = i; + break; + } + } + + // update dependency + CDrawContext *pContext = new CDrawContext (getFrame (), (void*)getFrame ()->getGC (), (void*)getFrame ()->getBackBuffer ()); + + setValue (currentIndex); + + if (listener) + listener->valueChanged (pContext, this); + delete pContext; +} + + +//------------------------------------------------------------------------ +// CAnimKnob +//------------------------------------------------------------------------ +/*! @class CAnimKnob +Such as a CKnob control object, but there is a unique pixmap which contains different views (subpixmaps) of this knob. +According to the value, a specific subpixmap is displayed. The different subpixmaps are stacked in the pixmap object. +*/ +CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) +: CKnob (size, listener, tag, background, 0, offset), bInverseBitmap (false) +{ + heightOfOneImage = size.height (); + subPixmaps = (short)(background->getHeight () / heightOfOneImage); + inset = 0; +} + +//------------------------------------------------------------------------ +CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset) +: CKnob (size, listener, tag, background, 0, offset), + subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), bInverseBitmap (false) +{ + inset = 0; +} + +//------------------------------------------------------------------------ +CAnimKnob::~CAnimKnob () +{} + +//----------------------------------------------------------------------------------------------- +bool CAnimKnob::isDirty () const +{ + if (!bDirty) + { + CPoint p; + valueToPoint (p); + if (p == lastDrawnPoint) + return false; + } + return CKnob::isDirty (); +} + +//------------------------------------------------------------------------ +void CAnimKnob::draw (CDrawContext *pContext) +{ +// DBG ("CAnimKnob::draw"); + + CPoint where (0, 0); + if (value >= 0.f) + { + CCoord tmp = heightOfOneImage * (subPixmaps - 1); + if (bInverseBitmap) + where.v = (long)((1 - value) * (float)tmp); + else + where.v = (long)(value * (float)tmp); + for (CCoord realY = 0; realY <= tmp; realY += heightOfOneImage) + { + if (where.v < realY) + { + where.v = realY - heightOfOneImage; + if (where.v < 0) + where.v = 0; + break; + } + } + } + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + valueToPoint (lastDrawnPoint); + setDirty (false); +} + +//------------------------------------------------------------------------ +// CVerticalSwitch +//------------------------------------------------------------------------ +/*! @class CVerticalSwitch +Define a switch with a given number of positions, the current position is defined by the position +of the last click on this object (the object is divided in its height by the number of position). +Each position has its subpixmap, each subpixmap is stacked in the given handle pixmap. +By clicking Alt+Left Mouse the default value is used. +*/ +CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset) +{ + heightOfOneImage = size.height (); + subPixmaps = (long)(background->getHeight () / heightOfOneImage); + iMaxPositions = subPixmaps; + + setDefaultValue (0.f); +} + +//------------------------------------------------------------------------ +CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // height of one image in pixel + long iMaxPositions, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), + subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), + iMaxPositions (iMaxPositions) +{ + setDefaultValue (0.f); +} + +//------------------------------------------------------------------------ +CVerticalSwitch::~CVerticalSwitch () +{} + +//------------------------------------------------------------------------ +void CVerticalSwitch::draw (CDrawContext *pContext) +{ +// DBG ("CVerticalSwitch::draw"); + + if (pBackground) + { + // source position in bitmap + CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f))); + + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void CVerticalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + if (!(button & kLButton)) + return; + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + // check if default value wanted + if (checkDefaultValue (pContext, button)) + return; + + double coef = (double)heightOfOneImage / (double)iMaxPositions; + + // begin of edit parameter + beginEdit (); + do + { + value = (long)((where.v - size.top) / coef) / (float)(iMaxPositions - 1); + if (value > 1.f) + value = 1.f; + else if (value < 0.f) + value = 0.f; + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + while (pContext->getMouseButtons () == button); + + // end of edit parameter + endEdit (); +} + + +//------------------------------------------------------------------------ +// CHorizontalSwitch +//------------------------------------------------------------------------ +/*! @class CHorizontalSwitch +Same as the CVerticalSwitch but horizontal. +*/ +CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset) +{ + heightOfOneImage = size.width (); + subPixmaps = (long)(background->getWidth () / heightOfOneImage); + iMaxPositions = subPixmaps; + + setDefaultValue (0.f); +} + +//------------------------------------------------------------------------ +CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // height of one image in pixel + long iMaxPositions, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), + subPixmaps (subPixmaps), + iMaxPositions (iMaxPositions), + heightOfOneImage (heightOfOneImage) +{ + setDefaultValue (0.f); +} + +//------------------------------------------------------------------------ +CHorizontalSwitch::~CHorizontalSwitch () +{} + +//------------------------------------------------------------------------ +void CHorizontalSwitch::draw (CDrawContext *pContext) +{ +// DBG ("CHorizontalSwitch::draw"); + + if (pBackground) + { + // source position in bitmap + CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f))); + + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void CHorizontalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + // check if default value wanted + if (checkDefaultValue (pContext, button)) + return; + + double coef = (double)pBackground->getWidth () / (double)iMaxPositions; + + // begin of edit parameter + beginEdit (); + do + { + value = (long)((where.h - size.left) / coef) / (float)(iMaxPositions - 1); + if (value > 1.f) + value = 1.f; + else if (value < 0.f) + value = 0.f; + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + while (pContext->getMouseButtons () == button); + + // end of edit parameter + endEdit (); +} + + +//------------------------------------------------------------------------ +// CRockerSwitch +//------------------------------------------------------------------------ +/*! @class CRockerSwitch +Define a rocker switch with 3 states using 3 subpixmaps. +One click on its leftside, then the first subpixmap is displayed. +One click on its rightside, then the third subpixmap is displayed. +When the mouse button is relaxed, the second subpixmap is framed. +*/ +CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) + CBitmap *background, CPoint &offset, const long style) +: CControl (size, listener, tag, background), offset (offset), style (style) +{ + heightOfOneImage = size.width (); +} + +//------------------------------------------------------------------------ +CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset, const long style) +: CControl (size, listener, tag, background), offset (offset), + heightOfOneImage (heightOfOneImage), style (style) +{} + +//------------------------------------------------------------------------ +CRockerSwitch::~CRockerSwitch () +{} + +//------------------------------------------------------------------------ +void CRockerSwitch::draw (CDrawContext *pContext) +{ +// DBG ("CRockerSwitch::draw"); + + CPoint where (offset.h, offset.v); + + if (value == 1.f) + where.v += 2 * heightOfOneImage; + else if (value == 0.f) + where.v += heightOfOneImage; + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void CRockerSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + float fEntryState = value; + + CCoord width_2 = size.width () / 2; + CCoord height_2 = size.height () / 2; + + // begin of edit parameter + beginEdit (); + + if (button) + { + do + { + if (style & kHorizontal) + { + if (where.h >= size.left && where.v >= size.top && + where.h <= (size.left + width_2) && where.v <= size.bottom) + value = -1.0f; + else if (where.h >= (size.left + width_2) && where.v >= size.top && + where.h <= size.right && where.v <= size.bottom) + value = 1.0f; + else + value = fEntryState; + } + else + { + if (where.h >= size.left && where.v >= size.top && + where.h <= size.right && where.v <= (size.top + height_2)) + value = -1.0f; + else if (where.h >= size.left && where.v >= (size.top + height_2) && + where.h <= size.right && where.v <= size.bottom) + value = 1.0f; + else + value = fEntryState; + } + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + while (pContext->getMouseButtons ()); + } + else + { + if (where.h >= size.left && where.v >= size.top && + where.h <= (size.left + width_2) && where.v <= size.bottom) + value = -1.0f; + else if (where.h >= (size.left + width_2) && where.v >= size.top && + where.h <= size.right && where.v <= size.bottom) + value = 1.0f; + + if (listener) + listener->valueChanged (pContext, this); + } + + value = 0.f; // set button to UNSELECTED state + if (listener) + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); +} + +//------------------------------------------------------------------------ +bool CRockerSwitch::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + if (!bMouseEnabled) + return false; + + if (distance > 0) + value = -1.0f; + else + value = 1.0f; + + // begin of edit parameter + beginEdit (); + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + value = 0.0f; // set button to UNSELECTED state + if (listener) + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + + return true; +} + + +//------------------------------------------------------------------------ +// CMovieBitmap +//------------------------------------------------------------------------ +/*! @class CMovieBitmap +A movie pixmap allows to display different subpixmaps according to its current value. +*/ +CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) + : CControl (size, listener, tag, background), offset (offset), + subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage) +{ + heightOfOneImage = size.height (); + subPixmaps = (long)(background->getHeight () / heightOfOneImage); +} + +//------------------------------------------------------------------------ +CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset) + : CControl (size, listener, tag, background), offset (offset), + subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage) +{} + +//------------------------------------------------------------------------ +CMovieBitmap::~CMovieBitmap () +{} + +//------------------------------------------------------------------------ +void CMovieBitmap::draw (CDrawContext *pContext) +{ + // DBG ("CMovieBitmap::draw"); + + CPoint where (offset.h, offset.v); + + if (value > 1.0f) + value = 1.0f; + + if (value > 0.0f) + where.v += heightOfOneImage * (int)(value * (subPixmaps - 1) + 0.5); + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + setDirty (false); +} + + +//------------------------------------------------------------------------ +// CMovieButton +//------------------------------------------------------------------------ +/*! @class CMovieButton +A movie button is a bi-states button with 2 subpixmaps. These subpixmaps are stacked in the pixmap. +*/ +CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), buttonState (value) +{ + heightOfOneImage = size.height (); +} + +//------------------------------------------------------------------------ +CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag, + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset) + : CControl (size, listener, tag, background), offset (offset), + heightOfOneImage (heightOfOneImage), buttonState (value) +{} + +//------------------------------------------------------------------------ +CMovieButton::~CMovieButton () +{} + +//------------------------------------------------------------------------ +void CMovieButton::draw (CDrawContext *pContext) +{ + // DBG ("CMovieButton::draw"); + + CPoint where; + + where.h = 0; + + bounceValue (); + + if (value) + where.v = heightOfOneImage; + else + where.v = 0; + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + buttonState = value; + + setDirty (false); +} + +//------------------------------------------------------------------------ +void CMovieButton::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + // this simulates a real windows button + float fEntryState = value; + + // begin of edit parameter + beginEdit (); + + if (pContext->getMouseButtons ()) + { + do + { + if (where.h >= size.left && + where.v >= size.top && + where.h <= size.right && + where.v <= size.bottom) + value = !fEntryState; + else + value = fEntryState; + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + while (pContext->getMouseButtons () == button); + } + else + { + value = !value; + if (listener) + listener->valueChanged (pContext, this); + } + + // end of edit parameter + endEdit (); + + buttonState = value; +} + + +//------------------------------------------------------------------------ +// CAutoAnimation +//------------------------------------------------------------------------ +/*! @class CAutoAnimation +An auto-animation control contains a given number of subpixmap which can be displayed in loop. +Two functions allows to get the previous or the next subpixmap (these functions increase or decrease the current value of this control). +*/ +// displays bitmaps within a (child-) window +CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), bWindowOpened (false) +{ + heightOfOneImage = size.height (); + subPixmaps = (long)(background->getHeight () / heightOfOneImage); + + totalHeightOfBitmap = heightOfOneImage * subPixmaps; +} + +//------------------------------------------------------------------------ +CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps... + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset) + : CControl (size, listener, tag, background), offset (offset), + subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), + bWindowOpened (false) +{ + totalHeightOfBitmap = heightOfOneImage * subPixmaps; +} + +//------------------------------------------------------------------------ +CAutoAnimation::~CAutoAnimation () +{} + +//------------------------------------------------------------------------ +void CAutoAnimation::draw (CDrawContext *pContext) +{ + // DBG ("CAutoAnimation::draw"); + + if (isWindowOpened ()) + { + CPoint where; + where.v = (long)value + offset.v; + where.h = offset.h; + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void CAutoAnimation::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + if (!isWindowOpened ()) + { + value = 0; + openWindow (); + setDirty (); // force to redraw + if (listener) + listener->valueChanged (pContext, this); + } + else + { + // stop info animation + value = 0; // draw first pic of bitmap + setDirty (); + closeWindow (); + } +} + +//------------------------------------------------------------------------ +void CAutoAnimation::openWindow () +{ + bWindowOpened = true; +} + +//------------------------------------------------------------------------ +void CAutoAnimation::closeWindow () +{ + bWindowOpened = false; +} + +//------------------------------------------------------------------------ +void CAutoAnimation::nextPixmap () +{ + value += heightOfOneImage; + if (value >= (totalHeightOfBitmap - heightOfOneImage)) + value = 0; +} + +//------------------------------------------------------------------------ +void CAutoAnimation::previousPixmap () +{ + value -= heightOfOneImage; + if (value < 0.f) + value = (float)(totalHeightOfBitmap - heightOfOneImage - 1); +} + + +//------------------------------------------------------------------------ +// CSlider +//------------------------------------------------------------------------ +/*! @class CSlider +Define a slider with a given background and handle. +The range of variation of the handle should be defined. +By default the handler is drawn with transparency (white color). +By clicking Alt+Left Mouse the default value is used. +*/ +CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag, + long iMinPos, // min position in pixel + long iMaxPos, // max position in pixel + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) + : CControl (rect, listener, tag, background), offset (offset), pHandle (handle), + pOScreen (0), style (style), bFreeClick (true) +{ + setDrawTransparentHandle (true); + + if (pHandle) + { + pHandle->remember (); + widthOfSlider = pHandle->getWidth (); + heightOfSlider = pHandle->getHeight (); + } + else + { + widthOfSlider = 1; + heightOfSlider = 1; + } + + widthControl = size.width (); + heightControl = size.height (); + + if (style & kHorizontal) + { + minPos = iMinPos - size.left; + rangeHandle = iMaxPos - iMinPos; + CPoint p (0, 0); + setOffsetHandle (p); + } + else + { + minPos = iMinPos - size.top; + rangeHandle = iMaxPos - iMinPos; + CPoint p (0, 0); + setOffsetHandle (p); + } + + zoomFactor = 10.f; + + setWantsFocus (true); +} + +//------------------------------------------------------------------------ +CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long _rangeHandle, // size of handle range + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) +: CControl (rect, listener, tag, background), offset (offset), pHandle (handle), + pOScreen (0), style (style), minPos (0), bFreeClick (true) +{ + setDrawTransparentHandle (true); + + if (pHandle) + { + pHandle->remember (); + widthOfSlider = pHandle->getWidth (); + heightOfSlider = pHandle->getHeight (); + } + else + { + widthOfSlider = 1; + heightOfSlider = 1; + } + + widthControl = size.width (); + heightControl = size.height (); + if (style & kHorizontal) + rangeHandle = _rangeHandle - widthOfSlider; + else + rangeHandle = _rangeHandle - heightOfSlider; + + setOffsetHandle (offsetHandle); + + zoomFactor = 10.f; + + setWantsFocus (true); +} + +//------------------------------------------------------------------------ +CSlider::~CSlider () +{ + if (pHandle) + pHandle->forget (); +} + +//------------------------------------------------------------------------ +void CSlider::setOffsetHandle (CPoint &val) +{ + offsetHandle = val; + + if (style & kHorizontal) + { + minTmp = offsetHandle.h + minPos; + maxTmp = minTmp + rangeHandle + widthOfSlider; + } + else + { + minTmp = offsetHandle.v + minPos; + maxTmp = minTmp + rangeHandle + heightOfSlider; + } +} + +//----------------------------------------------------------------------------- +bool CSlider::attached (CView *parent) +{ + if (pOScreen) + delete pOScreen; + + pOScreen = 0; // @TODO - faking offscreen +// pOScreen = new COffscreenContext (getFrame (), widthControl, heightControl, kBlackCColor); + + return CControl::attached (parent); +} + +//----------------------------------------------------------------------------- +bool CSlider::removed (CView *parent) +{ + if (pOScreen) + { + delete pOScreen; + pOScreen = 0; + } + return CControl::removed (parent); +} + +//------------------------------------------------------------------------ +void CSlider::draw (CDrawContext *pContext) +{ + // DBG ("CSlider::draw"); + + CDrawContext* drawContext = pOScreen ? pOScreen : pContext; + + if (pOScreen && bTransparencyEnabled) + pOScreen->copyTo (pContext, size); + + float fValue; + if (style & kLeft || style & kTop) + fValue = value; + else + fValue = 1.f - value; + + // (re)draw background + CRect rect (0, 0, widthControl, heightControl); + if (!pOScreen) + rect.offset (size.left, size.top); + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (drawContext, rect, offset); + else + pBackground->draw (drawContext, rect, offset); + } + + // calc new coords of slider + CRect rectNew; + if (style & kHorizontal) + { + rectNew.top = offsetHandle.v; + rectNew.bottom = rectNew.top + heightOfSlider; + + rectNew.left = offsetHandle.h + (int)(fValue * rangeHandle); + rectNew.left = (rectNew.left < minTmp) ? minTmp : rectNew.left; + + rectNew.right = rectNew.left + widthOfSlider; + rectNew.right = (rectNew.right > maxTmp) ? maxTmp : rectNew.right; + } + else + { + rectNew.left = offsetHandle.h; + rectNew.right = rectNew.left + widthOfSlider; + + rectNew.top = offsetHandle.v + (int)(fValue * rangeHandle); + rectNew.top = (rectNew.top < minTmp) ? minTmp : rectNew.top; + + rectNew.bottom = rectNew.top + heightOfSlider; + rectNew.bottom = (rectNew.bottom > maxTmp) ? maxTmp : rectNew.bottom; + } + if (!pOScreen) + rectNew.offset (size.left, size.top); + + // draw slider at new position + if (pHandle) + { + if (bDrawTransparentEnabled) + pHandle->drawTransparent (drawContext, rectNew); + else + pHandle->draw (drawContext, rectNew); + } + + if (pOScreen) + pOScreen->copyFrom (pContext, size); + + setDirty (false); +} + +//------------------------------------------------------------------------ +void CSlider::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + // check if default value wanted + if (checkDefaultValue (pContext, button)) + return; + + // allow left mousebutton only + if (!(button & kLButton)) + return; + + CCoord delta; + if (style & kHorizontal) + delta = size.left + offsetHandle.h; + else + delta = size.top + offsetHandle.v; + if (!bFreeClick) + { + float fValue; + if (style & kLeft || style & kTop) + fValue = value; + else + fValue = 1.f - value; + CCoord actualPos; + CRect rect; + + if (style & kHorizontal) + { + actualPos = offsetHandle.h + (int)(fValue * rangeHandle) + size.left; + + rect.left = actualPos; + rect.top = size.top + offsetHandle.v; + rect.right = rect.left + widthOfSlider; + rect.bottom = rect.top + heightOfSlider; + + if (!where.isInside (rect)) + return; + else + delta += where.h - actualPos; + } + else + { + actualPos = offsetHandle.v + (int)(fValue * rangeHandle) + size.top; + + rect.left = size.left + offsetHandle.h; + rect.top = actualPos; + rect.right = rect.left + widthOfSlider; + rect.bottom = rect.top + heightOfSlider; + + if (!where.isInside (rect)) + return; + else + delta += where.v - actualPos; + } + } + else + { + if (style & kHorizontal) + delta += widthOfSlider / 2 - 1; + else + delta += heightOfSlider / 2 - 1; + } + + float oldVal = value; + long oldButton = button; + + // begin of edit parameter + beginEdit (); + + while (1) + { + button = pContext->getMouseButtons (); + if (!(button & kLButton)) + break; + + if ((oldButton != button) && (button & kShift)) + { + oldVal = value; + oldButton = button; + } + else if (!(button & kShift)) + oldVal = value; + + if (style & kHorizontal) + value = (float)(where.h - delta) / (float)rangeHandle; + else + value = (float)(where.v - delta) / (float)rangeHandle; + + if (style & kRight || style & kBottom) + value = 1.f - value; + + if (button & kShift) + value = oldVal + ((value - oldVal) / zoomFactor); + bounceValue (); + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + + // end of edit parameter + endEdit (); +} + +//------------------------------------------------------------------------ +bool CSlider::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + if (!bMouseEnabled) + return false; + + long buttons = pContext->getMouseButtons (); + if (buttons & kShift) + value += 0.1f * distance * wheelInc; + else + value += distance * wheelInc; + bounceValue (); + + if (isDirty () && listener) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); + } + + return true; +} + +//------------------------------------------------------------------------ +long CSlider::onKeyDown (VstKeyCode& keyCode) +{ + switch (keyCode.virt) + { + case VKEY_UP : + case VKEY_RIGHT : + case VKEY_DOWN : + case VKEY_LEFT : + { + float distance = 1.f; + if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT) + distance = -distance; + + if (keyCode.modifier & MODIFIER_SHIFT) + value += 0.1f * distance * wheelInc; + else + value += distance * wheelInc; + bounceValue (); + + if (isDirty () && listener) + { + // begin of edit parameter + beginEdit (); + + listener->valueChanged (0, this); + + // end of edit parameter + endEdit (); + } + } return 1; + } + return -1; +} + +//------------------------------------------------------------------------ +void CSlider::setHandle (CBitmap *_pHandle) +{ + if (pHandle) + pHandle->forget (); + pHandle = _pHandle; + if (pHandle) + { + pHandle->remember (); + widthOfSlider = pHandle->getWidth (); + heightOfSlider = pHandle->getHeight (); + } +} + + +//------------------------------------------------------------------------ +// CVerticalSlider +//------------------------------------------------------------------------ +/*! @class CVerticalSlider +This is the vertical slider. See CSlider. +*/ +CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag, + long iMinPos, // min position in pixel + long iMaxPos, // max position in pixel + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kLeft, kRight) + : CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kVertical) +{} + +//------------------------------------------------------------------------ +CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long rangeHandle, // size of handle range + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kLeft, kRight) +: CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kVertical) +{} + + +//------------------------------------------------------------------------ +// CHorizontalSlider +//------------------------------------------------------------------------ +/*! @class CHorizontalSlider +This is the horizontal slider. See CSlider. +*/ +CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag, + long iMinPos, // min Y position in pixel + long iMaxPos, // max Y position in pixel + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kLeft, kRight) + : CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kHorizontal) +{} + +//------------------------------------------------------------------------ +CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long rangeHandle, // size of handle range + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style) // style (kLeft, kRight) +: CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kHorizontal) +{} + + +//------------------------------------------------------------------------ +// CSpecialDigit +//------------------------------------------------------------------------ +/*! @class CSpecialDigit +Can be used to display a counter with maximum 7 digits. +All digit have the same size and are stacked in height in the pixmap. +*/ +CSpecialDigit::CSpecialDigit (const CRect &size, + CControlListener *listener, + long tag, // tag identifier + long dwPos, // actual value + long iNumbers, // amount of numbers (max 7) + long *xpos, // array of all XPOS + long *ypos, // array of all YPOS + long width, // width of ONE number + long height, // height of ONE number + CBitmap *background) // bitmap numbers + : CControl (size, listener, tag, background), + iNumbers (iNumbers), width (width), height (height) +{ + setValue ((float)dwPos); // actual value + + if (iNumbers > 7) + iNumbers = 7; + + if (xpos == NULL) + { + // automatically init xpos/ypos if not provided by caller + const int numw = (const int)background->getWidth(); + int x = (int)size.left; + for (long i = 0; i < iNumbers; i++) + { + this->xpos[i] = x; + this->ypos[i] = (long)size.top; + x += numw; + } + } + else + { + // store coordinates of x/y pos of each digit + for (long i = 0; i < iNumbers; i++) + { + this->xpos[i] = xpos[i]; + this->ypos[i] = ypos[i]; + } + } + + setMax ((float)pow (10., (double)iNumbers) - 1.0f); + setMin (0.0f); +} + +//------------------------------------------------------------------------ +CSpecialDigit::~CSpecialDigit () +{} + +//------------------------------------------------------------------------ +void CSpecialDigit::draw (CDrawContext *pContext) +{ +// DBG ("CSpecialDigit::draw"); + + CPoint where; + CRect rectDest; + long i, j; + long dwValue; + long one_digit[16]; + + if ((long)value >= getMax ()) + dwValue = (long)getMax (); + else if ((long)value < getMin ()) + dwValue = (long)getMin (); + else + dwValue = (long)value; + + for (i = 0, j = ((long)getMax () + 1) / 10; i < iNumbers; i++, j /= 10) + { + one_digit[i] = dwValue / j; + dwValue -= (one_digit[i] * j); + } + + where.h = 0; + for (i = 0; i < iNumbers; i++) + { + j = one_digit[i]; + if (j > 9) + j = 9; + + rectDest.left = xpos[i]; + rectDest.top = ypos[i]; + + rectDest.right = rectDest.left + width; + rectDest.bottom = rectDest.top + height; + + // where = src from bitmap + where.v = j * height; + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, rectDest, where); + else + pBackground->draw (pContext, rectDest, where); + } + } + + setDirty (false); +} + +//------------------------------------------------------------------------ +float CSpecialDigit::getNormValue () const +{ + float fTemp; + fTemp = value / getMax (); + if (fTemp > 1.0f) + fTemp = 1.0f; + else if (fTemp < 0.0f) + fTemp = 0.0f; + + return fTemp; +} + + +//------------------------------------------------------------------------ +// CKickButton +//------------------------------------------------------------------------ +/*! @class CKickButton +Define a button with 2 states using 2 subpixmaps. +One click on it, then the second subpixmap is displayed. +When the mouse button is relaxed, the first subpixmap is framed. +*/ +CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset) +{ + heightOfOneImage = size.height (); +} + +//------------------------------------------------------------------------ +CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag, + CCoord heightOfOneImage, // height of one image in pixel + CBitmap *background, CPoint &offset) +: CControl (size, listener, tag, background), offset (offset), + heightOfOneImage (heightOfOneImage) +{} + +//------------------------------------------------------------------------ +CKickButton::~CKickButton () +{} + +//------------------------------------------------------------------------ +void CKickButton::draw (CDrawContext *pContext) +{ + // DBG ("CKickButton::draw"); + + CPoint where (offset.h, offset.v); + + bounceValue (); + + if (value) + where.v += heightOfOneImage; + + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size, where); + else + pBackground->draw (pContext, size, where); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +void CKickButton::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + // this simulates a real windows button + float fEntryState = value; + + // begin of edit parameter + beginEdit (); + + if (pContext->getMouseButtons () == kLButton) + { + do + { + if (where.h >= size.left && where.v >= size.top && + where.h <= size.right && where.v <= size.bottom) + value = !fEntryState; + else + value = fEntryState; + + if (isDirty () && listener) + listener->valueChanged (pContext, this); + + getMouseLocation (pContext, where); + + doIdleStuff (); + } + while (pContext->getMouseButtons () == kLButton); + } + else + { + value = !value; + if (listener) + listener->valueChanged (pContext, this); + } + + value = 0.0f; // set button to UNSELECTED state + if (listener) + listener->valueChanged (pContext, this); + + // end of edit parameter + endEdit (); +} + + +//------------------------------------------------------------------------ +// CSplashScreen +//------------------------------------------------------------------------ +/*! @class CSplashScreen +One click on its activated region and its pixmap is displayed, in this state the other control can not be used, +an another click on the displayed area reinstalls the normal frame. +This can be used to display a help view over the other views. +*/ +// one click draw its pixmap, an another click redraw its parent +CSplashScreen::CSplashScreen (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, + CRect &toDisplay, + CPoint &offset) +: CControl (size, listener, tag, background), + toDisplay (toDisplay), offset (offset), bitmapTransparency (255) +{} + +//------------------------------------------------------------------------ +CSplashScreen::~CSplashScreen () +{} + +//------------------------------------------------------------------------ +void CSplashScreen::setBitmapTransparency (unsigned char transparency) +{ + bitmapTransparency = transparency; + setTransparency (bitmapTransparency != 255); +} + +//------------------------------------------------------------------------ +void CSplashScreen::draw (CDrawContext *pContext) +{ + // DBG ("CSplashScreen::draw"); + + if (value && pBackground) + { + if (bTransparencyEnabled) + { + if (bitmapTransparency) + pBackground->drawAlphaBlend (pContext, toDisplay, offset, bitmapTransparency); + else + pBackground->drawTransparent (pContext, toDisplay, offset); + } + else + pBackground->draw (pContext, toDisplay, offset); + } + setDirty (false); +} + +//------------------------------------------------------------------------ +bool CSplashScreen::hitTest (const CPoint& where, const long buttons) +{ + bool result = CView::hitTest (where, buttons); + if (result && !(buttons & kLButton)) + return false; + return result; +} + +//------------------------------------------------------------------------ +void CSplashScreen::mouse (CDrawContext *pContext, CPoint &where, long button) +{ + if (!bMouseEnabled) + return; + + if (button == -1) button = pContext->getMouseButtons (); + + if (listener && button & (kAlt | kShift | kControl | kApple)) + { + if (listener->controlModifierClicked (pContext, this, button) != 0) + return; + } + + if (!(button & kLButton)) + return; + + value = !value; + if (value) + { + if (getFrame () && getFrame ()->setModalView (this)) + { + keepSize = size; + size = toDisplay; + mouseableArea = size; +// draw (pContext); + if (listener) + listener->valueChanged (pContext, this); + } + setDirty (); + } + else + { + size = keepSize; + mouseableArea = size; + if (listener) + listener->valueChanged (pContext, this); + if (getFrame ()) + { + getFrame ()->setDirty (true); + getFrame ()->setModalView (NULL); + } + } +} + +//------------------------------------------------------------------------ +void CSplashScreen::unSplash () +{ + setDirty (); + value = 0.f; + + size = keepSize; + if (getFrame ()) + { + if (getFrame ()->getModalView () == this) + { + getFrame ()->setModalView (NULL); + getFrame ()->redraw (); + } + } +} + +//------------------------------------------------------------------------ +// CVuMeter +//------------------------------------------------------------------------ +CVuMeter::CVuMeter (const CRect &size, CBitmap *onBitmap, CBitmap *offBitmap, + long nbLed, const long style) + : CControl (size, 0, 0), + onBitmap (onBitmap), offBitmap (offBitmap), pOScreen (0), + nbLed (nbLed), style (style) +{ + bUseOffscreen = false; + + setDecreaseStepValue (0.1f); + + if (onBitmap) + onBitmap->remember (); + if (offBitmap) + offBitmap->remember (); + + rectOn (size.left, size.top, size.right, size.bottom); + rectOff (size.left, size.top, size.right, size.bottom); +} + +//------------------------------------------------------------------------ +CVuMeter::~CVuMeter () +{ + if (onBitmap) + onBitmap->forget (); + if (offBitmap) + offBitmap->forget (); +} + +//------------------------------------------------------------------------ +void CVuMeter::setDirty (const bool val) +{ + CView::setDirty (val); +} + +//----------------------------------------------------------------------------- +bool CVuMeter::attached (CView *parent) +{ + if (pOScreen) + delete pOScreen; +/* + if (bUseOffscreen) + { + pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor); + rectOn (0, 0, size.width (), size.height ()); + rectOff (0, 0, size.width (), size.height ()); + } + else +*/ + { + rectOn (size.left, size.top, size.right, size.bottom); + rectOff (size.left, size.top, size.right, size.bottom); + } + + return CControl::attached (parent); +} + +//------------------------------------------------------------------------ +void CVuMeter::setUseOffscreen (bool val) +{ +// bUseOffscreen = val; // @TODO - faking offscreen + bUseOffscreen = false; +} + +//----------------------------------------------------------------------------- +bool CVuMeter::removed (CView *parent) +{ + if (pOScreen) + { + delete pOScreen; + pOScreen = 0; + } + return CControl::removed (parent); +} + +//------------------------------------------------------------------------ +void CVuMeter::draw (CDrawContext *_pContext) +{ + // DBG ("CVuMeter::draw"); + + if (!onBitmap) + return; + + CPoint pointOn; + CPoint pointOff; + CDrawContext *pContext = _pContext; + + bounceValue (); + + float newValue = oldValue - decreaseValue; + if (newValue < value) + newValue = value; + oldValue = newValue; + +/* + if (bUseOffscreen) + { + if (!pOScreen) + { + pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor); + rectOn (0, 0, size.width (), size.height ()); + rectOff (0, 0, size.width (), size.height ()); + } + pContext = pOScreen; + } +*/ + + if (style & kHorizontal) + { + CCoord tmp = (long)(((long)(nbLed * newValue + 0.5f) / (float)nbLed) * onBitmap->getWidth ()); + pointOff (tmp, 0); + if (!bUseOffscreen) + tmp += size.left; + + rectOff.left = tmp; + rectOn.right = tmp; + } + else + { + CCoord tmp = (long)(((long)(nbLed * (getMax () - newValue) + 0.5f) / (float)nbLed) * onBitmap->getHeight ()); + pointOn (0, tmp); + if (!bUseOffscreen) + tmp += size.top; + + rectOff.bottom = tmp; + rectOn.top = tmp; + } + + if (offBitmap) + { + if (bTransparencyEnabled) + offBitmap->drawTransparent (pContext, rectOff, pointOff); + else + offBitmap->draw (pContext, rectOff, pointOff); + } + + if (bTransparencyEnabled) + onBitmap->drawTransparent (pContext, rectOn, pointOn); + else + onBitmap->draw (pContext, rectOn, pointOn); + + if (pOScreen) + pOScreen->copyFrom (_pContext, size); + + setDirty (false); +} + +} // namespace VSTGUI + +//------------------------------------------------------------------------ +// END. +//------------------------------------------------------------------------ diff --git a/vstgui/vstcontrols.h b/vstgui/vstcontrols.h new file mode 100644 index 0000000..0505f82 --- /dev/null +++ b/vstgui/vstcontrols.h @@ -0,0 +1,994 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * + * Which was based on: + * ---------------------------------------------------------------------------- + * VSTGUI: Graphical User Interface Framework for VST plugins + * Version 3.0 $Date: 2005/08/12 12:45:00 $ + * ---------------------------------------------------------------------------- + * VSTGUI LICENSE + * 2004, Steinberg Media Technologies, All Rights Reserved + * ---------------------------------------------------------------------------- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Steinberg Media Technologies nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef __vstcontrols__ +#define __vstcontrols__ + +#ifndef __vstgui__ +#include "vstgui.h" +#endif + +//------------------ +// defines +//------------------ +#ifndef kPI +#define kPI 3.14159265358979323846 +#endif + +#ifndef k2PI +#define k2PI 6.28318530717958647692 +#endif + +#ifndef kPI_2 +#define kPI_2 1.57079632679489661923f +#endif + +#ifndef kPI_4 +#define kPI_4 0.78539816339744830962 +#endif + +#ifndef kE +#define kE 2.7182818284590452354 +#endif + +#ifndef kLN2 +#define kLN2 0.69314718055994530942 +#endif + +#ifndef kSQRT2 +#define kSQRT2 1.41421356237309504880 +#endif + +//------------------ +// CControlEnum type +//------------------ +enum CControlEnum +{ + kHorizontal = 1 << 0, + kVertical = 1 << 1, + kShadowText = 1 << 2, + kLeft = 1 << 3, + kRight = 1 << 4, + kTop = 1 << 5, + kBottom = 1 << 6, + k3DIn = 1 << 7, + k3DOut = 1 << 8, + kPopupStyle = 1 << 9, + kCheckStyle = 1 << 10, + kMultipleCheckStyle, + kNoTextStyle = 1 << 11, + kNoDrawStyle = 1 << 12, + kDoubleClickStyle = 1 << 13, + kNoFrame = 1 << 14 +}; + +//--------------------------- +// Some defines for Menu item +//--------------------------- +#define kMenuTitle "-T" +#define kMenuSeparator "-" +#define kMenuDisable "-G" +#define kMenuSubMenu "-M" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class CControlListener +{ +public: + virtual ~CControlListener () {} + + virtual void valueChanged (VSTGUI::CDrawContext *pContext, VSTGUI::CControl *pControl) = 0; + virtual long controlModifierClicked (VSTGUI::CDrawContext *pContext, VSTGUI::CControl *pControl, long button) { return 0; } // return 1 if you want the control to not handle it, otherwise 0 +}; + +class AudioEffectX; + +//----------------------------------------------------------------------------- +namespace VSTGUI { +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// CControl Declaration +//! base class of all VSTGUI controls +//----------------------------------------------------------------------------- +class CControl : public CView +{ +public: + CControl (const CRect &size, CControlListener *listener = 0, long tag = 0, + CBitmap *pBackground = 0); + virtual ~CControl (); + + virtual void draw (CDrawContext *pContext) = 0; + virtual void doIdleStuff () { if (pParentFrame) pParentFrame->doIdleStuff (); } + + virtual void setValue (float val) { value = val; } + virtual float getValue () const { return value; }; + + virtual void setMin (float val) { vmin = val; } + virtual float getMin () const { return vmin; } + virtual void setMax (float val) { vmax = val; } + virtual float getMax () const { return vmax; } + + virtual void setOldValue (float val) { oldValue = val; } + virtual float getOldValue (void) const { return oldValue; } + virtual void setDefaultValue (float val) { defaultValue = val; } + virtual float getDefaultValue (void) const { return defaultValue; } + + virtual void setTag (long val) { tag = val; } + virtual long getTag () const { return tag; } + + virtual bool isDirty () const; + virtual void setDirty (const bool val = true); + + virtual void beginEdit (); + virtual void endEdit (); + + virtual void setBackOffset (CPoint &offset); + virtual void copyBackOffset (); + + virtual void setWheelInc (float val) { wheelInc = val; } + virtual float getWheelInc () const { return wheelInc; } + + virtual void bounceValue (); + virtual bool checkDefaultValue (CDrawContext *pContext, long button); + + CControlListener* getListener () const { return listener; } + void setListener (CControlListener* l) { listener = l; } + bool isDoubleClick (); + + CLASS_METHODS(CControl, CView) + +protected: + CControlListener *listener; + long tag; + float oldValue; + float defaultValue; + float value; + float vmin; + float vmax; + float wheelInc; + + long lastTicks; + long delta; + + CPoint backOffset; +}; + + +//----------------------------------------------------------------------------- +// COnOffButton Declaration +//! a button control with 2 states +//----------------------------------------------------------------------------- +class COnOffButton : public CControl +{ +public: + COnOffButton (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, long style = kPreListenerUpdate); + virtual ~COnOffButton (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + virtual long getStyle () const { return style; } + virtual void setStyle (long newStyle) { style = newStyle; } + + enum { + kPreListenerUpdate, ///< listener will be called after doIdleStuff was called + kPostListenerUpdate ///< listener will be called before doIdleStuff is called + }; + + CLASS_METHODS(COnOffButton, CControl) +protected: + long style; +}; + + +//----------------------------------------------------------------------------- +// CParamDisplay Declaration +//! a parameter display control +//----------------------------------------------------------------------------- +class CParamDisplay : public CControl +{ +public: + CParamDisplay (const CRect &size, CBitmap *background = 0, const long style = 0); + virtual ~CParamDisplay (); + + virtual void setFont (CFont fontID); + CFont getFont () const { return fontID; } + + virtual void setFontColor (CColor color); + CColor getFontColor () const { return fontColor; } + + virtual void setBackColor (CColor color); + CColor getBackColor () const { return backColor; } + + virtual void setFrameColor (CColor color); + CColor getFrameColor () const { return frameColor; } + + virtual void setShadowColor (CColor color); + CColor getShadowColor () const { return shadowColor; } + + virtual void setHoriAlign (CHoriTxtAlign hAlign); + + virtual void setStringConvert (void (*convert) (float value, char *string)); + virtual void setStringConvert (void (*convert) (float value, char *string, void *userDta), + void *userData); + virtual void setString2FloatConvert (void (*convert) (char *string, float &output)); + + virtual void setStyle (long val); + long getStyle () const { return style; } + + virtual void setTxtFace (CTxtFace val); + CTxtFace getTxtFace () const { return txtFace; } + + virtual void draw (CDrawContext *pContext); + + virtual void setTextTransparency (bool val) { bTextTransparencyEnabled = val; } + bool getTextTransparency () const { return bTextTransparencyEnabled; } + + CLASS_METHODS(CParamDisplay, CControl) + +protected: + void drawText (CDrawContext *pContext, char *string, CBitmap *newBack = 0); + + void (*stringConvert) (float value, char *string); + void (*stringConvert2) (float value, char *string, void *userData); + void (*string2FloatConvert) (char *string, float &output); + void *userData; + + CHoriTxtAlign horiTxtAlign; + long style; + + CFont fontID; + CTxtFace txtFace; + CColor fontColor; + CColor backColor; + CColor frameColor; + CColor shadowColor; + bool bTextTransparencyEnabled; +}; + + +//----------------------------------------------------------------------------- +// CLabel Declaration +//! a text label +//----------------------------------------------------------------------------- +class CTextLabel : public CParamDisplay +{ +public: + CTextLabel (const CRect& size, const char* txt = 0, CBitmap* background = 0, const long style = 0); + ~CTextLabel (); + + virtual void setText (const char* txt); + virtual const char* getText () const; + + virtual void draw (CDrawContext *pContext); + + CLASS_METHODS(CTextLabel, CParamDisplay) + +protected: + void freeText (); + char* text; +}; + +//----------------------------------------------------------------------------- +// CTextEdit Declaration +//! a text edit control +//----------------------------------------------------------------------------- +class CTextEdit : public CParamDisplay +{ +public: + CTextEdit (const CRect &size, CControlListener *listener, long tag, const char *txt = 0, + CBitmap *background = 0, const long style = 0); + virtual ~CTextEdit (); + + virtual void setText (char *txt); + virtual void getText (char *txt) const; + + virtual void draw (CDrawContext *pContext); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + virtual void setTextEditConvert (void (*editConvert) (char *input, char *string)); + virtual void setTextEditConvert (void (*editConvert2) (char *input, char *string, + void *userDta), void *userData); + + virtual void takeFocus (CDrawContext *pContext = 0); + virtual void looseFocus (CDrawContext *pContext = 0); + + void *platformFontColor; + void *platformControl; + bool bWasReturnPressed; + + CLASS_METHODS(CTextEdit, CParamDisplay) + +protected: + void *platformFont; + char text[256]; + + void (*editConvert) (char *input, char *string); + void (*editConvert2) (char *input, char *string, void *userData); +}; + + +//----------------------------------------------------------------------------- +// COptionMenuScheme Declaration +//----------------------------------------------------------------------------- +class COptionMenuScheme : public CReferenceCounter +{ +public: + COptionMenuScheme (); + virtual ~COptionMenuScheme (); + + enum { kChecked = 0x01, kDisabled = 0x02, kSelected = 0x04, kSubMenu = 0x08, kTitle = 0x10 }; + + virtual void getItemSize (const char* text, CDrawContext* pContext, CPoint& size); + virtual void drawItem (const char* text, long itemId, long state, CDrawContext* pContext, const CRect& rect); + + void setColors (CColor back, CColor select, CColor text, CColor htext, CColor dtext) + { backgroundColor = back; selectionColor = select; textColor = text; + hiliteTextColor = htext; disableTextColor = dtext;} + + void setFont (CFont f) { font = f; } +protected: + + CColor backgroundColor; + CColor selectionColor; + CColor textColor; + CColor hiliteTextColor; + CColor disableTextColor; + CFont font; + + virtual void drawItemBack (CDrawContext* pContext, const CRect& rect, bool hilite); +}; + +//----------------------------------------------------------------------------- +extern COptionMenuScheme* gOptionMenuScheme; + +//----------------------------------------------------------------------------- +// COptionMenu Declaration +//! a popup menu control +//----------------------------------------------------------------------------- +class COptionMenu : public CParamDisplay +{ +public: + COptionMenu (const CRect &size, CControlListener *listener, long tag, + CBitmap *background = 0, CBitmap *bgWhenClick = 0, + const long style = 0); + virtual ~COptionMenu (); + + enum { MAX_ENTRY = 1024 }; + + virtual void setValue (float val); + virtual bool addEntry (COptionMenu *subMenu, char *txt); + virtual bool addEntry (char *txt, long index = -1); + virtual long getCurrent (char *txt = 0, bool countSeparator = true) const; + virtual bool setCurrent (long index, bool countSeparator = true); + virtual bool getEntry (long index, char *txt) const; + virtual bool setEntry (long index, char *txt); + virtual bool removeEntry (long index); + virtual bool removeAllEntry (); + virtual long getNbEntries () const { return nbEntries; } + virtual long getIndex (char *txt) const; + + virtual bool checkEntry (long index, bool state); + virtual bool checkEntryAlone (long index); + virtual bool isCheckEntry (long index) const; + + virtual void draw (CDrawContext *pContext); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + virtual void takeFocus (CDrawContext *pContext = 0); + virtual void looseFocus (CDrawContext *pContext = 0); + + virtual void setNbItemsPerColumn (long val) { nbItemsPerColumn = val; } + virtual long getNbItemsPerColumn () const { return nbItemsPerColumn; } + + void setCurrentSelected (void *itemSelected); + + long getLastResult () const { return lastResult; } + COptionMenu *getLastItemMenu (long &idxInMenu) const; + + void setScheme (COptionMenuScheme* s) { scheme = s; } + virtual COptionMenuScheme* getScheme () const { return scheme; } + + virtual void setPrefixNumbers (long preCount); + + COptionMenu* getSubMenu (long idx) const; + + CLASS_METHODS(COptionMenu, CParamDisplay) + +protected: + COptionMenu *getItemMenu (long idx, long &idxInMenu, long &offsetIdx); + void removeItems (); + void *appendItems (long &offsetIdx); + + void *platformControl; + + bool allocateMenu (long nb); + bool allocateSubMenu (long nb); + + char **entry; + COptionMenu **submenuEntry; + bool *check; + + void *itemWidget[MAX_ENTRY]; + + long nbEntries; + long nbSubMenus; + long currentIndex; + CBitmap *bgWhenClick; + long lastButton; + long nbItemsPerColumn; + long nbAllocated; + long nbSubMenuAllocated; + long lastResult; + long prefixNumbers; + COptionMenu *lastMenu; + COptionMenuScheme* scheme; +}; + + +//----------------------------------------------------------------------------- +// CKnob Declaration +//! a knob control +//----------------------------------------------------------------------------- +class CKnob : public CControl +{ +public: + CKnob (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CBitmap *handle, const CPoint &offset); + virtual ~CKnob (); + + virtual void draw (CDrawContext *pContext); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); + virtual long onKeyDown (VstKeyCode& keyCode); + + virtual void drawHandle (CDrawContext *pContext); + + virtual void setStartAngle (float val); + virtual float getStartAngle () const { return startAngle; } + + virtual void setRangeAngle (float val); + virtual float getRangeAngle () const { return rangeAngle; } + + virtual void valueToPoint (CPoint &point) const; + virtual float valueFromPoint (CPoint &point) const; + + virtual void setInsetValue (long val) { inset = val; } + + virtual void setColorShadowHandle (CColor color); + virtual void setColorHandle (CColor color); + + virtual void setHandleBitmap (CBitmap *bitmap); + + virtual void setZoomFactor (float val) { zoomFactor = val; } + virtual float getZoomFactor () const { return zoomFactor; } + + CLASS_METHODS(CKnob, CControl) + +protected: + void compute (); + + CPoint offset; + CColor colorHandle, colorShadowHandle; + + CBitmap *pHandle; + long inset; + float startAngle, rangeAngle, halfAngle; + float aCoef, bCoef; + float radius; + float zoomFactor; +}; + +//----------------------------------------------------------------------------- +// CAnimKnob Declaration +//! a bitmap knob control +//----------------------------------------------------------------------------- +class CAnimKnob : public CKnob +{ +public: + CAnimKnob (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CAnimKnob (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset); + virtual ~CAnimKnob (); + + virtual bool isDirty () const; + + virtual void draw (CDrawContext* pContext); + + void setInverseBitmap (bool val) { bInverseBitmap = val; } + + CLASS_METHODS(CAnimKnob, CKnob) + +protected: + long subPixmaps; // number of subPixmaps + CCoord heightOfOneImage; + bool bInverseBitmap; + CPoint lastDrawnPoint; +}; + +//----------------------------------------------------------------------------- +// CVerticalSwitch Declaration +//! a vertical switch control +//----------------------------------------------------------------------------- +class CVerticalSwitch : public CControl +{ +public: + CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // pixel + long iMaxPositions, + CBitmap *background, CPoint &offset); + virtual ~CVerticalSwitch (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + CLASS_METHODS(CVerticalSwitch, CControl) + +protected: + CPoint offset; + long subPixmaps; // number of subPixmaps + CCoord heightOfOneImage; + long iMaxPositions; +}; + + +//----------------------------------------------------------------------------- +// CHorizontalSwitch Declaration +//! a horizontal switch control +//----------------------------------------------------------------------------- +class CHorizontalSwitch : public CControl +{ +public: + CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // pixel + long iMaxPositions, + CBitmap *background, CPoint &offset); + virtual ~CHorizontalSwitch (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + CLASS_METHODS(CHorizontalSwitch, CControl) + +protected: + CPoint offset; + long subPixmaps; // number of subPixmaps + long iMaxPositions; + CCoord heightOfOneImage; +}; + + +//----------------------------------------------------------------------------- +// CRockerSwitch Declaration +//! a switch control with 3 sub bitmaps +//----------------------------------------------------------------------------- +class CRockerSwitch : public CControl +{ +public: + CRockerSwitch (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset, const long style = kHorizontal); + CRockerSwitch (const CRect &size, CControlListener *listener, long tag, + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset, const long style = kHorizontal); + virtual ~CRockerSwitch (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); + + CLASS_METHODS(CRockerSwitch, CControl) + +protected: + CPoint offset; + CCoord heightOfOneImage; + long style; +}; + + +//----------------------------------------------------------------------------- +// CMovieBitmap Declaration +//! a bitmap control that displays different bitmaps according to its current value +//----------------------------------------------------------------------------- +class CMovieBitmap : public CControl +{ +public: + CMovieBitmap (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CMovieBitmap (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset); + virtual ~CMovieBitmap (); + + virtual void draw (CDrawContext*); + + CLASS_METHODS(CMovieBitmap, CControl) + +protected: + CPoint offset; + long subPixmaps; // number of subPixmaps + CCoord heightOfOneImage; +}; + + +//----------------------------------------------------------------------------- +// CMovieButton Declaration +//! a bi-states button with 2 subbitmaps +//----------------------------------------------------------------------------- +class CMovieButton : public CControl +{ +public: + CMovieButton (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CMovieButton (const CRect &size, CControlListener *listener, long tag, + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset); + virtual ~CMovieButton (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + CLASS_METHODS(CMovieButton, CControl) + +protected: + CPoint offset; + CCoord heightOfOneImage; + float buttonState; +}; + + +//----------------------------------------------------------------------------- +// CAutoAnimation Declaration +//! +//----------------------------------------------------------------------------- +class CAutoAnimation : public CControl +{ +public: + CAutoAnimation (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CAutoAnimation (const CRect &size, CControlListener *listener, long tag, + long subPixmaps, // number of subPixmaps... + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset); + virtual ~CAutoAnimation (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + virtual void openWindow (void); + virtual void closeWindow (void); + + virtual void nextPixmap (void); + virtual void previousPixmap (void); + + bool isWindowOpened () const { return bWindowOpened; } + + CLASS_METHODS(CAutoAnimation, CControl) + +protected: + CPoint offset; + + long subPixmaps; + CCoord heightOfOneImage; + CCoord totalHeightOfBitmap; + + bool bWindowOpened; +}; + + +//----------------------------------------------------------------------------- +// CSlider Declaration +//! a slider control +//----------------------------------------------------------------------------- +class CSlider : public CControl +{ +public: + CSlider (const CRect &size, CControlListener *listener, long tag, + long iMinPos, // min position in pixel + long iMaxPos, // max position in pixel + CBitmap *handle, // handle bitmap + CBitmap *background, // background bitmap + CPoint &offset, // offset in the background + const long style = kLeft|kHorizontal); // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) + + CSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long rangeHandle, // size of handle range + CBitmap *handle, // handle bitmap + CBitmap *background, // background bitmap + CPoint &offset, // offset in the background + const long style = kLeft|kHorizontal); // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) + + virtual ~CSlider (); + + virtual bool attached (CView *parent); + virtual bool removed (CView *parent); + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); + virtual long onKeyDown (VstKeyCode& keyCode); + + virtual void setDrawTransparentHandle (bool val) { bDrawTransparentEnabled = val; } + virtual void setFreeClick (bool val) { bFreeClick = val; } + virtual bool getFreeClick () const { return bFreeClick; } + virtual void setOffsetHandle (CPoint &val); + + virtual void setHandle (CBitmap* pHandle); + virtual CBitmap *getHandle () const { return pHandle; } + + virtual void setZoomFactor (float val) { zoomFactor = val; } + virtual float getZoomFactor () const { return zoomFactor; } + + CLASS_METHODS(CSlider, CControl) + +protected: + CPoint offset; + CPoint offsetHandle; + + CBitmap *pHandle; + COffscreenContext *pOScreen; + + long style; + + CCoord widthOfSlider; // size of the handle-slider + CCoord heightOfSlider; + CCoord rangeHandle; + CCoord minTmp; + CCoord maxTmp; + CCoord minPos; + CCoord widthControl; + CCoord heightControl; + float zoomFactor; + + bool bDrawTransparentEnabled; + bool bFreeClick; +}; + +//----------------------------------------------------------------------------- +// CVerticalSlider Declaration +//! a vertical slider control +//----------------------------------------------------------------------------- +class CVerticalSlider : public CSlider +{ +public: + CVerticalSlider (const CRect &size, CControlListener *listener, long tag, + long iMinPos, // min Y position in pixel + long iMaxPos, // max Y position in pixel + CBitmap *handle, // bitmap slider + CBitmap *background, // bitmap background + CPoint &offset, // offset in the background + const long style = kBottom); // style (kBottom, kTop)) + + CVerticalSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long rangeHandle, // size of handle range + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style = kBottom); // style (kBottom, kTop) +}; + +//----------------------------------------------------------------------------- +// CHorizontalSlider Declaration +//! a horizontal slider control +//----------------------------------------------------------------------------- +class CHorizontalSlider : public CSlider +{ +public: + CHorizontalSlider (const CRect &size, CControlListener *listener, long tag, + long iMinPos, // min X position in pixel + long iMaxPos, // max X position in pixel + CBitmap *handle, // bitmap slider + CBitmap *background, // bitmap background + CPoint &offset, // offset in the background + const long style = kRight); // style (kRight, kLeft) + + CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag, + CPoint &offsetHandle, // handle offset + long rangeHandle, // size of handle range + CBitmap *handle, // bitmap of slider + CBitmap *background, // bitmap of background + CPoint &offset, // offset in the background + const long style = kRight); // style (kRight, kLeft) +}; + + +//----------------------------------------------------------------------------- +// CSpecialDigit Declaration +//! special display with custom numbers (0...9) +//----------------------------------------------------------------------------- +class CSpecialDigit : public CControl +{ +public: + CSpecialDigit (const CRect &size, CControlListener *listener, long tag, // tag identifier + long dwPos, // actual value + long iNumbers, // amount of numbers (max 7) + long *xpos, // array of all XPOS + long *ypos, // array of all YPOS + long width, // width of ONE number + long height, // height of ONE number + CBitmap *background); // bitmap numbers + virtual ~CSpecialDigit (); + + virtual void draw (CDrawContext*); + + virtual float getNormValue (void) const; + + CLASS_METHODS(CSpecialDigit, CControl) + +protected: + long iNumbers; // amount of numbers + long xpos[7]; // array of all XPOS, max 7 possible + long ypos[7]; // array of all YPOS, max 7 possible + long width; // width of ONE number + long height; // height of ONE number +}; + + +//----------------------------------------------------------------------------- +// CKickButton Declaration +//! +//----------------------------------------------------------------------------- +class CKickButton : public CControl +{ +public: + CKickButton (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, CPoint &offset); + CKickButton (const CRect &size, CControlListener *listener, long tag, + CCoord heightOfOneImage, // pixel + CBitmap *background, CPoint &offset); + virtual ~CKickButton (); + + virtual void draw (CDrawContext*); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + + CLASS_METHODS(CKickButton, CControl) + +protected: + CPoint offset; + CCoord heightOfOneImage; +}; + + +//----------------------------------------------------------------------------- +// CSplashScreen Declaration +//! +//----------------------------------------------------------------------------- +class CSplashScreen : public CControl +{ +public: + CSplashScreen (const CRect &size, CControlListener *listener, long tag, + CBitmap *background, + CRect &toDisplay, + CPoint &offset); + virtual ~CSplashScreen (); + + virtual void draw (CDrawContext*); + virtual bool hitTest (const CPoint& where, const long buttons = -1); + virtual void mouse (CDrawContext *pContext, CPoint &where, long button = -1); + virtual void unSplash (); + + void setBitmapTransparency (unsigned char transparency); + + CLASS_METHODS(CSplashScreen, CControl) + +protected: + CRect toDisplay; + CRect keepSize; + CPoint offset; + unsigned char bitmapTransparency; +}; + + +//----------------------------------------------------------------------------- +// CVuMeter Declaration +//! +//----------------------------------------------------------------------------- +class CVuMeter : public CControl +{ +public: + CVuMeter (const CRect& size, CBitmap *onBitmap, CBitmap *offBitmap, + long nbLed, const long style = kVertical); + virtual ~CVuMeter (); + + virtual void setDecreaseStepValue (float value) { decreaseValue = value; } + + virtual bool attached (CView *parent); + virtual bool removed (CView *parent); + virtual void draw (CDrawContext *pContext); + virtual void setDirty (const bool val = true); + + void setUseOffscreen (bool val = true); + bool getUseOffscreen () const { return bUseOffscreen; } + + CLASS_METHODS(CVuMeter, CControl) + +protected: + CBitmap *onBitmap; + CBitmap *offBitmap; + COffscreenContext *pOScreen; + + long nbLed; + long style; + float decreaseValue; + bool bUseOffscreen; + + CRect rectOn; + CRect rectOff; +}; + + +#if !PLUGGUI +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class CFileSelector +{ +public: + CFileSelector (AudioEffectX* effect); + virtual ~CFileSelector (); + + long run (VstFileSelect *vstFileSelect); + +protected: + AudioEffectX* effect; + VstFileSelect *vstFileSelect; +}; +#endif + +} // namespace VSTGUI + +#endif // __vstcontrol__ diff --git a/vstgui/vstgui.cpp b/vstgui/vstgui.cpp new file mode 100644 index 0000000..ab52007 --- /dev/null +++ b/vstgui/vstgui.cpp @@ -0,0 +1,3996 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * + * Which was based on: + * ---------------------------------------------------------------------------- + * VSTGUI: Graphical User Interface Framework for VST plugins + * Version 3.0 $Date: 2005/08/12 12:45:00 $ + * ---------------------------------------------------------------------------- + * VSTGUI LICENSE + * 2004, Steinberg Media Technologies, All Rights Reserved + * ---------------------------------------------------------------------------- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Steinberg Media Technologies nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#include "vstgui.h" +#include "audioeffectx.h" +#include "vstkeycode.h" + +//----------------------------------------------------------------------------- +// Some defines +//----------------------------------------------------------------------------- +#ifdef DEBUG + #define DBG(x) std::cout << x << std::endl; + #define assertfalse assert (false); +#else + #define DBG(x) + #define assertfalse +#endif + +#define USE_ALPHA_BLEND 0 +#define USE_CLIPPING_DRAWRECT 1 +#define NEW_UPDATE_MECHANISM 1 + + +//----------------------------------------------------------------------------- +// our global display +Display* display = 0; + + +//----------------------------------------------------------------------------- +// AEffGUIEditor Implementation +//----------------------------------------------------------------------------- +#define kIdleRate 100 // host idle rate in ms +#define kIdleRate2 50 +#define kIdleRateMin 4 // minimum time between 2 idles in ms + +//----------------------------------------------------------------------------- +VstInt32 AEffGUIEditor::knobMode = kCircularMode; + +//----------------------------------------------------------------------------- +AEffGUIEditor::AEffGUIEditor (AudioEffect* effect) +: AEffEditor (effect), + lLastTicks (0), + inIdleStuff (false), + bundlePath(NULL), + frame (0) +{ + rect.left = rect.top = rect.right = rect.bottom = 0; + lLastTicks = getTicks (); + + effect->setEditor (this); +} + +//----------------------------------------------------------------------------- +AEffGUIEditor::~AEffGUIEditor () +{ +} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::setParameter (VstInt32 index, float value) +{} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::beginEdit (VstInt32 index) +{ + ((AudioEffectX*)effect)->beginEdit (index); +} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::endEdit (VstInt32 index) +{ + ((AudioEffectX*)effect)->endEdit (index); +} + +//----------------------------------------------------------------------------- +#if VST_2_1_EXTENSIONS +long AEffGUIEditor::onKeyDown (VstKeyCode& keyCode) +{ + return frame && frame->onKeyDown (keyCode) == 1 ? 1 : 0; +} + +//----------------------------------------------------------------------------- +long AEffGUIEditor::onKeyUp (VstKeyCode& keyCode) +{ + return frame && frame->onKeyUp (keyCode) == 1 ? 1 : 0; +} + +//----------------------------------------------------------------------------- +long AEffGUIEditor::setKnobMode (VstInt32 val) +{ + knobMode = val; + return 1; +} + +//----------------------------------------------------------------------------- +bool AEffGUIEditor::onWheel (float distance) +{ +/* + if (frame) + { + CDrawContext context (frame, NULL, systemWindow); + CPoint where; + context.getMouseLocation (where); + return frame->onWheel (&context, where, distance); + } +*/ + return false; +} +#endif + +//----------------------------------------------------------------------------- +long AEffGUIEditor::getRect (ERect **ppErect) +{ + *ppErect = ▭ + return 1; +} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::idle () +{ + if (inIdleStuff) + return; + + AEffEditor::idle (); + if (frame) + frame->idle (); +} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::wait (unsigned int ms) +{ + usleep (ms * 1000); +} + +//----------------------------------------------------------------------------- +unsigned int AEffGUIEditor::getTicks () +{ + return _getTicks (); +} + +//----------------------------------------------------------------------------- +void AEffGUIEditor::doIdleStuff () +{ + // get the current time + unsigned int currentTicks = getTicks (); + + // YG TEST idle (); + if (currentTicks < lLastTicks) + { + wait (kIdleRateMin); + + currentTicks += kIdleRateMin; + if (currentTicks < lLastTicks - kIdleRate2) + return; + } + + idle (); + + // save the next time + lLastTicks = currentTicks + kIdleRate; + + inIdleStuff = true; + + if (effect) + effect->masterIdle (); + + inIdleStuff = false; +} + +void AEffGUIEditor::update () +{ + if (frame) + frame->invalidate (CRect (0, 0, rect.right, rect.bottom)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//---For Debugging------------------------ +#if DEBUG +long gNbCBitmap = 0; +long gNbCView = 0; +long gNbCDrawContext = 0; +long gNbCOffscreenContext = 0; +long gBitmapAllocation = 0; +long gNbDC = 0; +#include +void DebugPrint (const char *format, ...); +void DebugPrint (const char *format, ...) +{ + char string[300]; + va_list marker; + va_start (marker, format); + vsprintf (string, format, marker); + if (!strcmp(string, "")) + strcpy (string, "Empty string\n"); + fprintf (stderr, string); +} +#endif +//---End For Debugging------------------------ + +#include +#include +#include +#include +#include +#include + +#define XDRAWPARAM display, (Window)pWindow, (GC) pSystemContext +#define XWINPARAM display, (Window)pFrame->getWindow() +#define XGCPARAM display, (GC) pSystemContext + +// #define XDRAWPARAM display, (Window)pWindow, (GC)pSystemContext +// #define XWINPARAM display, (Window)pWindow +// #define XGCPARAM display, (GC)pSystemContext + +// init the static variable about font +bool gFontInit = false; +XFontStruct *gFontStructs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + +struct SFontTable { const char* name; const char* string; }; + +static SFontTable gFontTable[] = { + {"SystemFont", "-*-fixed-*-*-*--12-*-*-*-*-*-*-*"}, // kSystemFont + {"NormalFontVeryBig", "-*-fixed-*-*-*--18-*-*-*-*-*-*-*"}, // kNormalFontVeryBig + {"NormalFontBig", "-*-fixed-*-*-*--18-*-*-*-*-*-*-*"}, // kNormalFontBig + {"NormalFont", "-*-fixed-*-*-*--12-*-*-*-*-*-*-*"}, // kNormalFont + {"NormalFontSmall", "-*-courier-*-*-*--10-*-*-*-*-*-*-*"}, // kNormalFontSmall + {"NormalFontSmaller", "-*-courier-*-*-*--9-*-*-*-*-*-*-*"}, // kNormalFontSmaller + {"NormalFontVerySmall", "-*-courier-*-*-*--8-*-*-*-*-*-*-*"}, // kNormalFontVerySmall + {"SymbolFont", "-*-fixed-*-*-*--12-*-*-*-*-*-*-*"} // kSymbolFont +}; + +long gStandardFontSize[] = { 12, 18, 18, 12, 10, 9, 8, 13 }; + +// declaration of different local functions +static long convertPoint2Angle (CPoint &pm, CPoint &pt); + +// stuff for color +long CDrawContext::nbNewColor = 0; + +//----------------------------------------------------------------------------- +bool CRect::pointInside (const CPoint& where) const +{ + return where.h >= left && where.h < right && where.v >= top && where.v < bottom; +} + +//----------------------------------------------------------------------------- +bool CRect::isEmpty () const +{ + if (right <= left) + return true; + if (bottom <= top) + return true; + return false; +} + +//----------------------------------------------------------------------------- +void CRect::bound (const CRect& rect) +{ + if (left < rect.left) + left = rect.left; + if (top < rect.top) + top = rect.top; + if (right > rect.right) + right = rect.right; + if (bottom > rect.bottom) + bottom = rect.bottom; + if (bottom < top) + bottom = top; + if (right < left) + right = left; +} + +namespace VSTGUI { + +CColor kTransparentCColor = {255, 255, 255, 0}; +CColor kBlackCColor = { 0, 0, 0, 255}; +CColor kWhiteCColor = {255, 255, 255, 255}; +CColor kGreyCColor = {127, 127, 127, 255}; +CColor kRedCColor = {255, 0, 0, 255}; +CColor kGreenCColor = { 0, 255, 0, 255}; +CColor kBlueCColor = { 0, 0, 255, 255}; +CColor kYellowCColor = {255, 255, 0, 255}; +CColor kMagentaCColor = {255, 0, 255, 255}; +CColor kCyanCColor = { 0, 255, 255, 255}; + +#define kDragDelay 0 + +//----------------------------------------------------------------------------- +// CDrawContext Implementation +//----------------------------------------------------------------------------- +/** + * CDrawContext constructor. + * @param inFrame the parent CFrame + * @param inSystemContext the platform system context, can be NULL + * @param inWindow the platform window object + */ +CDrawContext::CDrawContext (CFrame *inFrame, void *inSystemContext, void *inWindow) +: pSystemContext (inSystemContext) +, pWindow (inWindow) +, pFrame (inFrame) +, fontSize (-1) +, fontStyle (0) +, fontId (kNumStandardFonts) +, frameWidth (0) +, lineStyle (kLineOnOffDash) +, drawMode (kAntialias) +{ +#if DEBUG + gNbCDrawContext++; +#endif + + // initialize values + if (pFrame) + pFrame->getViewSize (clipRect); + else + clipRect (0, 0, 1000, 1000); + + const CColor notInitalized = {0, 0, 0, 0}; + frameColor = notInitalized; + fillColor = notInitalized; + fontColor = notInitalized; + + // offsets use by offscreen + offset (0, 0); + offsetScreen (0, 0); + + // set the current font + if (pSystemContext) + { + // set the default values + setFont (kNormalFont); + setFrameColor (kWhiteCColor); + setLineStyle (kLineSolid); + setLineWidth (1); + setFont (kSystemFont); + setDrawMode (kCopyMode); + } +} + +//----------------------------------------------------------------------------- +CDrawContext::~CDrawContext () +{ +#if DEBUG + gNbCDrawContext--; +#endif +} + +//----------------------------------------------------------------------------- +void CDrawContext::setLineStyle (CLineStyle style) +{ + if (lineStyle == style) + return; + + lineStyle = style; + + long line_width; + long line_style; + if (frameWidth == 1) + line_width = 0; + else + line_width = frameWidth; + + switch (lineStyle) + { + case kLineOnOffDash: + line_style = LineOnOffDash; + break; + default: + line_style = LineSolid; + break; + } + + XSetLineAttributes (XGCPARAM, line_width, line_style, CapNotLast, JoinRound); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setLineWidth (CCoord width) +{ + if (frameWidth == width) + return; + + frameWidth = width; + + setLineStyle (lineStyle); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setDrawMode (CDrawMode mode) +{ + if (drawMode == mode) + return; + + drawMode = mode; + + long iMode = 0; + switch (drawMode) + { + case kXorMode : + iMode = GXinvert; + break; + case kOrMode : + iMode = GXor; + break; + default: + iMode = GXcopy; + } + + ((XGCValues*)pSystemContext)->function = iMode; + + XChangeGC (XGCPARAM, GCFunction, (XGCValues*)pSystemContext); +} + +//------------------------------------------------------------------------------ +void CDrawContext::setClipRect (const CRect &clip) +{ + CRect _clip (clip); + _clip.offset (offset.h, offset.v); + + if (clipRect == _clip) + return; + + clipRect = _clip; + + XRectangle r; + r.x = 0; + r.y = 0; + r.width = clipRect.right - clipRect.left + 1; + r.height = clipRect.bottom - clipRect.top + 1; + + XSetClipRectangles (XGCPARAM, clipRect.left, clipRect.top, &r, 1, Unsorted); +} + +//------------------------------------------------------------------------------ +void CDrawContext::resetClipRect () +{ + CRect newClip; + if (pFrame) + pFrame->getViewSize (newClip); + else + newClip (0, 0, 1000, 1000); + + setClipRect (newClip); + + clipRect = newClip; +} + +//----------------------------------------------------------------------------- +void CDrawContext::moveTo (const CPoint &_point) +{ + CPoint point (_point); + point.offset (offset.h, offset.v); + + penLoc = point; +} + +//----------------------------------------------------------------------------- +void CDrawContext::lineTo (const CPoint& _point) +{ + CPoint point (_point); + point.offset (offset.h, offset.v); + + CPoint start (penLoc); + CPoint end (point); + if (start.h == end.h) + { + if (start.v < -5) + start.v = -5; + else if (start.v > 10000) + start.v = 10000; + + if (end.v < -5) + end.v = -5; + else if (end.v > 10000) + end.v = 10000; + } + if (start.v == end.v) + { + if (start.h < -5) + start.h = -5; + else if (start.h > 10000) + start.h = 10000; + + if (end.h < -5) + end.h = -5; + else if (end.h > 10000) + end.h = 10000; + } + + XDrawLine (XDRAWPARAM, start.h, start.v, end.h, end.v); + + // keep trace of the new position + penLoc = point; +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawLines (const CPoint* points, const long& numLines) +{ + // default implementation, when no platform optimized code is implemented + for (long i = 0; i < numLines * 2; i+=2) + { + moveTo (points[i]); + lineTo (points[i+1]); + } +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawPolygon (const CPoint *pPoints, long numberOfPoints, const CDrawStyle drawStyle) +{ + if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) + fillPolygon (pPoints, numberOfPoints); + if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) + polyLine (pPoints, numberOfPoints); +} + +//----------------------------------------------------------------------------- +void CDrawContext::polyLine (const CPoint *pPoints, long numberOfPoints) +{ + XPoint* pt = (XPoint*)malloc (numberOfPoints * sizeof (XPoint)); + if (!pt) + return; + for (long i = 0; i < numberOfPoints; i++) + { + pt[i].x = (short)pPoints[i].h + offset.h; + pt[i].y = (short)pPoints[i].v + offset.v; + } + + XDrawLines (XDRAWPARAM, pt, numberOfPoints, CoordModeOrigin); + + free (pt); +} + +//----------------------------------------------------------------------------- +void CDrawContext::fillPolygon (const CPoint *pPoints, long numberOfPoints) +{ + // convert the points + XPoint* pt = (XPoint*)malloc (numberOfPoints * sizeof (XPoint)); + if (!pt) + return; + for (long i = 0; i < numberOfPoints; i++) + { + pt[i].x = (short)pPoints[i].h + offset.h; + pt[i].y = (short)pPoints[i].v + offset.v; + } + + XFillPolygon (XDRAWPARAM, pt, numberOfPoints, Convex, CoordModeOrigin); + + free (pt); +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawRect (const CRect &_rect, const CDrawStyle drawStyle) +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + + XDrawRectangle (XDRAWPARAM, rect.left, rect.top, rect.width (), rect.height ()); +} + +//----------------------------------------------------------------------------- +void CDrawContext::fillRect (const CRect &_rect) +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + + // Don't draw boundary + XFillRectangle (XDRAWPARAM, rect.left + 1, rect.top + 1, rect.width () - 1, rect.height () - 1); +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawEllipse (const CRect &_rect, const CDrawStyle drawStyle) +{ + CPoint point (_rect.left + (_rect.right - _rect.left) / 2, _rect.top); + drawArc (_rect, point, point); +} + +//----------------------------------------------------------------------------- +void CDrawContext::fillEllipse (const CRect &_rect) +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + + // Don't draw boundary + CPoint point (_rect.left + ((_rect.right - _rect.left) / 2), _rect.top); + fillArc (_rect, point, point); +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawPoint (const CPoint &_point, CColor color) +{ + CPoint point (_point); + + CColor oldframecolor = frameColor; + setFrameColor (color); + + XDrawPoint (XDRAWPARAM, point.h, point.v); + + setFrameColor (oldframecolor); +} + +//----------------------------------------------------------------------------- +CColor CDrawContext::getPoint (const CPoint& _point) +{ +// CPoint point (_point); +// point.offset (offset.h, offset.v); + + assertfalse // not implemented ! + + CColor color = kBlackCColor; + return color; +} + +//----------------------------------------------------------------------------- +void CDrawContext::floodFill (const CPoint& _start) +{ +// CPoint start (_start); +// start.offset (offset.h, offset.v); + + assertfalse // not implemented ! +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawArc (const CRect &_rect, const float _startAngle, const float _endAngle, const CDrawStyle drawStyle) // in degree +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + + XDrawArc (XDRAWPARAM, rect.left, rect.top, rect.width (), rect.height (), + (int) _startAngle * 64, (int) _endAngle * 64); +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawArc (const CRect &_rect, const CPoint &_point1, const CPoint &_point2) +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + CPoint point1 (_point1); + point1.offset (offset.h, offset.v); + CPoint point2 (_point2); + point2.offset (offset.h, offset.v); + + int angle1, angle2; + if ((point1.v == point2.v) && (point1.h == point2.h)) + { + angle1 = 0; + angle2 = 23040; // 360 * 64 + } + else + { + CPoint pm ((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2); + angle1 = convertPoint2Angle (pm, point1); + angle2 = convertPoint2Angle (pm, point2) - angle1; + if (angle2 < 0) + angle2 += 23040; // 360 * 64 + } + + XDrawArc (XDRAWPARAM, rect.left, rect.top, rect.width (), rect.height (), + angle1, angle2); +} + +//----------------------------------------------------------------------------- +void CDrawContext::fillArc (const CRect &_rect, const CPoint &_point1, const CPoint &_point2) +{ + CRect rect (_rect); + rect.offset (offset.h, offset.v); + CPoint point1 (_point1); + point1.offset (offset.h, offset.v); + CPoint point2 (_point2); + point2.offset (offset.h, offset.v); + + // Don't draw boundary + int angle1, angle2; + if ((point1.v == point2.v) && (point1.h == point2.h)) + { + angle1 = 0; + angle2 = 23040; // 360 * 64 + } + else + { + CPoint pm ((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2); + angle1 = convertPoint2Angle (pm, point1); + angle2 = convertPoint2Angle (pm, point2); + } + + XFillArc (XDRAWPARAM, rect.left, rect.top, rect.width (), rect.height (), + angle1, angle2); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setFontColor (const CColor color) +{ + fontColor = color; + + setFrameColor (fontColor); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setFrameColor (const CColor color) +{ + if (frameColor == color) + return; + + frameColor = color; + + XSetForeground (XGCPARAM, getIndexColor (frameColor)); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setFillColor (const CColor color) +{ + if (fillColor == color) + return; + + fillColor = color; + + // set the background for the text + //XSetBackground (XGCPARAM, getIndexColor (fillColor)); + XSetBackground (display, pFrame->getGC(), getIndexColor (fillColor)); + + // set the foreground for the fill + setFrameColor (fillColor); +} + +//----------------------------------------------------------------------------- +void CDrawContext::setFont (CFont fontID, const long size, long style) +{ + if (fontID < 0 || fontID >= kNumStandardFonts) + fontID = kSystemFont; + + if (fontId == fontID && fontSize == (size != 0 ? size : gStandardFontSize[fontID]) && fontStyle == style) + return; + + fontStyle = style; + fontId = fontID; + if (size != 0) + fontSize = size; + else + fontSize = gStandardFontSize[fontID]; + + if (gFontStructs[fontID] != NULL) + XSetFont (display, pFrame->getGC(), gFontStructs[fontID]->fid); + else + fprintf(stderr, "ERROR: Font not defined\n"); + + // keep trace of the current font + pFontInfoStruct = gFontStructs[fontID]; + +#ifdef DEBUG +// if (!pFontInfoStruct) assert (false); // fonts have invalid pointers ! +#endif +} + +//------------------------------------------------------------------------------ +CCoord CDrawContext::getStringWidth (const char *pStr) +{ + CCoord result = 0; + + result = (long) XTextWidth (pFontInfoStruct, pStr, strlen (pStr)); + + return result; +} + +//----------------------------------------------------------------------------- +void CDrawContext::drawString (const char *string, const CRect &_rect, + const short opaque, const CHoriTxtAlign hAlign) +{ + if (!string) + return; + + CRect rect (_rect); + rect.offset (offset.h, offset.v); + + int width; + int fontHeight = pFontInfoStruct->ascent + pFontInfoStruct->descent; + int xPos; + int yPos; + int rectHeight = rect.height (); + + if (rectHeight >= fontHeight) + yPos = rect.bottom - (rectHeight - fontHeight) / 2; + else + yPos = rect.bottom; + yPos -= pFontInfoStruct->descent; + + switch (hAlign) + { + case kCenterText: + width = XTextWidth (pFontInfoStruct, string, strlen (string)); + xPos = (rect.right + rect.left - width) / 2; + break; + + case kRightText: + width = XTextWidth (pFontInfoStruct, string, strlen (string)); + xPos = rect.right - width; + break; + + default: // left adjust + xPos = rect.left + 1; + } + + if (opaque) + XDrawImageString (XDRAWPARAM, xPos, yPos, string, strlen (string)); + else + XDrawString (XDRAWPARAM, xPos, yPos, string, strlen (string)); +} + +//----------------------------------------------------------------------------- +long CDrawContext::getMouseButtons () +{ + long buttons = 0; + + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer (XWINPARAM, + &root, + &child, + &rootX, + &rootY, + &childX, + &childY, + &mask); + + if (mask & Button1Mask) + buttons |= kLButton; + if (mask & Button2Mask) + buttons |= kMButton; + if (mask & Button3Mask) + buttons |= kRButton; + + if (mask & ShiftMask) + buttons |= kShift; + if (mask & ControlMask) + buttons |= kControl; + if (mask & Mod1Mask) + buttons |= kAlt; + + return buttons; +} + +//----------------------------------------------------------------------------- +void CDrawContext::getMouseLocation (CPoint &point) +{ + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer (XWINPARAM, + &root, + &child, + &rootX, + &rootY, + &childX, + &childY, + &mask); + + point (childX, childY); + + point.offset (-offsetScreen.h, -offsetScreen.v); +} + +//----------------------------------------------------------------------------- +bool CDrawContext::waitDoubleClick () +{ + bool doubleClick = false; + + long currentTime = _getTicks (); + long clickTime = currentTime + 200; // XtGetMultiClickTime (display); + + XEvent e; + while (currentTime < clickTime) + { + if (XCheckTypedEvent (display, ButtonPress, &e)) + { + doubleClick = true; + break; + } + + currentTime = _getTicks (); + } + + return doubleClick; +} + +//----------------------------------------------------------------------------- +bool CDrawContext::waitDrag () +{ + if (!pFrame) + return false; + + CPoint mouseLoc; + getMouseLocation (mouseLoc); + CRect observe (mouseLoc.h - 2, mouseLoc.v - 2, mouseLoc.h + 2, mouseLoc.v + 2); + + long currentTime = pFrame->getTicks (); + bool wasOutside = false; + + while (((getMouseButtons () & ~(kMButton|kRButton)) & kLButton) != 0) + { + pFrame->doIdleStuff (); + if (!wasOutside) + { + getMouseLocation (mouseLoc); + if (!observe.pointInside (mouseLoc)) + { + if (kDragDelay <= 0) + return true; + wasOutside = true; + } + } + + if (wasOutside && (pFrame->getTicks () - currentTime > kDragDelay)) + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +void CDrawContext::forget () +{ + CReferenceCounter::forget (); +} + +//----------------------------------------------------------------------------- +long CDrawContext::getIndexColor (CColor color) +{ + XColor xcol; + xcol.red = color.red << 8; + xcol.green = color.green << 8; + xcol.blue = color.blue << 8; + xcol.flags = (DoRed | DoGreen | DoBlue); + XAllocColor (display, XDefaultColormap (display, 0), &xcol); + return xcol.pixel; +} + +//----------------------------------------------------------------------------- +Colormap CDrawContext::getColormap () +{ + if (pFrame) + return pFrame->getColormap (); + else + return None; +} + +//----------------------------------------------------------------------------- +Visual* CDrawContext::getVisual () +{ + if (pFrame) + return pFrame->getVisual (); + else + return None; +} + +//----------------------------------------------------------------------------- +unsigned int CDrawContext::getDepth () +{ + if (pFrame) + return pFrame->getDepth (); + else + return None; +} + + +//----------------------------------------------------------------------------- +// COffscreenContext Implementation +//----------------------------------------------------------------------------- +COffscreenContext::COffscreenContext (CDrawContext *pContext, CBitmap *pBitmapBg, bool drawInBitmap) +: CDrawContext (pContext->pFrame, NULL, NULL) +, pBitmap (0) +, pBitmapBg (pBitmapBg) +, height (20) +, width (20) +{ + std::cout << "COffscreenContext::COffscreenContext with bitmap" << std::endl; + + if (pBitmapBg) + { + height = pBitmapBg->getHeight (); + width = pBitmapBg->getWidth (); + + clipRect (0, 0, width, height); + } + +#if DEBUG + gNbCOffscreenContext++; + gBitmapAllocation += (long)height * (long)width; +#endif + + bDestroyPixmap = false; + +// Window root = RootWindow (display, DefaultScreen (display)); + + // if no bitmap handle => create one + if (! pWindow) + { + pWindow = (void*) XCreatePixmap (display, pFrame->getWindow(), width, height, pFrame->getDepth ()); + bDestroyPixmap = true; + } + + // set the current font + if (pSystemContext) + setFont (kNormalFont); + + if (!drawInBitmap) + { + // draw bitmap to Offscreen + CRect r (0, 0, width, height); + if (pBitmapBg) + pBitmapBg->draw (this, r); + else + { + setFillColor (kBlackCColor); + fillRect (r); + } + } +} + +//----------------------------------------------------------------------------- +COffscreenContext::COffscreenContext (CFrame *pFrame, long width, long height, const CColor backgroundColor) +: CDrawContext (pFrame, NULL, NULL) +, pBitmap (0) +, pBitmapBg (0) +, height (height) +, width (width) +, backgroundColor (backgroundColor) +{ + std::cout << "COffscreenContext::COffscreenContext without bitmap" << std::endl; + + clipRect (0, 0, width, height); + +#if DEBUG + gNbCOffscreenContext++; + gBitmapAllocation += height * width; +#endif + + bDestroyPixmap = true; + + pWindow = (void*) XCreatePixmap (display, + pFrame->getWindow(), + width, + height, + pFrame->getDepth ()); + +/* + // clear the pixmap + XGCValues values; + values.foreground = getIndexColor (backgroundColor); + GC gc = XCreateGC (display, (Window)pWindow, GCForeground, &values); + XFillRectangle (display, (Window)pWindow, gc, 0, 0, width, height); + XFreeGC (display, gc); +*/ + + XGCValues values; + values.foreground = getIndexColor (backgroundColor); + pSystemContext = XCreateGC (display, (Window)pWindow, GCForeground, &values); + XFillRectangle (display, (Window)pWindow, (GC) pSystemContext, 0, 0, width, height); + + // set the current font + if (pSystemContext) + setFont (kNormalFont); +} + +//----------------------------------------------------------------------------- +COffscreenContext::~COffscreenContext () +{ +#if DEBUG + gNbCOffscreenContext--; + gBitmapAllocation -= (long)height * (long)width; +#endif + + if (pBitmap) + pBitmap->forget (); + + if (bDestroyPixmap && pWindow) + XFreePixmap (display, (Pixmap)pWindow); + + if (pSystemContext) + XFreeGC (display, (GC) pSystemContext); +} + +//----------------------------------------------------------------------------- +void COffscreenContext::copyTo (CDrawContext* pContext, CRect& srcRect, CPoint destOffset) +{ + std::cout << "COffscreenContext::copyTo" << std::endl; + + XCopyArea (display, + (Drawable) pContext->pWindow, + (Drawable) pWindow, + (GC) pSystemContext, + srcRect.left, + srcRect.top, + srcRect.width (), + srcRect.height (), + destOffset.h, + destOffset.v); +} + +//----------------------------------------------------------------------------- +void COffscreenContext::copyFrom (CDrawContext *pContext, CRect destRect, CPoint srcOffset) +{ + //std::cout << "COffscreenContext::copyFrom "; + + XCopyArea (display, + (Drawable) pContext->pWindow, + (Drawable) pWindow, + (GC) pSystemContext, + srcOffset.h, + srcOffset.v, + destRect.width(), + destRect.height(), + destRect.left, + destRect.top); + + pContext->setFrameColor (kRedCColor); + pContext->drawRect (destRect); +} + + +//----------------------------------------------------------------------------- +class CAttributeListEntry +{ +public: + CAttributeListEntry (long size, CViewAttributeID id) + : nextEntry (0) + , pointer (0) + , sizeOfPointer (size) + , id (id) + { + pointer = malloc (size); + } + + ~CAttributeListEntry () + { + if (pointer) + free (pointer); + } + + CViewAttributeID getID () const { return id; } + long getSize () const { return sizeOfPointer; } + void* getPointer () const { return pointer; } + CAttributeListEntry* getNext () const { return nextEntry; } + + void setNext (CAttributeListEntry* entry) { nextEntry = entry; } + +protected: + CAttributeListEntry () : nextEntry (0), pointer (0), sizeOfPointer (0), id (0) {} + + CAttributeListEntry* nextEntry; + void* pointer; + long sizeOfPointer; + CViewAttributeID id; +}; + +//----------------------------------------------------------------------------- +const char* kMsgCheckIfViewContainer = "kMsgCheckIfViewContainer"; + +//----------------------------------------------------------------------------- +// CView +//----------------------------------------------------------------------------- +/*! @class CView +base class of all view objects +*/ +//----------------------------------------------------------------------------- +CView::CView (const CRect& size) +: size (size) +, mouseableArea (size) +, pParentFrame (0) +, pParentView (0) +, bDirty (false) +, bMouseEnabled (true) +, bTransparencyEnabled (false) +, bWantsFocus (false) +, bVisible (true) +, pBackground (0) +, pAttributeList (0) +{ +#if DEBUG + gNbCView++; +#endif +} + +//----------------------------------------------------------------------------- +CView::~CView () +{ + if (pBackground) + pBackground->forget (); + + if (pAttributeList) + { + CAttributeListEntry* entry = pAttributeList; + while (entry) + { + CAttributeListEntry* nextEntry = entry->getNext (); + delete entry; + entry = nextEntry; + } + } +#if DEBUG + gNbCView--; +#endif +} + +//----------------------------------------------------------------------------- +void CView::getMouseLocation (CDrawContext* context, CPoint &point) +{ + if (context) + { + if (pParentView && pParentView->notify (this, kMsgCheckIfViewContainer) == kMessageNotified) + { + CCoord save[4]; + ((CViewContainer*)pParentView)->modifyDrawContext (save, context); + pParentView->getMouseLocation (context, point); + ((CViewContainer*)pParentView)->restoreDrawContext (context, save); + } + else + context->getMouseLocation (point); + } +} + +//----------------------------------------------------------------------------- +CPoint& CView::frameToLocal (CPoint& point) const +{ + if (pParentView && pParentView->isTypeOf ("CViewContainer")) + return pParentView->frameToLocal (point); + return point; +} + +//----------------------------------------------------------------------------- +CPoint& CView::localToFrame (CPoint& point) const +{ + if (pParentView && pParentView->isTypeOf ("CViewContainer")) + return pParentView->localToFrame (point); + return point; +} + +//----------------------------------------------------------------------------- +void CView::redraw () +{ + if (pParentFrame) + pParentFrame->draw (this); +} + +//----------------------------------------------------------------------------- +void CView::redrawRect (CDrawContext* context, const CRect& rect) +{ + if (pParentView) + pParentView->redrawRect (context, rect); + else if (pParentFrame) + pParentFrame->drawRect (context, rect); +} + +//----------------------------------------------------------------------------- +void CView::draw (CDrawContext *pContext) +{ + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, size); + else + pBackground->draw (pContext, size); + } + setDirty (false); +} + +//----------------------------------------------------------------------------- +void CView::mouse (CDrawContext *pContext, CPoint &where, long buttons) +{} + +//----------------------------------------------------------------------------- +bool CView::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + return false; +} + +//------------------------------------------------------------------------ +bool CView::onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance) +{ + return onWheel (pContext, where, distance); +} + +//------------------------------------------------------------------------ +void CView::update (CDrawContext *pContext) +{ + if (isDirty ()) + { +#if NEW_UPDATE_MECHANISM + if (pContext) + redrawRect (pContext, size); + else + redraw (); +#else + #if USE_ALPHA_BLEND + if (pContext) + { + if (bTransparencyEnabled) + getFrame ()->drawRect (pContext, size); + else + draw (pContext); + } + #else + if (pContext) + draw (pContext); + #endif + else + redraw (); +#endif + setDirty (false); + } +} + +//------------------------------------------------------------------------------ +long CView::onKeyDown (VstKeyCode& keyCode) +{ + return -1; +} + +//------------------------------------------------------------------------------ +long CView::onKeyUp (VstKeyCode& keyCode) +{ + return -1; +} + +//------------------------------------------------------------------------------ +long CView::notify (CView* sender, const char* message) +{ + return kMessageUnknown; +} + +//------------------------------------------------------------------------------ +void CView::looseFocus (CDrawContext *pContext) +{} + +//------------------------------------------------------------------------------ +void CView::takeFocus (CDrawContext *pContext) +{} + +//------------------------------------------------------------------------------ +void CView::setViewSize (CRect &rect) +{ + size = rect; + setDirty (); +} + +//----------------------------------------------------------------------------- +void *CView::getEditor () const +{ + return pParentFrame ? pParentFrame->getEditor () : 0; +} + +//----------------------------------------------------------------------------- +void CView::setBackground (CBitmap *background) +{ + if (pBackground) + pBackground->forget (); + pBackground = background; + if (pBackground) + pBackground->remember (); + setDirty (true); +} + +//----------------------------------------------------------------------------- +const CViewAttributeID kCViewAttributeReferencePointer = (CViewAttributeID) "cvrp"; + +//----------------------------------------------------------------------------- +/** + * @param id the ID of the Attribute + * @param outSize on return the size of the attribute + */ +bool CView::getAttributeSize (const CViewAttributeID id, long& outSize) const +{ + if (pAttributeList) + { + CAttributeListEntry* entry = pAttributeList; + while (entry) + { + if (entry->getID () == id) + break; + entry = entry->getNext (); + } + if (entry) + { + outSize = entry->getSize (); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +/** + * @param id the ID of the Attribute + * @param inSize the size of the outData pointer + * @param outData a pointer where to copy the attribute data + * @param outSize the size in bytes which was copied into outData + */ +bool CView::getAttribute (const CViewAttributeID id, const long inSize, void* outData, long& outSize) const +{ + if (pAttributeList) + { + CAttributeListEntry* entry = pAttributeList; + while (entry) + { + if (entry->getID () == id) + break; + entry = entry->getNext (); + } + if (entry && inSize >= entry->getSize ()) + { + outSize = entry->getSize (); + memcpy (outData, entry->getPointer (), outSize); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +/** + * copies data into the attribute. If it does not exist, creates a new attribute. + * @param id the ID of the Attribute + * @param inSize the size of the outData pointer + * @param inData a pointer to the data + */ +bool CView::setAttribute (const CViewAttributeID id, const long inSize, void* inData) +{ + CAttributeListEntry* lastEntry = 0; + if (pAttributeList) + { + CAttributeListEntry* entry = pAttributeList; + while (entry) + { + if (entry->getID () == id) + break; + if (entry->getNext () == 0) + lastEntry = entry; + entry = entry->getNext (); + } + if (entry) + { + if (entry->getSize () >= inSize) + { + memcpy (entry->getPointer (), inData, inSize); + return true; + } + else + return false; + } + } + + // create a new attribute + CAttributeListEntry* newEntry = new CAttributeListEntry (inSize, id); + memcpy (newEntry->getPointer (), inData, inSize); + if (lastEntry) + lastEntry->setNext (newEntry); + else if (!pAttributeList) + pAttributeList = newEntry; + else + { + delete newEntry; + return false; + } + return true; +} + +#if DEBUG +//----------------------------------------------------------------------------- +void CView::dumpInfo () +{ + CRect viewRect = getViewSize (viewRect); + DebugPrint ("left:%4d, top:%4d, width:%4d, height:%4d ", viewRect.left, viewRect.top, viewRect.getWidth (), viewRect.getHeight ()); + if (getMouseEnabled ()) + DebugPrint ("(Mouse Enabled) "); + if (getTransparency ()) + DebugPrint ("(Transparent) "); + CRect mouseRect = getMouseableArea (mouseRect); + if (mouseRect != viewRect) + DebugPrint (" (Mouseable Area: left:%4d, top:%4d, width:%4d, height:%4d ", mouseRect.left, mouseRect.top, mouseRect.getWidth (), mouseRect.getHeight ()); +} +#endif + +#define FOREACHSUBVIEW for (CCView *pSv = pFirstView; pSv; pSv = pSv->pNext) {CView *pV = pSv->pView; +#define FOREACHSUBVIEW_REVERSE(reverse) for (CCView *pSv = reverse ? pLastView : pFirstView; pSv; pSv = reverse ? pSv->pPrevious : pSv->pNext) {CView *pV = pSv->pView; +#define ENDFOR } + +//----------------------------------------------------------------------------- +// CFrame Implementation +//----------------------------------------------------------------------------- +/*! @class CFrame +It creates a platform dependend view object. +On classic Mac OS it just draws into the provided window. +On Mac OS X it is a ControlRef. +On Windows it's a WS_CHILD Window. +*/ +CFrame::CFrame (const CRect &inSize, void *inSystemWindow, void *inEditor) +: CViewContainer (inSize, 0, 0) +, pEditor (inEditor) +, pModalView (0) +, pFocusView (0) +, bFirstDraw (true) +, bDropActive (false) +, bUpdatesDisabled (false) +, pSystemWindow ((Window)inSystemWindow) +, pFrameContext (0) +, bAddedWindow (false) +, defaultCursor (0) +{ + setOpenFlag (true); + + pParentFrame = this; + + depth = 0; + pVisual = 0; + window = 0; + gc = 0; + + if (display == NULL) + display = XOpenDisplay(NULL); + + initFrame ((void*)pSystemWindow); + backBuffer = XdbeAllocateBackBufferName(display, (Window)window, XdbeUndefined); + + XGCValues values; + values.foreground = 1; + gc = XCreateGC (display, (Window)backBuffer, GCForeground, &values); + + pFrameContext = new CDrawContext (this, gc, (void*)backBuffer); +} + +//----------------------------------------------------------------------------- +CFrame::~CFrame () +{ + if (pModalView) + removeView (pModalView, false); + + setCursor (kCursorDefault); + + setDropActive (false); + + if (pFrameContext) + pFrameContext->forget (); + + if (getOpenFlag ()) + close (); + + if (window) + XDestroyWindow (display, (Window) window); + window = 0; + + // remove callbacks to avoid undesirable update + if (gc) + freeGc (); +} + +//----------------------------------------------------------------------------- +bool CFrame::open () +{ + if (! window) + return false; + + XMapRaised (display, window); + + return getOpenFlag (); +} + +//----------------------------------------------------------------------------- +bool CFrame::close () +{ + if (!window || !getOpenFlag () || !pSystemWindow) + return false; + + XUnmapWindow (display, window); + + return true; +} + +//----------------------------------------------------------------------------- +bool xerror; +int errorHandler (Display *dp, XErrorEvent *e) +{ + xerror = true; + return 0; +} +int getProperty (Window handle, Atom atom) +{ + int result = 0, userSize; + unsigned long bytes, userCount; + unsigned char *data; + Atom userType; + xerror = false; + XErrorHandler olderrorhandler = XSetErrorHandler (errorHandler); + XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, + &userType, &userSize, &userCount, &bytes, &data); + if (xerror == false && userCount == 1) + result = *(int*)data; + XSetErrorHandler (olderrorhandler); + return result; +} + +void eventProc (XEvent* ev) +{ + CFrame* frame = (CFrame*) getProperty (ev->xany.window, XInternAtom (display, "_this", false)); + if (frame == 0) + return; + + switch (ev->type) + { + case ButtonPress: + { + CPoint where (ev->xbutton.x, ev->xbutton.y); + frame->mouse (frame->createDrawContext(), where); + break; + } + + case MotionNotify: + { + CPoint where (ev->xbutton.x, ev->xbutton.y); + frame->mouse (frame->createDrawContext(), where); + break; + } + + case ButtonRelease: + { + break; + } + + case Expose: + { + CRect rc (ev->xexpose.x, ev->xexpose.y, + ev->xexpose.x + ev->xexpose.width, ev->xexpose.y + ev->xexpose.height); + + while (XCheckTypedWindowEvent (display, ev->xexpose.window, Expose, ev)) + { + rc.left = std::min ((int) rc.left, ev->xexpose.x); + rc.top = std::min ((int) rc.top, ev->xexpose.y); + rc.right = std::max ((int) rc.right, ev->xexpose.x + ev->xexpose.width); + rc.bottom = std::max ((int) rc.bottom, ev->xexpose.y + ev->xexpose.height); + } + + frame->drawRect (0, rc); + break; + } + + case EnterNotify: + break; + + case LeaveNotify: + { + XCrossingEvent *xevent = (XCrossingEvent*) ev; + if (frame->getFocusView ()) + { + if (xevent->x < 0 || xevent->x >= frame->getWidth () + || xevent->y < 0 || xevent->y >= frame->getHeight ()) + { + // if button pressed => don't defocus + if (xevent->state & (Button1Mask | Button2Mask | Button3Mask)) + break; + + frame->getFocusView ()->looseFocus (); + frame->setFocusView (0); + } + } + break; + } + + case ConfigureNotify: + { + frame->draw (); // @XXX - keep out ? + break; + } + } +} + +//----------------------------------------------------------------------------- +bool CFrame::initFrame (void *systemWin) +{ + if (!systemWin) + return false; + + // window attributes + XSetWindowAttributes swa; + swa.override_redirect = false; + swa.background_pixmap = None; + swa.colormap = 0; + swa.event_mask = + StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | FocusChangeMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + // create child window of host supplied window + window = XCreateWindow (display, (Window)systemWin, + 0, 0, size.width(), size.height(), + 0, CopyFromParent, InputOutput, (Visual*) CopyFromParent, + CWEventMask | CWOverrideRedirect | CWColormap | CWBackPixmap | CWEventMask, &swa); + + XGrabButton (display, AnyButton, AnyModifier, window, False, + ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, None, None); + +#if 0 + // remove window caption/frame + #define MWM_HINTS_DECORATIONS (1L << 1) + #define PROP_MOTIF_WM_HINTS_ELEMENTS 5 + typedef struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; + } + PropMotifWmHints; + + PropMotifWmHints motif_hints; + motif_hints.flags = MWM_HINTS_DECORATIONS; + motif_hints.decorations = 0; + Atom prop = XInternAtom (display, "_MOTIF_WM_HINTS", True ); + XChangeProperty (display, window, prop, prop, 32, PropModeReplace, + (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS); +#endif + + Atom atom; + void* data = this; + atom = XInternAtom (display, "_this", false); + XChangeProperty (display, (Window)window, atom, atom, 32, + PropModeReplace, (unsigned char*)&data, 1); + + data = (void*)&eventProc; + atom = XInternAtom (display, "_XEventProc", false); + XChangeProperty (display, (Window)window, atom, atom, 32, + PropModeReplace, (unsigned char*)&data, 1); + + XGCValues values; + values.foreground = 1; + gc = XCreateGC (display, (Window)window, GCForeground, &values); + + // get the std colormap + XWindowAttributes attr; + XGetWindowAttributes (display, (Window) window, &attr); + colormap = attr.colormap; + pVisual = attr.visual; + depth = attr.depth; + + // init and load the fonts + if (! gFontInit) { + for (long i = 0; i < kNumStandardFonts; i++) { + DBG (gFontTable[i].string); + gFontStructs[i] = XLoadQueryFont (display, gFontTable[i].string); + assert (gFontStructs[i] != 0); + } + gFontInit = true; + } + + setDropActive (true); + bAddedWindow = true; + + //XReparentWindow (display, window, (Window) systemWin, 0, 0); + //XMapRaised (display, window); + + return true; +} + +//----------------------------------------------------------------------------- +bool CFrame::setDropActive (bool val) +{ + if (!bDropActive && !val) + return true; + + bDropActive = val; + return true; +} + +//----------------------------------------------------------------------------- +void CFrame::freeGc () +{ + XFreeGC (display, gc); +} + +//----------------------------------------------------------------------------- +CDrawContext* CFrame::createDrawContext () +{ + if (pFrameContext) + { + pFrameContext->remember (); + return pFrameContext; + } + + pFrameContext = new CDrawContext (this, gc, (void*)window); + + return pFrameContext; +} + +//----------------------------------------------------------------------------- +void CFrame::draw (CDrawContext *pContext) +{ + if (bFirstDraw) + bFirstDraw = false; + + bool localContext = false; + if (! pContext) + { + localContext = true; + pContext = createDrawContext (); + } + + // draw the background and the children + CViewContainer::draw (pContext); + + if (localContext) + pContext->forget (); +} + +//----------------------------------------------------------------------------- +void CFrame::drawRect (CDrawContext *pContext, const CRect& updateRect) +{ + if (bFirstDraw) + bFirstDraw = false; + + bool localContext = false; + if (! pContext) + { + localContext = true; + pContext = createDrawContext (); + } + +#if USE_CLIPPING_DRAWRECT + CRect oldClip; + pContext->getClipRect (oldClip); + CRect newClip (updateRect); + newClip.bound (oldClip); + pContext->setClipRect (newClip); +#endif + + // draw the background and the children + if (updateRect.getWidth () > 0 && updateRect.getHeight () > 0) + CViewContainer::drawRect (pContext, updateRect); + +#if USE_CLIPPING_DRAWRECT + pContext->setClipRect (oldClip); +#endif + + if (localContext) + pContext->forget (); +} + +//----------------------------------------------------------------------------- +void CFrame::draw (CView *pView) +{ + CView *pViewToDraw = 0; + + // Search it in the view list + if (pView && isChild(pView)) + pViewToDraw = pView; + + CDrawContext *pContext = createDrawContext (); + if (pContext) + { + if (pViewToDraw && pViewToDraw->isVisible()) + pViewToDraw->draw (pContext); + else + draw (pContext); + + pContext->forget (); + } +} + +//----------------------------------------------------------------------------- +void CFrame::mouse (CDrawContext *pContext, CPoint &where, long buttons) +{ +/* + XGrabPointer (display, + window, + False, + ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask, + GrabModeAsync, + GrabModeAsync, + None, + None, + CurrentTime); +*/ + + if (!pContext) + pContext = pFrameContext; + + if (pFocusView) + setFocusView (NULL); + + if (buttons == -1 && pContext) + buttons = pContext->getMouseButtons (); + + if (pModalView && pModalView->isVisible()) + { + if (pModalView->hitTest (where, buttons)) + pModalView->mouse (pContext, where, buttons); + } + else + { + CViewContainer::mouse (pContext, where, buttons); + } + +// XUngrabPointer (display, CurrentTime); +} + +//----------------------------------------------------------------------------- +long CFrame::onKeyDown (VstKeyCode& keyCode) +{ + long result = -1; + + if (pFocusView && pFocusView->isVisible()) + result = pFocusView->onKeyDown (keyCode); + + if (result == -1 && pModalView && pModalView->isVisible()) + result = pModalView->onKeyDown (keyCode); + + if (result == -1 && keyCode.virt == VKEY_TAB) + result = advanceNextFocusView (pFocusView, (keyCode.modifier & MODIFIER_SHIFT) ? true : false) ? 1 : -1; + + return result; +} + +//----------------------------------------------------------------------------- +long CFrame::onKeyUp (VstKeyCode& keyCode) +{ + long result = -1; + + if (pFocusView && pFocusView->isVisible()) + result = pFocusView->onKeyUp (keyCode); + + if (result == -1 && pModalView && pModalView->isVisible()) + result = pModalView->onKeyUp (keyCode); + + return result; +} + +//------------------------------------------------------------------------ +bool CFrame::onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance) +{ + bool result = false; + + CView *view = pModalView ? pModalView : getViewAt (where); + if (view && view->isVisible()) + { + bool localContext = false; + if (!pContext) + { + localContext = true; + pContext = createDrawContext (); + } + + result = view->onWheel (pContext, where, axis, distance); + + if (localContext) + pContext->forget (); + } + return result; +} + +//----------------------------------------------------------------------------- +bool CFrame::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + return onWheel (pContext, where, kMouseWheelAxisY, distance); +} + +//----------------------------------------------------------------------------- +void CFrame::update (CDrawContext *pContext) +{ + if (!getOpenFlag () || updatesDisabled ()) + return; + + CDrawContext* dc = pContext; + + if (bDirty) + { + draw (dc); + setDirty (false); + } + else + { +#if USE_CLIPPING_DRAWRECT + CRect oldClipRect; + dc->getClipRect (oldClipRect); +#endif +#if NEW_UPDATE_MECHANISM + if (pModalView && pModalView->isVisible () && pModalView->isDirty ()) + pModalView->update (dc); +#endif + FOREACHSUBVIEW +#if USE_CLIPPING_DRAWRECT + CRect viewSize (pV->size); + viewSize.bound (oldClipRect); + dc->setClipRect (viewSize); +#endif + pV->update (dc); + ENDFOR +#if USE_CLIPPING_DRAWRECT + dc->setClipRect (oldClipRect); +#endif + } +} + +//----------------------------------------------------------------------------- +void CFrame::idle () +{ + if (!getOpenFlag ()) + return; + + // don't do an idle before a draw + if (bFirstDraw) + return; + + if (!isDirty ()) + return; + + XdbeBeginIdiom(display); + XdbeSwapInfo swap_info; + swap_info.swap_window = window; + swap_info.swap_action = XdbeUndefined; + XdbeSwapBuffers(display, &swap_info, 1); + + CDrawContext *pContext = createDrawContext (); + + update (pContext); + + pContext->forget (); + + XdbeEndIdiom(display); +} + +//----------------------------------------------------------------------------- +void CFrame::doIdleStuff () +{ + if (pEditor) + ((AEffGUIEditor*)pEditor)->doIdleStuff (); +} + +//----------------------------------------------------------------------------- +unsigned long CFrame::getTicks () const +{ + if (pEditor) + return ((AEffGUIEditor*)pEditor)->getTicks (); + return 0; +} + +//----------------------------------------------------------------------------- +long CFrame::getKnobMode () const +{ + return AEffGUIEditor::getKnobMode (); +} + +//----------------------------------------------------------------------------- +bool CFrame::setPosition (CCoord x, CCoord y) +{ + size.left = 0; + size.top = 0; + + XWindowChanges attr; + attr.x = x; + attr.y = y; + + XConfigureWindow (display, window, CWX | CWY, &attr); + + return false; +} + +//----------------------------------------------------------------------------- +bool CFrame::getPosition (CCoord &x, CCoord &y) const +{ + x = size.left; + y = size.top; + return true; +} + +//----------------------------------------------------------------------------- +void CFrame::setViewSize (CRect& inRect) +{ + setSize (inRect.width (), inRect.height ()); +} + +//----------------------------------------------------------------------------- +bool CFrame::setSize (CCoord width, CCoord height) +{ + if ((width == size.width ()) && (height == size.height ())) + return false; + + // set the new size + size.right = size.left + width; + size.bottom = size.top + height; + + XResizeWindow (display, window, size.width (), size.height ()); + + CRect myViewSize (0, 0, size.width (), size.height ()); + CViewContainer::setViewSize (myViewSize); + + return true; +} + +//----------------------------------------------------------------------------- +bool CFrame::getSize (CRect *pRect) const +{ +// if (!getOpenFlag ()) +// return false; + + pRect->left = 0; + pRect->top = 0; + pRect->right = size.width () + pRect->left; + pRect->bottom = size.height () + pRect->top; + + return true; +} + +//----------------------------------------------------------------------------- +bool CFrame::getSize (CRect& outSize) const +{ + return getSize (&outSize); +} + +//----------------------------------------------------------------------------- +long CFrame::setModalView (CView *pView) +{ + // There's already a modal view so we get out + if (pView && pModalView) + return 0; + + if (pModalView) + removeView (pModalView, false); + + pModalView = pView; + if (pModalView) + addView (pModalView); + + return 1; +} + +//----------------------------------------------------------------------------- +void CFrame::beginEdit (long index) +{ + if (pEditor) + ((AEffGUIEditor*)pEditor)->beginEdit (index); +} + +//----------------------------------------------------------------------------- +void CFrame::endEdit (long index) +{ + if (pEditor) + ((AEffGUIEditor*)pEditor)->endEdit (index); +} + +//----------------------------------------------------------------------------- +CView *CFrame::getCurrentView () const +{ + if (pModalView) + return pModalView; + + return CViewContainer::getCurrentView (); +} + +//----------------------------------------------------------------------------- +bool CFrame::getCurrentLocation (CPoint &where) +{ + // create a local context + CDrawContext *pContext = createDrawContext (); + if (pContext) + { + // get the current position + pContext->getMouseLocation (where); + pContext->forget (); + } + return true; +} + +//----------------------------------------------------------------------------- +void CFrame::setCursor (CCursorType type) +{ +} + +//----------------------------------------------------------------------------- +void CFrame::setFocusView (CView *pView) +{ + if (pView && ! pView->isVisible()) + return; + + CView *pOldFocusView = pFocusView; + pFocusView = pView; + + if (pFocusView && pFocusView->wantsFocus ()) + pFocusView->setDirty (); + + if (pOldFocusView) + { + pOldFocusView->looseFocus (); + if (pOldFocusView->wantsFocus ()) + pOldFocusView->setDirty (); + } +} + +//----------------------------------------------------------------------------- +bool CFrame::advanceNextFocusView (CView* oldFocus, bool reverse) +{ + if (pModalView) + return false; // currently not supported, but should be done sometime + if (oldFocus == 0) + { + if (pFocusView == 0) + return CViewContainer::advanceNextFocusView (0, reverse); + oldFocus = pFocusView; + } + if (isChild (oldFocus)) + { + if (CViewContainer::advanceNextFocusView (oldFocus, reverse)) + return true; + else + { + setFocusView (NULL); + return false; + } + } + CView* parentView = oldFocus->getParentView (); + if (parentView && parentView->isTypeOf ("CViewContainer")) + { + CView* tempOldFocus = oldFocus; + CViewContainer* vc = (CViewContainer*)parentView; + while (vc) + { + if (vc->advanceNextFocusView (tempOldFocus, reverse)) + return true; + else + { + tempOldFocus = vc; + if (vc->getParentView () && vc->getParentView ()->isTypeOf ("CViewContainer")) + vc = (CViewContainer*)vc->getParentView (); + else + vc = 0; + } + } + } + return CViewContainer::advanceNextFocusView (oldFocus, reverse); +} + +//----------------------------------------------------------------------------- +void CFrame::invalidate (const CRect &rect) +{ + CRect rectView; + FOREACHSUBVIEW + if (pV) + { + pV->getViewSize (rectView); + if (rect.rectOverlap (rectView)) + pV->setDirty (true); + } + ENDFOR + + XClearArea (display, + window, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + true); + + XSync (display, false); + +/* + XEvent ev; + memset (&ev, 0, sizeof (XEvent)); + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = (Window) window; + + ev.xexpose.x = rect.left; + ev.xexpose.y = rect.top; + ev.xexpose.width = rect.right - rect.left; + ev.xexpose.height = rect.bottom - rect.top; + + XSendEvent (display, (Window) window, False, 0L, &ev); + XFlush (display); +*/ +} + +#if DEBUG +//----------------------------------------------------------------------------- +void CFrame::dumpHierarchy () +{ + dumpInfo (); + DebugPrint ("\n"); + CViewContainer::dumpHierarchy (); +} +#endif + +//----------------------------------------------------------------------------- +// CCView Implementation +//----------------------------------------------------------------------------- +CCView::CCView (CView *pView) + : pView (pView), pNext (0), pPrevious (0) +{ + if (pView) + pView->remember (); +} + +//----------------------------------------------------------------------------- +CCView::~CCView () +{ + if (pView) + pView->forget (); +} + +//----------------------------------------------------------------------------- +// CViewContainer Implementation +//----------------------------------------------------------------------------- +/** + * CViewContainer constructor. + * @param rect the size of the container + * @param pParent the parent CFrame + * @param pBackground the background bitmap, can be NULL + */ +CViewContainer::CViewContainer (const CRect &rect, CFrame *pParent, CBitmap *pBackground) + : CView (rect), + pFirstView (0), + pLastView (0), + mode (kNormalUpdate), + pOffscreenContext (0), + bDrawInOffscreen (false), + currentDragView (0) +{ + pParentFrame = pParent; + + backgroundOffset (0, 0); + + setBackground (pBackground); + backgroundColor = kBlackCColor; + +#if NEW_UPDATE_MECHANISM + mode = kOnlyDirtyUpdate; +#endif +} + +//----------------------------------------------------------------------------- +CViewContainer::~CViewContainer () +{ + // remove all views + removeAll (true); + + if (pOffscreenContext) + pOffscreenContext->forget (); + pOffscreenContext = 0; +} + +//----------------------------------------------------------------------------- +/** + * @param rect the new size of the container + */ +void CViewContainer::setViewSize (CRect &rect) +{ + CView::setViewSize (rect); + + if (pOffscreenContext && bDrawInOffscreen) + { + pOffscreenContext->forget (); + pOffscreenContext = new COffscreenContext (pParentFrame, (long)size.width (), (long)size.height (), kBlackCColor); + } +} + +//----------------------------------------------------------------------------- +/** + * @param color the new background color of the container + */ +void CViewContainer::setBackgroundColor (CColor color) +{ + backgroundColor = color; + setDirty (true); +} + +//------------------------------------------------------------------------------ +long CViewContainer::notify (CView* sender, const char* message) +{ + if (message == kMsgCheckIfViewContainer) + return kMessageNotified; + return kMessageUnknown; +} + +//----------------------------------------------------------------------------- +/** + * @param pView the view object to add to this container + */ +void CViewContainer::addView (CView *pView) +{ + if (!pView) + return; + + CCView *pSv = new CCView (pView); + + pView->pParentFrame = pParentFrame; + pView->pParentView = this; + + CCView *pV = pFirstView; + if (!pV) + { + pLastView = pFirstView = pSv; + } + else + { + while (pV->pNext) + pV = pV->pNext; + pV->pNext = pSv; + pSv->pPrevious = pV; + pLastView = pSv; + } + pView->attached (this); + pView->setDirty (); +} + +//----------------------------------------------------------------------------- +/** + * @param pView the view object to add to this container + * @param mouseableArea the view area in where the view will get mouse events + * @param mouseEnabled bool to set if view will get mouse events + */ +void CViewContainer::addView (CView *pView, CRect &mouseableArea, bool mouseEnabled) +{ + if (!pView) + return; + + pView->setMouseEnabled (mouseEnabled); + pView->setMouseableArea (mouseableArea); + + addView (pView); +} + +//----------------------------------------------------------------------------- +/** + * @param withForget bool to indicate if the view's reference counter should be decreased after removed from the container + */ +void CViewContainer::removeAll (const bool &withForget) +{ + CCView *pV = pFirstView; + while (pV) + { + CCView *pNext = pV->pNext; + if (pV->pView) + { + pV->pView->removed (this); + if (withForget) + pV->pView->forget (); + } + + delete pV; + + pV = pNext; + } + pFirstView = 0; + pLastView = 0; +} + +//----------------------------------------------------------------------------- +/** + * @param pView the view which should be removed from the container + * @param withForget bool to indicate if the view's reference counter should be decreased after removed from the container + */ +void CViewContainer::removeView (CView *pView, const bool &withForget) +{ + if (pParentFrame && pParentFrame->getFocusView () == pView) + pParentFrame->setFocusView (0); + CCView *pV = pFirstView; + while (pV) + { + if (pView == pV->pView) + { + CCView *pNext = pV->pNext; + CCView *pPrevious = pV->pPrevious; + if (pV->pView) + { + pV->pView->removed (this); + if (withForget) + pV->pView->forget (); + } + delete pV; + if (pPrevious) + { + pPrevious->pNext = pNext; + if (pNext) + pNext->pPrevious = pPrevious; + else + pLastView = pPrevious; + } + else + { + pFirstView = pNext; + if (pNext) + pNext->pPrevious = 0; + else + pLastView = 0; + } + break; + } + else + pV = pV->pNext; + } +} + +//----------------------------------------------------------------------------- +/** + * @param pView the view which should be checked if it is a child of this container + */ +bool CViewContainer::isChild (CView *pView) const +{ + bool found = false; + + CCView *pV = pFirstView; + while (pV) + { + if (pView == pV->pView) + { + found = true; + break; + } + pV = pV->pNext; + } + return found; +} + +//----------------------------------------------------------------------------- +long CViewContainer::getNbViews () const +{ + long nb = 0; + CCView *pV = pFirstView; + while (pV) + { + pV = pV->pNext; + nb++; + } + return nb; +} + +//----------------------------------------------------------------------------- +/** + * @param index the index of the view to return + */ +CView *CViewContainer::getView (long index) const +{ + long nb = 0; + CCView *pV = pFirstView; + while (pV) + { + if (nb == index) + return pV->pView; + pV = pV->pNext; + nb++; + } + return 0; +} + +//----------------------------------------------------------------------------- +/** + * @param pContext the context which to use to draw this container and its subviews + */ +void CViewContainer::draw (CDrawContext *pContext) +{ + CDrawContext *pC; + CCoord save[4]; + + if (!pOffscreenContext && bDrawInOffscreen) + pOffscreenContext = new COffscreenContext (pParentFrame, (long)size.width (), (long)size.height (), kBlackCColor); + +#if USE_ALPHA_BLEND + if (pOffscreenContext && bTransparencyEnabled) + pOffscreenContext->copyTo (pContext, size); +#endif + + if (bDrawInOffscreen) + pC = pOffscreenContext; + else + { + pC = pContext; + modifyDrawContext (save, pContext); + } + + CRect r (0, 0, size.width (), size.height ()); + +#if USE_CLIPPING_DRAWRECT + CRect oldClip; + pContext->getClipRect (oldClip); + CRect oldClip2 (oldClip); + if (bDrawInOffscreen && getFrame () != this) + oldClip.offset (-oldClip.left, -oldClip.top); + + CRect newClip (r); + newClip.bound (oldClip); + pC->setClipRect (newClip); +#endif + + // draw the background + if (pBackground) + { + if (bTransparencyEnabled) + pBackground->drawTransparent (pC, r, backgroundOffset); + else + pBackground->draw (pC, r, backgroundOffset); + } + else if (!bTransparencyEnabled) + { + pC->setFillColor (backgroundColor); + pC->fillRect (r); + } + + // draw each view + FOREACHSUBVIEW +#if USE_CLIPPING_DRAWRECT + CRect vSize (pV->size); + vSize.bound (oldClip); + pC->setClipRect (vSize); +#endif + if (pV->isVisible()) + pV->draw (pC); + ENDFOR + +#if USE_CLIPPING_DRAWRECT + pC->setClipRect (oldClip2); +#endif + + // transfert offscreen + if (bDrawInOffscreen) + ((COffscreenContext*)pC)->copyFrom (pContext, size); + else + restoreDrawContext (pContext, save); + + setDirty (false); +} + +//----------------------------------------------------------------------------- +/** + * @param pContext the context which to use to draw the background + * @param _updateRect the area which to draw + */ +void CViewContainer::drawBackgroundRect (CDrawContext *pContext, CRect& _updateRect) +{ + if (pBackground) + { + CRect oldClip; + pContext->getClipRect (oldClip); + CRect newClip (_updateRect); + newClip.bound (oldClip); + pContext->setClipRect (newClip); + CRect tr (0, 0, pBackground->getWidth (), pBackground->getHeight ()); + if (bTransparencyEnabled) + pBackground->drawTransparent (pContext, tr, backgroundOffset); + else + pBackground->draw (pContext, tr, backgroundOffset); + pContext->setClipRect (oldClip); + } + else if (!bTransparencyEnabled) + { + pContext->setFillColor (backgroundColor); + pContext->fillRect (_updateRect); + } +} + +//----------------------------------------------------------------------------- +/** + * @param pContext the context which to use to draw + * @param _updateRect the area which to draw + */ +void CViewContainer::drawRect (CDrawContext *pContext, const CRect& _updateRect) +{ + CDrawContext *pC; + CCoord save[4]; + + if (!pOffscreenContext && bDrawInOffscreen) + pOffscreenContext = new COffscreenContext (pParentFrame, (long)size.width (), (long)size.height (), kBlackCColor); + +#if USE_ALPHA_BLEND + if (pOffscreenContext && bTransparencyEnabled) + pOffscreenContext->copyTo (pContext, size); +#endif + + if (bDrawInOffscreen) + pC = pOffscreenContext; + else + { + pC = pContext; + modifyDrawContext (save, pContext); + } + + CRect updateRect (_updateRect); + updateRect.bound (size); + + CRect clientRect (updateRect); + clientRect.offset (-size.left, -size.top); + +#if USE_CLIPPING_DRAWRECT + CRect oldClip; + pContext->getClipRect (oldClip); + CRect oldClip2 (oldClip); + if (bDrawInOffscreen && getFrame () != this) + oldClip.offset (-oldClip.left, -oldClip.top); + + CRect newClip (clientRect); + newClip.bound (oldClip); + pC->setClipRect (newClip); +#endif + + // draw the background + drawBackgroundRect (pC, clientRect); + + // draw each view + FOREACHSUBVIEW + if (pV->isVisible() && pV->checkUpdate (clientRect)) + { +#if USE_CLIPPING_DRAWRECT + CRect viewSize (pV->size); + viewSize.bound (newClip); + if (viewSize.getWidth () == 0 || viewSize.getHeight () == 0) + continue; + pC->setClipRect (viewSize); +#endif + + bool wasDirty = pV->isDirty (); + pV->drawRect (pC, clientRect); + +#if DEBUG_FOCUS_DRAWING + if (getFrame ()->getFocusView() == pV && pV->wantsFocus ()) + { + pC->setDrawMode (kCopyMode); + pC->setFrameColor (kRedCColor); + pC->drawRect (pV->size); + } +#endif + if (wasDirty && /* pV->size != viewSize &&*/ !isTypeOf ("CScrollContainer")) + { + pV->setDirty (true); + } + } + ENDFOR + +#if USE_CLIPPING_DRAWRECT + pC->setClipRect (oldClip2); +#endif + + // transfer offscreen + if (bDrawInOffscreen) + ((COffscreenContext*)pC)->copyFrom (pContext, updateRect, CPoint (clientRect.left, clientRect.top)); + else + restoreDrawContext (pContext, save); + +#if EVENT_DRAW_FIX + if (bDirty && newClip == size) +#endif + setDirty (false); +} + +//----------------------------------------------------------------------------- +/** + * @param context the context which to use to redraw this container + * @param rect the area which to redraw + */ +void CViewContainer::redrawRect (CDrawContext* context, const CRect& rect) +{ + CRect _rect (rect); + _rect.offset (size.left, size.top); + if (bTransparencyEnabled) + { + // as this is transparent, we call the parentview to redraw this area. + if (pParentView) + pParentView->redrawRect (context, _rect); + else if (pParentFrame) + pParentFrame->drawRect (context, _rect); + } + else + { + CCoord save[4]; + if (pParentView) + { + CPoint off; + pParentView->localToFrame (off); + // store + save[0] = context->offsetScreen.h; + save[1] = context->offsetScreen.v; + save[2] = context->offset.h; + save[3] = context->offset.v; + + context->offsetScreen.h += off.x; + context->offsetScreen.v += off.y; + context->offset.h += off.x; + context->offset.v += off.y; + + drawRect (context, _rect); + + // restore + context->offsetScreen.h = save[0]; + context->offsetScreen.v = save[1]; + context->offset.h = save[2]; + context->offset.v = save[3]; + } + else + { + drawRect (context, _rect); + } + } +} + +//----------------------------------------------------------------------------- +bool CViewContainer::hitTestSubViews (const CPoint& where, const long buttons) +{ + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + CCView *pSv = pLastView; + while (pSv) + { + CView *pV = pSv->pView; + if (pV && pV->getMouseEnabled () && pV->hitTest (where2, buttons)) + return true; + pSv = pSv->pPrevious; + } + return false; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::hitTest (const CPoint& where, const long buttons) +{ + //return hitTestSubViews (where); would change default behavior + return CView::hitTest (where, buttons); +} + +//----------------------------------------------------------------------------- +void CViewContainer::mouse (CDrawContext *pContext, CPoint &where, long buttons) +{ + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + if (buttons == -1 && pContext) + buttons = pContext->getMouseButtons (); + + CCView *pSv = pLastView; + while (pSv) + { + CView *pV = pSv->pView; + if (pV && pV->getMouseEnabled () && pV->hitTest (where2, buttons)) + { + pV->mouse (pContext, where2, buttons); + break; + } + pSv = pSv->pPrevious; + } +} + +//----------------------------------------------------------------------------- +long CViewContainer::onKeyDown (VstKeyCode& keyCode) +{ + long result = -1; + + CCView* pSv = pLastView; + while (pSv) + { + long result = pSv->pView->onKeyDown (keyCode); + if (result != -1) + break; + + pSv = pSv->pPrevious; + } + + return result; +} + +//----------------------------------------------------------------------------- +long CViewContainer::onKeyUp (VstKeyCode& keyCode) +{ + long result = -1; + + CCView* pSv = pLastView; + while (pSv) + { + long result = pSv->pView->onKeyUp (keyCode); + if (result != -1) + break; + + pSv = pSv->pPrevious; + } + + return result; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance) +{ + bool result = false; + CView *view = getViewAt (where); + if (view) + { + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + CCoord save[4]; + modifyDrawContext (save, pContext); + + result = view->onWheel (pContext, where2, axis, distance); + + restoreDrawContext (pContext, save); + } + return result; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::onWheel (CDrawContext *pContext, const CPoint &where, float distance) +{ + return onWheel (pContext, where, kMouseWheelAxisY, distance); +} + +//----------------------------------------------------------------------------- +bool CViewContainer::onDrop (CDrawContext* context, CDragContainer* drag, const CPoint& where) +{ + if (!pParentFrame) + return false; + + bool result = false; + + CCoord save[4]; + modifyDrawContext (save, context); + + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + CView* view = getViewAt (where); + if (view != currentDragView) + { + if (currentDragView) + currentDragView->onDragLeave (context, drag, where2); + currentDragView = view; + } + if (currentDragView) + { + result = currentDragView->onDrop (context, drag, where2); + currentDragView->onDragLeave (context, drag, where2); + } + currentDragView = 0; + + restoreDrawContext (context, save); + + return result; +} + +//----------------------------------------------------------------------------- +void CViewContainer::onDragEnter (CDrawContext* context, CDragContainer* drag, const CPoint& where) +{ + if (!pParentFrame) + return; + + CCoord save[4]; + modifyDrawContext (save, context); + + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + if (currentDragView) + currentDragView->onDragLeave (context, drag, where2); + CView* view = getViewAt (where); + currentDragView = view; + if (view) + view->onDragEnter (context, drag, where2); + + restoreDrawContext (context, save); +} + +//----------------------------------------------------------------------------- +void CViewContainer::onDragLeave (CDrawContext* context, CDragContainer* drag, const CPoint& where) +{ + if (!pParentFrame) + return; + + CCoord save[4]; + modifyDrawContext (save, context); + + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + if (currentDragView) + currentDragView->onDragLeave (context, drag, where2); + currentDragView = 0; + + restoreDrawContext (context, save); +} + +//----------------------------------------------------------------------------- +void CViewContainer::onDragMove (CDrawContext* context, CDragContainer* drag, const CPoint& where) +{ + if (!pParentFrame) + return; + + CCoord save[4]; + modifyDrawContext (save, context); + + // convert to relativ pos + CPoint where2 (where); + where2.offset (-size.left, -size.top); + + CView* view = getViewAt (where); + if (view != currentDragView) + { + if (currentDragView) + currentDragView->onDragLeave (context, drag, where2); + if (view) + view->onDragEnter (context, drag, where2); + currentDragView = view; + } + else if (currentDragView) + currentDragView->onDragMove (context, drag, where2); + + restoreDrawContext (context, save); +} + +//----------------------------------------------------------------------------- +void CViewContainer::update (CDrawContext *pContext) +{ + switch (mode) + { + //---Normal : redraw all... + case kNormalUpdate: + if (isDirty ()) + { +#if NEW_UPDATE_MECHANISM + CRect ur (0, 0, size.width (), size.height ()); + redrawRect (pContext, ur); +#else + #if USE_ALPHA_BLEND + if (bTransparencyEnabled) + { + CRect updateRect (size); + CPoint offset (0,0); + localToFrame (offset); + updateRect.offset (offset.x, offset.y); + getFrame ()->drawRect (pContext, updateRect); + } + else + #endif + draw (pContext); + #endif + setDirty (false); + } + break; + + //---Redraw only dirty controls----- + case kOnlyDirtyUpdate: + { +#if NEW_UPDATE_MECHANISM + if (bDirty) + { + CRect ur (0, 0, size.width (), size.height ()); + redrawRect (pContext, ur); + } + else + { + CRect updateRect (size); + updateRect.offset (-size.left, -size.top); + FOREACHSUBVIEW + if (pV->isDirty () && pV->checkUpdate (updateRect)) + { + if (pV->notify (this, kMsgCheckIfViewContainer)) + pV->update (pContext); + else + { + CRect drawSize (pV->size); + drawSize.bound (updateRect); + pV->redrawRect (pContext, drawSize); + } + } + ENDFOR + } +#else + #if USE_ALPHA_BLEND + if (bTransparencyEnabled) + { + if (bDirty) + { + CRect updateRect (size); + CPoint offset (0,0); + localToFrame (offset); + updateRect.offset (offset.x, offset.y); + getFrame ()->drawRect (pContext, updateRect); + } + else + { + CRect updateRect (size); + updateRect.offset (-size.left, -size.top); + FOREACHSUBVIEW + if (pV->isDirty () && pV->checkUpdate (updateRect)) + { + if (pV->notify (this, kMsgCheckIfViewContainer)) + { + pV->update (pContext); + } + else + { + CPoint offset; + CRect viewSize (pV->size); + pV->localToFrame (offset); + viewSize.offset (offset.x, offset.y); + getFrame ()->drawRect (pContext, viewSize); + } + } + ENDFOR + } + setDirty (false); + return; + } + #endif + if (bDirty) + draw (pContext); + else if (bDrawInOffscreen && pOffscreenContext) + { + bool doCopy = false; + if (isDirty ()) + doCopy = true; + + FOREACHSUBVIEW + pV->update (pOffscreenContext); + ENDFOR + + // transfert offscreen + if (doCopy) + pOffscreenContext->copyFrom (pContext, size); + } + else + { + long save[4]; + modifyDrawContext (save, pContext); + + FOREACHSUBVIEW + if (pV->isDirty ()) + { + long oldMode = 0; + CViewContainer* child = 0; + if (pV->notify (this, kMsgCheckIfViewContainer)) + { + child = (CViewContainer*)pV; + oldMode = child->getMode (); + child->setMode (kNormalUpdate); + } + CRect viewSize (pV->size); + drawBackgroundRect (pContext, viewSize); + pV->update (pContext); + if (child) + child->setMode (oldMode); + } + ENDFOR + + restoreDrawContext (pContext, save); + } +#endif + setDirty (false); + break; + } + } +} + +//----------------------------------------------------------------------------- +void CViewContainer::looseFocus (CDrawContext *pContext) +{ + FOREACHSUBVIEW + pV->looseFocus (pContext); + ENDFOR +} + +//----------------------------------------------------------------------------- +void CViewContainer::takeFocus (CDrawContext *pContext) +{ + FOREACHSUBVIEW + pV->takeFocus (pContext); + ENDFOR +} + +//----------------------------------------------------------------------------- +bool CViewContainer::advanceNextFocusView (CView* oldFocus, bool reverse) +{ + bool foundOld = false; + FOREACHSUBVIEW_REVERSE(reverse) + if (oldFocus && !foundOld) + { + if (oldFocus == pV) + { + foundOld = true; + continue; + } + } + else + { + if (pV->wantsFocus ()) + { + getFrame ()->setFocusView (pV); + return true; + } + else if (pV->isTypeOf ("CViewContainer")) + { + if (((CViewContainer*)pV)->advanceNextFocusView (0, reverse)) + return true; + } + } + ENDFOR + return false; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::isDirty () const +{ + if (bDirty) + return true; + + CRect viewSize (size); + viewSize.offset (-size.left, -size.top); + + FOREACHSUBVIEW + if (pV->isDirty ()) + { + CRect r (pV->size); + r.bound (viewSize); + if (r.getWidth () > 0 && r.getHeight () > 0) + return true; + } + ENDFOR + return false; +} + +//----------------------------------------------------------------------------- +CView *CViewContainer::getCurrentView () const +{ + if (!pParentFrame) + return 0; + + // get the current position + CPoint where; + pParentFrame->getCurrentLocation (where); + + frameToLocal (where); + + CCView *pSv = pLastView; + while (pSv) + { + CView *pV = pSv->pView; + if (pV && where.isInside (pV->mouseableArea)) + return pV; + pSv = pSv->pPrevious; + } + + return 0; +} + +//----------------------------------------------------------------------------- +CView *CViewContainer::getViewAt (const CPoint& p, bool deep) const +{ + if (!pParentFrame) + return 0; + + CPoint where (p); + + // convert to relativ pos + where.offset (-size.left, -size.top); + + CCView *pSv = pLastView; + while (pSv) + { + CView *pV = pSv->pView; + if (pV && where.isInside (pV->mouseableArea)) + { + if (deep) + { + if (pV->isTypeOf ("CViewContainer")) + return ((CViewContainer*)pV)->getViewAt (where, deep); + } + return pV; + } + pSv = pSv->pPrevious; + } + + return 0; +} + +//----------------------------------------------------------------------------- +CPoint& CViewContainer::frameToLocal (CPoint& point) const +{ + point.offset (-size.left, -size.top); + if (pParentView && pParentView->isTypeOf ("CViewContainer")) + return pParentView->frameToLocal (point); + return point; +} + +//----------------------------------------------------------------------------- +CPoint& CViewContainer::localToFrame (CPoint& point) const +{ + point.offset (size.left, size.top); + if (pParentView && pParentView->isTypeOf ("CViewContainer")) + return pParentView->localToFrame (point); + return point; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::removed (CView* parent) +{ + if (pOffscreenContext) + pOffscreenContext->forget (); + pOffscreenContext = 0; + + return true; +} + +//----------------------------------------------------------------------------- +bool CViewContainer::attached (CView* view) +{ + // create offscreen bitmap + if (!pOffscreenContext && bDrawInOffscreen) + pOffscreenContext = new COffscreenContext (pParentFrame, (long)size.width (), (long)size.height (), kBlackCColor); + + return true; +} + +//----------------------------------------------------------------------------- +void CViewContainer::useOffscreen (bool b) +{ + bDrawInOffscreen = b; + + if (!bDrawInOffscreen && pOffscreenContext) + { + pOffscreenContext->forget (); + pOffscreenContext = 0; + } +} + +//----------------------------------------------------------------------------- +void CViewContainer::modifyDrawContext (CCoord save[4], CDrawContext* pContext) +{ + // store + save[0] = pContext->offsetScreen.h; + save[1] = pContext->offsetScreen.v; + save[2] = pContext->offset.h; + save[3] = pContext->offset.v; + + pContext->offsetScreen.h += size.left; + pContext->offsetScreen.v += size.top; + pContext->offset.h += size.left; + pContext->offset.v += size.top; +} + +//----------------------------------------------------------------------------- +void CViewContainer::restoreDrawContext (CDrawContext* pContext, CCoord save[4]) +{ + // restore + pContext->offsetScreen.h = save[0]; + pContext->offsetScreen.v = save[1]; + pContext->offset.h = save[2]; + pContext->offset.v = save[3]; +} + +#if DEBUG +static long _debugDumpLevel = 0; +//----------------------------------------------------------------------------- +void CViewContainer::dumpInfo () +{ + static const char* modeString[] = { "Normal Update Mode", "Only Dirty Update Mode"}; + DebugPrint ("CViewContainer: Mode: %s, Offscreen:%s ", modeString[mode], bDrawInOffscreen ? "Yes" : "No"); + CView::dumpInfo (); +} + +//----------------------------------------------------------------------------- +void CViewContainer::dumpHierarchy () +{ + _debugDumpLevel++; + FOREACHSUBVIEW + for (long i = 0; i < _debugDumpLevel; i++) + DebugPrint ("\t"); + pV->dumpInfo (); + DebugPrint ("\n"); + if (pV->isTypeOf ("CViewContainer")) + ((CViewContainer*)pV)->dumpHierarchy (); + ENDFOR + _debugDumpLevel--; +} + +#endif + + +//----------------------------------------------------------------------------- +// CBitmap Implementation +//----------------------------------------------------------------------------- +/*! @class CBitmap +@section cbitmap_alphablend Alpha Blend and Transparency +With Version 3.0 of VSTGUI it is possible to use alpha blended bitmaps. This comes free on Mac OS X and with Windows you need to include libpng. +Per default PNG images will be rendered alpha blended. If you want to use a transparency color with PNG Bitmaps, you need to call setNoAlpha(true) on the bitmap and set the transparency color. +@section cbitmap_macos Classic Apple Mac OS +The Bitmaps are PICTs and stored inside the resource fork. +@section cbitmap_macosx Apple Mac OS X +The Bitmaps can be of type PNG, JPEG, PICT, BMP and are stored in the Resources folder of the plugin bundle. +They must be named bmp00100.png (or bmp00100.jpg, etc). The number is the resource id. +@section cbitmap_windows Microsoft Windows +The Bitmaps are .bmp files and must be included in the plug (usually using a .rc file). +It's also possible to use png as of version 3.0 if you define the macro USE_LIBPNG and include the libpng and zlib libraries/sources to your project. +*/ +CBitmap::CBitmap (AEffGUIEditor& editor, const char* filename) + : resourceID (resourceID), width (0), height (0), noAlpha (true) +{ +#if DEBUG + gNbCBitmap++; +#endif + + bool found = false; + long i = 0; + long ncolors, cpp; + + pHandle = 0; + pMask = 0; + + if (editor.getBundlePath() == NULL) { + std::cerr << "ERROR: No bundle path set, unable to load images" << std::endl; + } else { + std::string path = std::string(editor.getBundlePath()) + filename; + if (openPng(path.c_str())) // reads width, height + closePng(); + } + + setTransparentColor (kTransparentCColor); + +#if DEBUG + gBitmapAllocation += (long)height * (long)width; +#endif +} + +//----------------------------------------------------------------------------- +CBitmap::CBitmap (CFrame &frame, CCoord width, CCoord height) + : width (width), height (height), noAlpha (true), pMask (0) +{ +#if DEBUG + gNbCBitmap++; +#endif + + pHandle = (void*) XCreatePixmap (display, + frame.getBackBuffer (), + width, + height, + frame.getDepth ()); + + setTransparentColor (kTransparentCColor); +} + +//----------------------------------------------------------------------------- +CBitmap::CBitmap () + : resourceID (0) + , width (0) + , height (0) + , noAlpha (true) +{ + pMask = 0; + pHandle = 0; +} + +//----------------------------------------------------------------------------- +CBitmap::~CBitmap () +{ + dispose (); +} + +//----------------------------------------------------------------------------- +void CBitmap::dispose () +{ +#if DEBUG + gNbCBitmap--; + gBitmapAllocation -= (long)height * (long)width; +#endif + + if (pHandle) + XFreePixmap (display, (Pixmap)pHandle); + if (pMask) + XFreePixmap (display, (Pixmap)pMask); + + pHandle = 0; + pMask = 0; + + width = 0; + height = 0; + +} + +//----------------------------------------------------------------------------- +void *CBitmap::getHandle () const +{ + return pHandle; +} + +//----------------------------------------------------------------------------- +bool CBitmap::loadFromResource (long resourceID) +{ + dispose (); + fprintf (stderr, "CBitmap::loadFromResource not implemented\n"); + return false; +} + +//----------------------------------------------------------------------------- +bool CBitmap::loadFromPath (const void* platformPath) +{ + if (pHandle != NULL) { + dispose (); + closePng (); + } + + bool success = openPng ((char*)platformPath); + if (success) + closePng (); + + return success; +} + +//----------------------------------------------------------------------------- +bool CBitmap::isLoaded () const +{ + return (pHandle != NULL); +} + +//----------------------------------------------------------------------------- +void CBitmap::draw (CDrawContext *pContext, CRect &rect, const CPoint &offset) +{ + if (!pHandle) + { + // the first time try to decode the pixmap + pHandle = createPixmapFromPng (pContext); + if (!pHandle) + return; + } + + CFrame* frame = pContext->pFrame; + + XCopyArea (display, + (Drawable) pHandle, + (Drawable) frame->getBackBuffer(), + (GC) frame->getGC(), + offset.h, + offset.v, + std::min (rect.width (), getWidth()), + std::min (rect.height (), getHeight()), + rect.left + pContext->offset.h, + rect.top + pContext->offset.v); +} + +//----------------------------------------------------------------------------- +void CBitmap::drawTransparent (CDrawContext *pContext, CRect &rect, const CPoint &offset) +{ + if (!pHandle) + { + // the first time try to decode the pixmap + pHandle = createPixmapFromPng (pContext); + if (!pHandle) + return; + } + + CFrame* frame = pContext->pFrame; + + if (pMask == 0) + { + // get image from the pixmap + XImage* image = XGetImage (display, (Drawable)pHandle, + 0, 0, width, height, AllPlanes, ZPixmap); + assert (image); + + // create the bitmap mask + pMask = (void*) XCreatePixmap (display, (Drawable)frame->getWindow(), + width, height, 1); + assert (pMask); + + // create a associated GC + XGCValues values; + values.foreground = 1; + GC gc = XCreateGC (display, (Drawable)pMask, GCForeground, &values); + + // clear the mask + XFillRectangle (display, (Drawable)pMask, gc, 0, 0, width, height); + + // get the transparent color index + int color = pContext->getIndexColor (transparentCColor); + + // inverse the color + values.foreground = 0; + XChangeGC (display, gc, GCForeground, &values); + + // compute the mask + XPoint *points = new XPoint [height * width]; + int x, y, nbPoints = 0; + switch (image->depth) + { + case 8: + for (y = 0; y < height; y++) + { + char* src = image->data + (y * image->bytes_per_line); + + for (x = 0; x < width; x++) + { + if (src[x] == color) + { + points[nbPoints].x = x; + points[nbPoints].y = y; + nbPoints++; + } + } + } + break; + + case 24: { + int bytesPerPixel = image->bits_per_pixel >> 3; + char *lp = image->data; + for (y = 0; y < height; y++) + { + char* cp = lp; + for (x = 0; x < width; x++) + { + if (*(int*)cp == color) + { + points[nbPoints].x = x; + points[nbPoints].y = y; + nbPoints++; + } + cp += bytesPerPixel; + } + lp += image->bytes_per_line; + } + } break; + + default : + break; + } + + XDrawPoints (display, (Drawable)pMask, gc, + points, nbPoints, CoordModeOrigin); + + // free + XFreeGC (display, gc); + delete []points; + + // delete + XDestroyImage (image); + } + + // set the new clipmask + XGCValues value; + value.clip_mask = (Pixmap)pMask; + value.clip_x_origin = (rect.left + pContext->offset.h) - offset.h; + value.clip_y_origin = (rect.top + pContext->offset.v) - offset.v; + + XChangeGC (display, + (GC) /*frame->getGC(),*/ pContext->pSystemContext, + GCClipMask | GCClipXOrigin | GCClipYOrigin, + &value); + + XCopyArea (display, + (Drawable) pHandle, + (Drawable) frame->getBackBuffer(), + (GC) frame->getGC(), + offset.h, + offset.v, + rect.width (), + rect.height (), + rect.left + pContext->offset.h, + rect.top + pContext->offset.v); + + // unset the clipmask + XSetClipMask (display, (GC)frame->getGC(), None); +} + +//----------------------------------------------------------------------------- +void CBitmap::drawAlphaBlend (CDrawContext *pContext, CRect &rect, const CPoint &offset, unsigned char alpha) +{ + std::cout << "CBitmap::drawAlphaBlend (not implemented!)" << std::endl; +} + +//----------------------------------------------------------------------------- +void CBitmap::setTransparentColor (const CColor color) +{ + transparentCColor = color; +} + +//----------------------------------------------------------------------------- +void CBitmap::setTransparencyMask (CDrawContext* pContext, const CPoint& offset) +{ + // todo: implement me! +} + + +//----------------------------------------------------------------------------- +bool CBitmap::openPng (const char* path) +{ + assert(path); + + FILE* fp = fopen(path, "rb"); + if (!fp) { + fprintf(stderr, "Unable to open file %s\n", path); + return false; + } + + png_byte header[8]; + fread(header, 1, 8, fp); + if (png_sig_cmp(header, 0, 8)) { + fprintf(stderr, "File not recognized as a PNG image"); + return false; + } + + pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!pngRead) { + fprintf(stderr, "Unable to initialize libpng\n"); + return false; + } + + pngInfo = png_create_info_struct(pngRead); + if (!pngInfo) { + png_destroy_read_struct(&pngRead, NULL, NULL); + return false; + } + + png_init_io(pngRead, fp); + png_set_sig_bytes(pngRead, 8); + + png_read_info(pngRead, pngInfo); + + width = pngInfo->width; + height = pngInfo->height; + + pngFp = fp; + pngRead = pngRead; + pngPath = path; + + return true; +} + +//----------------------------------------------------------------------------- +bool CBitmap::closePng () +{ + png_destroy_read_struct(&pngRead, &pngInfo, NULL); + fclose(pngFp); + pngFp = NULL; + return true; +} + +//----------------------------------------------------------------------------- +void* CBitmap::createPixmapFromPng (CDrawContext *pContext) +{ + if (!openPng(pngPath.c_str())) + return NULL; + + assert(width > 0 && height > 0); + + png_byte** rows = (png_byte**)malloc(height * sizeof(png_byte*)); + for (int i = 0; i < height; ++i) + rows[i] = (png_byte*)(malloc(pngInfo->width * sizeof(uint32_t))); + + png_read_image(pngRead, rows); + + CFrame* frame = pContext->pFrame; + + Pixmap p = XCreatePixmap(display, frame->getBackBuffer(), + pngInfo->width, pngInfo->height, 24); + + XGCValues values; + values.foreground = 0xFFFFFFFF; + + // Draw + GC gc = XCreateGC (display, p, GCForeground, &values); + for (unsigned y = 0; y < pngInfo->height; y++) { + for (unsigned x = 0; x < pngInfo->width; ++x) { + char r = rows[y][(x*3)]; + char g = rows[y][(x*3)+1]; + char b = rows[y][(x*3)+2]; + uint32_t color = (r << 16) + (g << 8) + b; + XSetForeground(display, gc, color); + XDrawPoint(display, p, gc, x, y); + } + } + XFreeGC (display, gc); + + closePng(); + + return (void*)p; +} + +//----------------------------------------------------------------------------- +// CDragContainer Implementation +//----------------------------------------------------------------------------- +CDragContainer::CDragContainer (void* platformDrag) +: platformDrag (platformDrag) +, nbItems (0) +, iterator (0) +, lastItem (0) +{ +} + +//----------------------------------------------------------------------------- +CDragContainer::~CDragContainer () +{ + if (lastItem) + { + free (lastItem); + lastItem = 0; + } +} + +//----------------------------------------------------------------------------- +long CDragContainer::getType (long idx) const +{ + // not implemented + return kUnknown; +} + +//----------------------------------------------------------------------------- +void* CDragContainer::first (long& size, long& type) +{ + iterator = 0; + return next (size, type); +} + +//----------------------------------------------------------------------------- +void* CDragContainer::next (long& size, long& type) +{ + if (lastItem) + { + free (lastItem); + lastItem = 0; + } + size = 0; + type = kUnknown; + + // not implemented + + return NULL; +} + +} // namespace VSTGUI + +//----------------------------------------------------------------------------- +// return a degre value between [0, 360 * 64[ +static long convertPoint2Angle (CPoint &pm, CPoint &pt) +{ + long angle; + if (pt.h == pm.h) + { + if (pt.v < pm.v) + angle = 5760; // 90 * 64 + else + angle = 17280; // 270 * 64 + } + else if (pt.v == pm.v) + { + if (pt.h < pm.h) + angle = 11520; // 180 * 64 + else + angle = 0; + } + else + { + // 3666.9299 = 180 * 64 / pi + angle = (long)(3666.9298 * atan ((double)(pm.v - pt.v) / (double)(pt.h - pm.h))); + + if (pt.v < pm.v) + { + if (pt.h < pm.h) + angle += 11520; // 180 * 64 + } + else + { + if (pt.h < pm.h) + angle += 11520; // 180 * 64 + else + angle += 23040; // 360 * 64 + } + } + return angle; +} + + + +#if !PLUGGUI +//----------------------------------------------------------------------------- +// CFileSelector Implementation +//----------------------------------------------------------------------------- +#define stringAnyType "Any Type (*.*)" +#define stringAllTypes "All Types: (" +#define stringSelect "Select" +#define stringCancel "Cancel" +#define stringLookIn "Look in" +#define kPathMax 1024 + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +CFileSelector::CFileSelector (AudioEffectX* effect) +: effect (effect), vstFileSelect (0) +{} + +//----------------------------------------------------------------------------- +CFileSelector::~CFileSelector () +{ + if (vstFileSelect) + { + if (effect && effect->canHostDo ("closeFileSelector")) + effect->closeFileSelector (vstFileSelect); + else + { + if (vstFileSelect->reserved == 1 && vstFileSelect->returnPath) + { + delete []vstFileSelect->returnPath; + vstFileSelect->returnPath = 0; + vstFileSelect->sizeReturnPath = 0; + } + if (vstFileSelect->returnMultiplePaths) + { + for (long i = 0; i < vstFileSelect->nbReturnPath; i++) + { + delete []vstFileSelect->returnMultiplePaths[i]; + vstFileSelect->returnMultiplePaths[i] = 0; + } + delete[] vstFileSelect->returnMultiplePaths; + vstFileSelect->returnMultiplePaths = 0; + } + } + } +} + +//----------------------------------------------------------------------------- +long CFileSelector::run (VstFileSelect *vstFileSelect_) +{ + vstFileSelect = vstFileSelect_; + vstFileSelect->nbReturnPath = 0; + vstFileSelect->returnPath[0] = 0; + + if (effect + && effect->canHostDo ("openFileSelector") + && effect->canHostDo ("closeFileSelector")) + { + if (effect->openFileSelector (vstFileSelect)) + return vstFileSelect->nbReturnPath; + } + return 0; +} + +} // namespace VSTGUI + +//----------------------------------------------------------------------------- +#endif // !PLUGGUI +//----------------------------------------------------------------------------- + diff --git a/vstgui/vstgui.h b/vstgui/vstgui.h new file mode 100644 index 0000000..2ab0e7d --- /dev/null +++ b/vstgui/vstgui.h @@ -0,0 +1,1105 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * + * Which was based on: + * ---------------------------------------------------------------------------- + * VSTGUI: Graphical User Interface Framework for VST plugins + * Version 3.0 $Date: 2005/08/12 12:45:00 $ + * ---------------------------------------------------------------------------- + * VSTGUI LICENSE + * 2004, Steinberg Media Technologies, All Rights Reserved + * ---------------------------------------------------------------------------- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Steinberg Media Technologies nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef __vstgui__ +#define __vstgui__ + +// define global defines +#define MOTIF 1 +#define PNG 1 +#define DEBUG 1 + +#include "audioeffectx.h" +#include +#include +#include +#include + +// VSTGUI Version +#define VSTGUI_VERSION_MAJOR 3 +#define VSTGUI_VERSION_MINOR 0 + +struct ERect { + VstInt16 left; + VstInt16 top; + VstInt16 right; + VstInt16 bottom; +}; + +//---------------------------------------------------- +//---------------------------------------------------- +namespace VSTGUI { + +class CFrame; +class CDrawContext; +class COffscreenContext; +class CControl; +class CBitmap; + +} // namespace VSTGUI + +class AudioEffect; + +#ifndef __aeffeditor__ +#include "AEffEditor.hpp" +#endif + +//----------------------------------------------------------------------------- +// AEffGUIEditor Declaration +//----------------------------------------------------------------------------- +class AEffGUIEditor : public AEffEditor +{ +public: +//----------------------------------------------------------------------------- + AEffGUIEditor (AudioEffect* effect); + ~AEffGUIEditor (); + + // get the CFrame object + VSTGUI::CFrame* getFrame () { return frame; } + + virtual void setParameter (VstInt32 index, float value); + virtual void beginEdit (VstInt32 index); + virtual void endEdit (VstInt32 index); + + // feedback to application + virtual void doIdleStuff (); + + // wait (in ms) + void wait (unsigned int ms); + + // get the current time (in ms) + unsigned int getTicks (); + + // get version of this VSTGUI + static int getVstGuiVersion () { return (VSTGUI_VERSION_MAJOR << 16) + VSTGUI_VERSION_MINOR; } + + // get the knob mode + static VstInt32 getKnobMode () { return knobMode; } + + long getRect (ERect** rect); + void idle (); + void update (); + + void setBundlePath(const char* path) { bundlePath = strdup(path); } + const char* getBundlePath() { return bundlePath; } + +#if VST_2_1_EXTENSIONS + long onKeyDown (VstKeyCode& keyCode); + long onKeyUp (VstKeyCode& keyCode); + bool onWheel (float distance); + long setKnobMode (VstInt32 val); +#endif +//----------------------------------------------------------------------------- +protected: + ERect rect; + unsigned int lLastTicks; + bool inIdleStuff; + static VstInt32 knobMode; + char* bundlePath; + friend class VSTGUI::CFrame; + VSTGUI::CFrame* frame; +}; + +//---------------------------------------------------- +#include +#include +#include +#include +#include + +inline unsigned int _getTicks () +{ + // gettimeofday is not what we need here, checkout API for hw time + struct timeval tv; + struct timezone tz; + gettimeofday (&tv, &tz); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +struct VstKeyCode; + +namespace VSTGUI { + +struct CPoint; + +#define CLASS_METHODS(name, parent) \ + virtual bool isTypeOf (const char* s) const \ + { return (!strcmp (s, (#name))) ? true : parent::isTypeOf (s); }\ + +#ifdef VSTGUI_FLOAT_COORDINATES + typedef float CCoord; +#else + typedef long CCoord; +#endif + +//----------------------------------------------------------------------------- +// Structure CRect +//----------------------------------------------------------------------------- +struct CRect +{ + CRect (CCoord l = 0, CCoord t = 0, CCoord r = 0, CCoord b = 0) + : left (l), top (t), right (r), bottom (b) {} + CRect (const CRect& r) + : left (r.left), top (r.top), right (r.right), bottom (r.bottom) {} + CRect& operator () (CCoord l, CCoord t, CCoord r, CCoord b) + { + if (l < r) + this->left = l, this->right = r; + else + this->left = r, this->right = l; + if (top < bottom) + this->top = t, this->bottom = b; + else + this->top = b, this->bottom = t; + return *this; + } + + bool operator != (const CRect& other) const + { return (left != other.left || right != other.right || + top != other.top || bottom != other.bottom); } + + bool operator == (const CRect& other) const + { return (left == other.left && right == other.right && + top == other.top && bottom == other.bottom); } + + inline CCoord width () const { return right - left; } + inline CCoord height () const { return bottom - top; } + + inline CCoord getWidth () const { return right - left; } + inline CCoord getHeight () const { return bottom - top; } + + inline void setWidth (CCoord w) { right = left + w; } + inline void setHeight (CCoord h) { bottom = top + h; } + + CRect &offset (CCoord off_x, CCoord off_y) + { left += off_x; right += off_x; top += off_y; bottom += off_y; return *this; } + + CRect &inset (CCoord deltaX, CCoord deltaY) + { left += deltaX; right -= deltaX; top += deltaY; bottom -= deltaY; + return *this; } + + CRect &moveTo (CCoord dest_x, CCoord dest_y) + { CCoord vDiff = dest_y - top; CCoord hDiff = dest_x - left; + top += vDiff; bottom += vDiff; left += hDiff; right += hDiff; + return *this; } + + bool pointInside (const CPoint& where) const; // Checks if point is inside this rect + bool isEmpty () const; + + bool rectOverlap (const CRect& rect) const + { + if (right < rect.left) return false; + if (left > rect.right) return false; + if (bottom < rect.top) return false; + if (top > rect.bottom) return false; + return true; + } + + void bound (const CRect& rect); + + union + { CCoord left; CCoord x;}; + + union + { CCoord top; CCoord y;}; + + union + { CCoord right; CCoord x2;}; + + union + { CCoord bottom; CCoord y2;}; +}; + +//----------------------------------------------------------------------------- +// Structure CPoint +//----------------------------------------------------------------------------- +struct CPoint +{ + CPoint (CCoord a_h = 0, CCoord a_v = 0) : h (a_h), v (a_v) {} + CPoint& operator () (CCoord a_h, CCoord a_v) + { this->h = a_h; this->v = a_v; return *this; } + + bool isInside (CRect& r) const + { return h >= r.left && h <= r.right && v >= r.top && v <= r.bottom; } + + bool operator != (const CPoint &other) const + { return (h != other.h || v != other.v); } + + bool operator == (const CPoint &other) const + { return (h == other.h && v == other.v); } + + CPoint &offset (CCoord off_h, CCoord off_v) + { this->h += off_h; this->v += off_v; return *this; } + + union + { CCoord h; CCoord x;}; + + union + { CCoord v; CCoord y;}; +}; + +//----------------------------------------------------------------------------- +// Structure CColor +//----------------------------------------------------------------------------- +struct CColor +{ + CColor& operator () (unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char alpha) + { + this->red = red; + this->green = green; + this->blue = blue; + this->alpha = alpha; + return *this; + } + + CColor& operator = (const CColor& newColor) + { + red = newColor.red; + green = newColor.green; + blue = newColor.blue; + alpha = newColor.alpha; + return *this; + } + + CColor operator ~ () + { + CColor c; + c.red = ~red; + c.green = ~green; + c.blue = ~blue; + c.alpha = ~alpha; + return c; + } + + bool operator != (const CColor &other) const + { return (red != other.red || green != other.green || blue != other.blue || alpha != other.alpha); } + + bool operator == (const CColor &other) const + { return (red == other.red && green == other.green && blue == other.blue && alpha == other.alpha); } + + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +}; + +// define some basic colors +extern CColor kTransparentCColor; +extern CColor kBlackCColor; +extern CColor kWhiteCColor; +extern CColor kGreyCColor; +extern CColor kRedCColor; +extern CColor kGreenCColor; +extern CColor kBlueCColor; +extern CColor kYellowCColor; +extern CColor kCyanCColor; +extern CColor kMagentaCColor; + + +//----------------------------------------------------------------------------- +// Definitions of special characters in a platform independent way + +#define kDegreeSymbol "\xB0" +#define kInfiniteSymbol "oo" +#define kCopyrightSymbol "\xA9" +#define kTrademarkSymbol "\x99" +#define kRegisteredSymbol "\xAE" +#define kMicroSymbol "\x85" +#define kPerthousandSymbol "\x89" + +class CDragContainer; +class CCView; +class CAttributeListEntry; + +//----------------------------------------------------------------------------- +typedef intptr_t CViewAttributeID; +//----------------------------------------------------------------------------- +// Attributes +// all attributes where the first letter is lowercase are reserved for the vstgui lib + +extern const CViewAttributeID kCViewAttributeReferencePointer; // 'cvrp' + +//----------------------------------------------------------------------------- +//----------- +// Font Type +//----------- +enum CFont +{ + kSystemFont = 0, + kNormalFontVeryBig, + kNormalFontBig, + kNormalFont, + kNormalFontSmall, + kNormalFontSmaller, + kNormalFontVerySmall, + kSymbolFont, + + kNumStandardFonts +}; + +//----------- +// Text Face +//----------- +enum CTxtFace +{ + kNormalFace = 0, + kBoldFace = 1, + kItalicFace = 2, + kUnderlineFace = 4 +}; + +//----------- +// Line Style +//----------- +enum CLineStyle +{ + kLineSolid = 0, + kLineOnOffDash +}; + +//----------- +// Draw Mode +//----------- +enum CDrawMode +{ + kCopyMode = 0, + kOrMode, + kXorMode, + kAntialias +}; + +//---------------------------- +// Text Alignment (Horizontal) +//---------------------------- +enum CHoriTxtAlign +{ + kLeftText = 0, + kCenterText, + kRightText +}; + +//---------------------------- +// Buttons Type (+modifiers) +//---------------------------- +enum CButton +{ + kLButton = 1, + kMButton = 2, + kRButton = 4, + kShift = 8, + kControl = 16, + kAlt = 32, + kApple = 64 +}; + +//---------------------------- +// Cursor Type +//---------------------------- +enum CCursorType +{ + kCursorDefault = 0, + kCursorWait, + kCursorHSize, + kCursorVSize, + kCursorSizeAll, + kCursorNESWSize, + kCursorNWSESize, + kCursorCopy, + kCursorNotAllowed, + kCursorHand +}; + +//---------------------------- +// Knob Mode +//---------------------------- +enum CKnobMode +{ + kCircularMode = 0, + kRelativCircularMode, + kLinearMode +}; + +//---------------------------- +// Draw Style +//---------------------------- +enum CDrawStyle +{ + kDrawStroked = 0, + kDrawFilled, + kDrawFilledAndStroked +}; + +enum CMouseWheelAxis +{ + kMouseWheelAxisX = 0, + kMouseWheelAxisY +}; + +//----------------------------------------------------------------------------- +// CReferenceCounter Declaration (Reference Counting) +//----------------------------------------------------------------------------- +class CReferenceCounter +{ +public: + CReferenceCounter () : nbReference (1) {} + virtual ~CReferenceCounter () {} + + virtual void forget () { nbReference--; if (nbReference == 0) delete this; } + virtual void remember () { nbReference++; } + long getNbReference () const { return nbReference; } + +private: + long nbReference; +}; + +//----------------------------------------------------------------------------- +// CDrawContext Declaration +//! A drawing context encapsulates the drawing context of the underlying OS. It implements the drawing functions. +//----------------------------------------------------------------------------- +class CDrawContext : public CReferenceCounter +{ +public: + CDrawContext (CFrame *pFrame, void *pSystemContext, void *pWindow = 0); + virtual ~CDrawContext (); + + void moveTo (const CPoint &point); ///< move line position to point + void lineTo (const CPoint &point); ///< draw a line from current position to point + void drawLines (const CPoint* points, const long& numberOfLines); ///< draw multiple lines at once + + void drawPolygon (const CPoint *pPoints, long numberOfPoints, const CDrawStyle drawStyle = kDrawStroked); ///< draw a polygon + void polyLine (const CPoint *pPoint, long numberOfPoints); ///< draw a stroked polygon + void fillPolygon (const CPoint *pPoint, long numberOfPoints); ///< draw a filled polygon + + void drawRect (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked); ///< draw a stroked rect + void fillRect (const CRect &rect); ///< draw a filled rect + + void drawArc (const CRect &rect, const float startAngle1, const float endAngle2, const CDrawStyle drawStyle = kDrawStroked); ///< draw a stroked arc, where the angles are in degree + void drawArc (const CRect &rect, const CPoint &point1, const CPoint &point2); ///< draw a stroked arc between point1 and point2 + void fillArc (const CRect &rect, const CPoint &point1, const CPoint &point2); ///< draw a filled arc between point1 and point2 + + void drawEllipse (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked); ///< draw an ellipse + void fillEllipse (const CRect &rect); ///< draw a filled ellipse + + void drawPoint (const CPoint &point, CColor color); ///< draw a point + CColor getPoint (const CPoint& point); ///< \deprecated + + void floodFill (const CPoint& start); ///< \deprecated + + void setLineStyle (CLineStyle style); ///< set the current line style + CLineStyle getLineStyle () const { return lineStyle; } ///< get the current line style + + void setLineWidth (CCoord width); ///< set the current line width + CCoord getLineWidth () const { return frameWidth; } ///< get the current line width + + void setDrawMode (CDrawMode mode); ///< set the current draw mode, see CDrawMode + CDrawMode getDrawMode () const { return drawMode; } ///< get the current draw mode, see CDrawMode + + void setClipRect (const CRect &clip); ///< set the current clip + CRect &getClipRect (CRect &clip) const { clip = clipRect; clip.offset (-offset.h, -offset.v); return clip; } ///< get the current clip + void resetClipRect (); ///< reset the clip to the default state + + void setFillColor (const CColor color); ///< set current fill color + CColor getFillColor () const { return fillColor; } ///< get current fill color + + void setFrameColor (const CColor color); ///< set current stroke color + CColor getFrameColor () const { return frameColor; } ///< get current stroke color + + void setFontColor (const CColor color); ///< set current font color + CColor getFontColor () const { return fontColor; } ///< get current font color + void setFont (CFont fontID, const long size = 0, long style = 0); ///< set current font + CFont getFont () const { return fontId; } ///< get current font + long getFontSize () const { return fontSize; } ///< get current font size + + CCoord getStringWidth (const char* pStr); ///< get the width of a string + + void drawString (const char *pString, const CRect &rect, const short opaque = false, + const CHoriTxtAlign hAlign = kCenterText); ///< draw a string + + long getMouseButtons (); ///< get current mouse buttons + void getMouseLocation (CPoint &point); ///< get current mouse location. should not be used, see CView::getMouseLocation + bool waitDoubleClick (); ///< check if another mouse click occurs in the near future + bool waitDrag (); ///< check if the mouse will be dragged + +#if MOTIF + long getIndexColor (CColor color); + Colormap getColormap (); + Visual *getVisual (); + unsigned int getDepth (); + static long nbNewColor; +#endif + + void *getWindow () { return pWindow; } + void setWindow (void *ptr) { pWindow = ptr; } + void getLoc (CPoint &where) const { where = penLoc; } + CFrame* getFrame () const { return pFrame; } + + CPoint offsetScreen; + CPoint offset; + + void *getSystemContext () const { return pSystemContext; } + + virtual void forget (); + + //------------------------------------------- +protected: + + friend class CBitmap; + friend class COffscreenContext; + + void *pSystemContext; + void *pWindow; + CFrame *pFrame; + + long fontSize; + long fontStyle; + CFont fontId; + CColor fontColor; + CPoint penLoc; + + CCoord frameWidth; + CColor frameColor; + CColor fillColor; + CLineStyle lineStyle; + CDrawMode drawMode; + CRect clipRect; + +#if MOTIF + XFontStruct *pFontInfoStruct; +#endif +}; + + +//----------------------------------------------------------------------------- +// COffscreenContext Declaration +//! A drawing device which uses a pixmap as its drawing surface. +//----------------------------------------------------------------------------- +class COffscreenContext : public CDrawContext +{ +public: + COffscreenContext (CDrawContext *pContext, CBitmap *pBitmap, bool drawInBitmap = false); + COffscreenContext (CFrame *pFrame, long width, long height, const CColor backgroundColor = kBlackCColor); + + virtual ~COffscreenContext (); + + void copyFrom (CDrawContext *pContext, CRect destRect, CPoint srcOffset = CPoint (0, 0)); ///< copy from offscreen to pContext + void copyTo (CDrawContext* pContext, CRect& srcRect, CPoint destOffset = CPoint (0, 0)); ///< copy to offscreen from pContext + + inline CCoord getWidth () const { return width; } + inline CCoord getHeight () const { return height; } + + //------------------------------------------- +protected: + CBitmap *pBitmap; + CBitmap *pBitmapBg; + CCoord height; + CCoord width; + bool bDestroyPixmap; + + CColor backgroundColor; + +#if MOTIF + Display *pXdisplay; +#endif +}; + + +//----------------------------------------------------------------------------- +// CBitmap Declaration +//! Encapsulates various platform depended kinds of bitmaps. +//----------------------------------------------------------------------------- +class CBitmap : public CReferenceCounter +{ +public: + CBitmap (AEffGUIEditor& editor, const char* img_name); ///< Create from a filename + CBitmap (CFrame &frame, CCoord width, CCoord height); ///< Create a pixmap with a given size. + virtual ~CBitmap (); + + virtual void draw (CDrawContext *pContext, CRect &rect, const CPoint &offset = CPoint (0, 0)); ///< Draw the pixmap using a given rect as output position and a given offset of its source pixmap. + virtual void drawTransparent (CDrawContext *pContext, CRect &rect, const CPoint &offset = CPoint (0, 0)); + virtual void drawAlphaBlend (CDrawContext *pContext, CRect &rect, const CPoint &offset = CPoint (0, 0), unsigned char alpha = 128); ///< Same as CBitmap::draw except that it uses the alpha value to draw the bitmap alpha blended. + + inline CCoord getWidth () const { return width; } + inline CCoord getHeight () const { return height; } + + bool isLoaded () const; + void *getHandle () const; + + void setTransparentColor (const CColor color); + CColor getTransparentColor () const { return transparentCColor; } + void setTransparencyMask (CDrawContext* pContext, const CPoint& offset = CPoint (0, 0)); + + void setNoAlpha (bool state) { noAlpha = state; } + bool getNoAlpha () const { return noAlpha; } + + //------------------------------------------- +protected: + CBitmap (); + + virtual void dispose (); + virtual bool loadFromResource (long resourceID); + virtual bool loadFromPath (const void* platformPath); // platformPath is a C string + + long resourceID; + CCoord width; + CCoord height; + + CColor transparentCColor; + bool noAlpha; + +#if PNG + void *createPixmapFromPng (CDrawContext *pContext); + bool openPng (const char* path); + bool closePng (); + std::string pngPath; + png_structp pngRead; + png_infop pngInfo; + FILE* pngFp; +#endif +#if MOTIF + void *createPixmapFromXpm (CDrawContext *pContext); + + char **ppDataXpm; + void *pHandle; + void *pMask; +#endif +}; + +enum { + kMessageUnknown = 0, + kMessageNotified = 1 +}; + +//----------------------------------------------------------------------------- +// CView Declaration +//----------------------------------------------------------------------------- +class CView : public CReferenceCounter +{ +public: + CView (const CRect &size); + virtual ~CView (); + + virtual void draw (CDrawContext *pContext); ///< called if the view should draw itself + virtual void drawRect (CDrawContext *pContext, const CRect& updateRect) { draw (pContext); } ///< called if the view should draw itself + virtual bool checkUpdate (CRect& updateRect) const { return updateRect.rectOverlap (size); } + virtual void mouse (CDrawContext *pContext, CPoint &where, long buttons = -1); ///< called if a mouse click event occurs + + virtual void setBackground (CBitmap *background); ///< set the background image of this view + virtual CBitmap *getBackground () const { return pBackground; } ///< get the background image of this view + + virtual long onKeyDown (VstKeyCode& keyCode); ///< called if a key down event occurs and this view has focus + virtual long onKeyUp (VstKeyCode& keyCode); ///< called if a key up event occurs and this view has focus + + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); ///< called if a mouse wheel event is happening over this view + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance); ///< called if a mouse wheel event is happening over this view + + virtual bool onDrop (CDrawContext* context, CDragContainer* drag, const CPoint& where) { return false; } ///< called if a drag is dropped onto this view + virtual void onDragEnter (CDrawContext* context, CDragContainer* drag, const CPoint& where) {} ///< called if a drag is entering this view + virtual void onDragLeave (CDrawContext* context, CDragContainer* drag, const CPoint& where) {} ///< called if a drag is leaving this view + virtual void onDragMove (CDrawContext* context, CDragContainer* drag, const CPoint& where) {} ///< called if a drag is current moved over this view + + virtual void looseFocus (CDrawContext *pContext = 0); ///< called if view should loose focus + virtual void takeFocus (CDrawContext *pContext = 0); ///< called if view should take focus + + virtual bool isDirty () const { return bDirty; } ///< check if view is dirty + virtual void setDirty (const bool val = true) { bDirty = val; } ///< set the view to dirty so that it is redrawn in the next idle. Thread Safe ! + + virtual bool isVisible () const { return bVisible; } ///< check if view is dirty + virtual void setVisible (const bool val = true) { bVisible = val; } ///< set the view to dirty so that it is redrawn in the next idle. Thread Safe ! + + virtual void setMouseEnabled (const bool bEnable = true) { bMouseEnabled = bEnable; } ///< turn on/off mouse usage for this view + virtual bool getMouseEnabled () const { return bMouseEnabled; } ///< get the state of wheather this view uses the mouse or not + + virtual void setMouseableArea (const CRect &rect) { mouseableArea = rect; } ///< set the area in which the view reacts to the mouse + virtual CRect &getMouseableArea (CRect &rect) const { rect = mouseableArea; return rect;} ///< get the area in which the view reacts to the mouse + + virtual bool hitTest (const CPoint& where, const long buttons = -1) { return where.isInside (mouseableArea); } ///< check if where hits this view + + virtual void setTransparency (bool val) { bTransparencyEnabled = val; } ///< set views transparent state + virtual bool getTransparency () const { return bTransparencyEnabled; } ///< is view transparent ? + + CCoord getHeight () const { return size.height (); } ///< get the height of the view + CCoord getWidth () const { return size.width (); } ///< get the width of the view + + virtual void setViewSize (CRect &rect); ///< set views size + virtual CRect &getViewSize (CRect &rect) const { rect = size; return rect; } ///< returns the current view size + virtual const CRect &getViewSize () const { return size; } ///< returns the current view size + + virtual bool removed (CView* parent) { return true; } ///< view is removed from parent view + virtual bool attached (CView* view) { return true; } ///< view is attached to a parent view + + virtual void getMouseLocation (CDrawContext* context, CPoint &point); ///< get current mouse location in local view coordinates + + virtual CPoint& frameToLocal (CPoint& point) const; ///< conversion from frame coordinates to local view coordinates + virtual CPoint& localToFrame (CPoint& point) const; ///< conversion from local view coordinates to frame coordinates + + bool getAttributeSize (const CViewAttributeID id, long& outSize) const; ///< get the size of an attribute + bool getAttribute (const CViewAttributeID id, const long inSize, void* outData, long& outSize) const; ///< get an attribute + bool setAttribute (const CViewAttributeID id, const long inSize, void* inData); ///< set an attribute + + CView *getParentView () const { return pParentView; } + CFrame *getParent () const { return pParentFrame; } + CFrame *getFrame () const { return pParentFrame; } + virtual void *getEditor () const; + + virtual long notify (CView* sender, const char* message); + void redraw (); + virtual void redrawRect (CDrawContext* context, const CRect& rect); + + virtual bool wantsFocus () const { return bWantsFocus; } ///< check if view supports focus + virtual void setWantsFocus (bool state) { bWantsFocus = state; } ///< set focus support on/off + +#if DEBUG + virtual void dumpInfo (); +#endif + + virtual bool isTypeOf (const char* s) const + { return (!strcmp (s, "CView")); } + + virtual void update (CDrawContext *pContext); // don't call this !!! + + //------------------------------------------- +protected: + friend class CControl; + friend class CFrame; + friend class CViewContainer; + + CRect size; + CRect mouseableArea; + + CFrame *pParentFrame; + CView *pParentView; + + bool bDirty; + bool bMouseEnabled; + bool bTransparencyEnabled; + bool bWantsFocus; + bool bVisible; + + CBitmap* pBackground; + CAttributeListEntry* pAttributeList; +}; + +// Message to check if View is a CViewContainer +const extern char* kMsgCheckIfViewContainer; + +//----------------------------------------------------------------------------- +// CViewContainer Declaration +//! Container Class of CView objects. +//----------------------------------------------------------------------------- +class CViewContainer : public CView +{ +public: + CViewContainer (const CRect &size, CFrame *pParent, CBitmap *pBackground = 0); + virtual ~CViewContainer (); + + virtual void addView (CView *pView); ///< add a child view + virtual void addView (CView *pView, CRect &mouseableArea, bool mouseEnabled = true); ///< add a child view + virtual void removeView (CView *pView, const bool &withForget = true); ///< remove a child view + virtual void removeAll (const bool &withForget = true); ///< remove all child views + virtual bool isChild (CView *pView) const; ///< check if pView is a child view of this container + virtual long getNbViews () const; ///< get the number of child views + virtual CView *getView (long index) const; ///< get the child view at index + + virtual void setBackgroundColor (const CColor color); ///< set the background color (will only be drawn if this container is not set to transparent and does not have a background bitmap) + virtual CColor getBackgroundColor () const { return backgroundColor; } ///< get the background color + virtual void setBackgroundOffset (const CPoint &p) { backgroundOffset = p; } ///< set the offset of the background bitmap + virtual const CPoint& getBackgroundOffset () const { return backgroundOffset; } ///< get the offset of the background bitmap + + virtual void drawBackgroundRect (CDrawContext *pContext, CRect& _updateRect); ///< draw the background + + enum { + kNormalUpdate = 0, ///< this mode redraws the whole container if something is dirty + kOnlyDirtyUpdate ///< this mode only redraws the views which are dirty + }; + + virtual void setMode (long val) { mode = val; } ///< set the update mode + virtual long getMode () const { return mode; } ///< get the update mode + + virtual void useOffscreen (bool b); ///< turn on/off using an offscreen + + virtual CView *getCurrentView () const; ///< get the current view under the mouse + virtual CView *getViewAt (const CPoint& where, bool deep = false) const; ///< get the view at point where + + void modifyDrawContext (CCoord save[4], CDrawContext* pContext); + void restoreDrawContext (CDrawContext* pContext, CCoord save[4]); + + // CView + virtual void draw (CDrawContext *pContext); + virtual void drawRect (CDrawContext *pContext, const CRect& updateRect); + virtual void mouse (CDrawContext *pContext, CPoint &where, long buttons = -1); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance); + virtual void update (CDrawContext *pContext); + virtual bool hitTest (const CPoint& where, const long buttons = -1); + virtual long onKeyDown (VstKeyCode& keyCode); + virtual long onKeyUp (VstKeyCode& keyCode); + virtual long notify (CView* sender, const char* message); + + virtual bool onDrop (CDrawContext* context, CDragContainer* drag, const CPoint& where); + virtual void onDragEnter (CDrawContext* context, CDragContainer* drag, const CPoint& where); + virtual void onDragLeave (CDrawContext* context, CDragContainer* drag, const CPoint& where); + virtual void onDragMove (CDrawContext* context, CDragContainer* drag, const CPoint& where); + + virtual void looseFocus (CDrawContext *pContext = 0); + virtual void takeFocus (CDrawContext *pContext = 0); + virtual bool advanceNextFocusView (CView* oldFocus, bool reverse = false); + + virtual bool isDirty () const; + + virtual void setViewSize (CRect &rect); + + virtual bool removed (CView* parent); + virtual bool attached (CView* view); + + virtual CPoint& frameToLocal (CPoint& point) const; + virtual CPoint& localToFrame (CPoint& point) const; + + virtual void redrawRect (CDrawContext* context, const CRect& rect); + + CLASS_METHODS(CViewContainer, CView) + +#if DEBUG + virtual void dumpInfo (); + virtual void dumpHierarchy (); +#endif + + //------------------------------------------- +protected: + bool hitTestSubViews (const CPoint& where, const long buttons = -1); + + CCView *pFirstView; + CCView *pLastView; + long mode; + COffscreenContext *pOffscreenContext; + CColor backgroundColor; + CPoint backgroundOffset; + bool bDrawInOffscreen; + + CView* currentDragView; +}; + +//----------------------------------------------------------------------------- +// CFrame Declaration +//! The CFrame is the parent container of all views. +//----------------------------------------------------------------------------- +class CFrame : public CViewContainer +{ +public: + CFrame (const CRect &size, void *pSystemWindow, void *pEditor); + + virtual ~CFrame (); + + virtual bool open (); + virtual bool close (); + virtual bool isOpen () const { return bOpenFlag; } + + virtual void idle (); + virtual void doIdleStuff (); + + virtual unsigned long getTicks () const; ///< get the current time (in ms) + virtual long getKnobMode () const; ///< get hosts knob mode + + virtual bool setPosition (CCoord x, CCoord y); + virtual bool getPosition (CCoord &x, CCoord &y) const; + + virtual bool setSize (CCoord width, CCoord height); + virtual bool getSize (CRect *pSize) const; + virtual bool getSize (CRect &pSize) const; + + virtual long setModalView (CView *pView); + virtual CView *getModalView () const { return pModalView; } + + virtual void beginEdit (long index); + virtual void endEdit (long index); + + virtual bool getCurrentLocation (CPoint &where); + virtual void setCursor (CCursorType type); + + virtual void setFocusView (CView *pView); + virtual CView *getFocusView () const { return pFocusView; } + virtual bool advanceNextFocusView (CView* oldFocus, bool reverse = false); + + virtual bool setDropActive (bool val); + virtual bool isDropActive () const { return bDropActive; }; + + CDrawContext* createDrawContext (); + + virtual void setOpenFlag (bool val) { bOpenFlag = val;}; + virtual bool getOpenFlag () const { return bOpenFlag; }; + + virtual void invalidate (const CRect &rect); + + virtual bool updatesDisabled () const { return bUpdatesDisabled; } + virtual bool updatesDisabled (bool state) { bool before = bUpdatesDisabled; bUpdatesDisabled = state; return before; } + + void *getSystemWindow () const { return (void*)pSystemWindow; } + void *getParentSystemWindow () const { return (void*)pSystemWindow; } + void setParentSystemWindow (void *val) { pSystemWindow = (XdbeBackBuffer)val; } + + // CView + virtual void draw (CDrawContext *pContext); + virtual void drawRect (CDrawContext *pContext, const CRect& updateRect); + virtual void draw (CView *pView = 0); + virtual void mouse (CDrawContext *pContext, CPoint &where, long buttons = -1); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, float distance); + virtual bool onWheel (CDrawContext *pContext, const CPoint &where, const CMouseWheelAxis axis, float distance); + virtual long onKeyDown (VstKeyCode& keyCode); + virtual long onKeyUp (VstKeyCode& keyCode); + virtual void update (CDrawContext *pContext); + virtual void setViewSize (CRect& inRect); + virtual CView *getCurrentView () const; + + virtual void *getEditor () const { return pEditor; } + +#if MOTIF + Colormap getColormap () const { return colormap; } + Visual *getVisual () const { return pVisual; } + unsigned int getDepth () const { return depth; } + Window getWindow () const { return window; } + Drawable getBackBuffer () const { return backBuffer; } + void freeGc (); + + Region region; + + GC gc; + GC getGC () const { return gc; } +#endif + +#if DEBUG + virtual void dumpHierarchy (); +#endif + + CLASS_METHODS(CFrame, CViewContainer) + + //------------------------------------------- +protected: + bool initFrame (void *pSystemWin); + + void *pEditor; + + CView *pModalView; + CView *pFocusView; + + bool bFirstDraw; + bool bOpenFlag; + bool bDropActive; + bool bUpdatesDisabled; + +#if MOTIF + Colormap colormap; + Visual *pVisual; + Window window; + unsigned int depth; + XdbeBackBuffer backBuffer; + Window pSystemWindow; +#endif + + //------------------------------------------- +private: + CDrawContext *pFrameContext; + bool bAddedWindow; + void *defaultCursor; +}; + +//----------------------------------------------------------------------------- +// CDragContainer Declaration +//----------------------------------------------------------------------------- +class CDragContainer : public CReferenceCounter +{ +public: + CDragContainer (void* platformDrag); + ~CDragContainer (); + + void* first (long& size, long& type); ///< returns pointer on a char array if type is known + void* next (long& size, long& type); ///< returns pointer on a char array if type is known + + long getType (long idx) const; + long getCount () const { return nbItems; } + + enum { + kFile = 0, + kText, + + kUnknown = -1 + }; + +protected: + void* platformDrag; + long nbItems; + + long iterator; + void* lastItem; +}; + +//----------------------------------------------------------------------------- +// CCView Declaration +//----------------------------------------------------------------------------- +class CCView +{ +public: + CCView (CView *pView); + ~CCView (); + + CView *pView; + CCView *pNext; + CCView *pPrevious; +}; + +} // namespace VSTGUI + +// include the control objects +#ifndef __vstcontrols__ +#include "vstcontrols.h" +#endif + +#ifndef USE_NAMESPACE +using namespace VSTGUI; +#endif + +#endif // __vstgui__ diff --git a/vstgui/vstkeycode.h b/vstgui/vstkeycode.h new file mode 100644 index 0000000..1eba98c --- /dev/null +++ b/vstgui/vstkeycode.h @@ -0,0 +1,104 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VST Plug-Ins SDK Linux ONLY Port + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * ---------------------------------------------------------------------------- + */ + +#ifndef __vstkeycode__ +#define __vstkeycode__ + + +/** Structure and enum used for keyUp/keyDown */ +struct VstKeyCode +{ + long character; + unsigned char virt; ///< see enum VstVirtualKey + unsigned char modifier; ///< see enum VstModifierKey +}; + + +/** Used by member virt of VstKeyCode */ +enum VstVirtualKey +{ + VKEY_BACK = 1, + VKEY_TAB, + VKEY_CLEAR, + VKEY_RETURN, + VKEY_PAUSE, + VKEY_ESCAPE, + VKEY_SPACE, + VKEY_NEXT, + VKEY_END, + VKEY_HOME, + + VKEY_LEFT, + VKEY_UP, + VKEY_RIGHT, + VKEY_DOWN, + VKEY_PAGEUP, + VKEY_PAGEDOWN, + + VKEY_SELECT, + VKEY_PRINT, + VKEY_ENTER, + VKEY_SNAPSHOT, + VKEY_INSERT, + VKEY_DELETE, + VKEY_HELP, + VKEY_NUMPAD0, + VKEY_NUMPAD1, + VKEY_NUMPAD2, + VKEY_NUMPAD3, + VKEY_NUMPAD4, + VKEY_NUMPAD5, + VKEY_NUMPAD6, + VKEY_NUMPAD7, + VKEY_NUMPAD8, + VKEY_NUMPAD9, + VKEY_MULTIPLY, + VKEY_ADD, + VKEY_SEPARATOR, + VKEY_SUBTRACT, + VKEY_DECIMAL, + VKEY_DIVIDE, + VKEY_F1, + VKEY_F2, + VKEY_F3, + VKEY_F4, + VKEY_F5, + VKEY_F6, + VKEY_F7, + VKEY_F8, + VKEY_F9, + VKEY_F10, + VKEY_F11, + VKEY_F12, + VKEY_NUMLOCK, + VKEY_SCROLL, + + VKEY_SHIFT, + VKEY_CONTROL, + VKEY_ALT, + + VKEY_EQUALS +}; + +/** Used by member modifier of VstKeyCode */ +enum VstModifierKey +{ + MODIFIER_SHIFT = 1<<0, ///< Shift + MODIFIER_ALTERNATE = 1<<1, ///< Alt + MODIFIER_COMMAND = 1<<2, ///< Control on Mac + MODIFIER_CONTROL = 1<<3 ///< Ctrl on PC, Apple on Mac +}; + +#endif -- cgit v1.2.1