diff options
Diffstat (limited to 'libs')
120 files changed, 10647 insertions, 1883 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 58a2bbf825..98a1b362bb 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -14,7 +14,7 @@ ardour = env.Copy() domain = 'libardour' -ardour.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0) +ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0) ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") ardour.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") ardour.Append(PACKAGE = domain) @@ -44,6 +44,7 @@ buffer_set.cc meter.cc amp.cc panner.cc +destructive_filesource.cc audiofilesource.cc audiofilter.cc audioregion.cc @@ -64,7 +65,6 @@ crossfade.cc curve.cc cycle_timer.cc default_click.cc -destructive_filesource.cc gain.cc gdither.cc globals.cc @@ -105,7 +105,6 @@ sndfile_helpers.cc sndfilesource.cc source.cc source_factory.cc -state_manager.cc tempo.cc utils.cc version.cc @@ -242,7 +241,7 @@ ardour = conf.Finish () ardour.Merge ([ libraries['core'], libraries['xml'], - libraries['sndfile'], + libraries['sndfile-ardour'], libraries['raptor'], libraries['lrdf'], libraries['samplerate'], @@ -260,8 +259,6 @@ if ardour['LIBLO']: if ardour['COREAUDIO'] or ardour['AUDIOUNITS']: ardour.Merge ([ libraries['appleutility'] ]) -ardour.VersionBuild(['version.cc', 'ardour/version.h'], 'SConscript') - def SharedAsmObjectEmitter(target, source, env): for tgt in target: tgt.attributes.shared = 1 @@ -291,7 +288,9 @@ if env['NLS']: env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libardour)) - + +env.Alias('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], [])) + env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h', 'sse_functions.s', 'sse_functions_64bit.s' ] + ardour_files + osc_files + vst_files + coreaudio_files + audiounit_files + diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 76b76b1061..4a95e094a9 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -77,6 +77,8 @@ class AudioDiskstream : public Diskstream } void set_record_enabled (bool yn); + int set_destructive (bool yn); + bool can_become_destructive (bool& requires_bounce) const; float peak_power(uint32_t n=0) { float x = channels[n].peak_power; diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index fdf373a1cb..e87434b0fb 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -37,6 +37,9 @@ class AudioTrack : public Track AudioTrack (Session&, const XMLNode&); ~AudioTrack (); + int set_mode (TrackMode m); + bool can_use_mode (TrackMode m, bool& bounce_required); + int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, bool can_record, bool rec_monitors_input); @@ -63,6 +66,8 @@ class AudioTrack : public Track protected: XMLNode& state (bool full); + + int _set_state (const XMLNode&, bool call_base); private: int set_diskstream (boost::shared_ptr<AudioDiskstream>, void *); diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index 8a51580f27..88591ab845 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -1,6 +1,6 @@ /* Copyright (C) 2006 Paul Davis - Written by Taybin Rutkin + Written by Taybin Rutkin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index af5f5cdb60..bd609a7d80 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -31,6 +31,7 @@ struct SoundFileInfo { uint16_t channels; int64_t length; std::string format_name; + int64_t timecode; }; class AudioFileSource : public AudioSource { @@ -81,6 +82,8 @@ class AudioFileSource : public AudioSource { void mark_take (string); string take_id() const { return _take_id; } + bool is_embedded() const { return _is_embedded; } + static void set_bwf_serial_number (int); static void set_search_path (string); @@ -93,6 +96,9 @@ class AudioFileSource : public AudioSource { XMLNode& get_state (); int set_state (const XMLNode&); + bool destructive() const { return (_flags & Destructive); } + virtual bool set_destructive (bool yn) { return false; } + /* this should really be protected, but C++ is getting stricter and creating slots from protected member functions is starting to cause issues. @@ -121,9 +127,12 @@ class AudioFileSource : public AudioSource { string _path; Flag _flags; string _take_id; - uint64_t timeline_position; + int64_t timeline_position; bool file_is_new; + bool _is_embedded; + static bool determine_embeddedness(string path); + static string peak_dir; static string search_path; @@ -133,7 +142,7 @@ class AudioFileSource : public AudioSource { static uint64_t header_position_offset; - virtual void set_timeline_position (nframes_t pos); + virtual void set_timeline_position (int64_t pos); virtual void set_header_timeline_position () = 0; bool find (std::string path, bool must_exist, bool& is_new); diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index e14cfd0180..53c7e68b82 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -110,6 +110,7 @@ class AudioRegion : public Region void set_fade_out (FadeShape, nframes_t); void set_envelope_active (bool yn); + void set_default_envelope (); int separate_by_channel (ARDOUR::Session&, vector<AudioRegion*>&) const; @@ -143,7 +144,6 @@ class AudioRegion : public Region void set_default_fades (); void set_default_fade_in (); void set_default_fade_out (); - void set_default_envelope (); void recompute_gain_at_end (); void recompute_gain_at_start (); @@ -157,8 +157,11 @@ class AudioRegion : public Region void recompute_at_start (); void recompute_at_end (); - void envelope_changed (Change); + void envelope_changed (); + void fade_in_changed (); + void fade_out_changed (); void source_offset_changed (); + void listen_to_my_curves (); mutable Curve _fade_in; FadeShape _fade_in_shape; @@ -171,6 +174,13 @@ class AudioRegion : public Region protected: int set_live_state (const XMLNode&, Change&, bool send); + + virtual bool verify_start (jack_nframes_t); + virtual bool verify_start_and_length (jack_nframes_t, jack_nframes_t); + virtual bool verify_start_mutable (jack_nframes_t&_start); + virtual bool verify_length (jack_nframes_t); + /*virtual void recompute_at_start () = 0; + virtual void recompute_at_end () = 0;*/ }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index db82acf894..2ada255236 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -70,7 +70,7 @@ const nframes_t frames_per_peak = 256; uint32_t read_data_count() const { return _read_data_count; } uint32_t write_data_count() const { return _write_data_count; } - int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const; + virtual int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const; int build_peaks (); bool peaks_ready (sigc::slot<void>, sigc::connection&) const; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index 22ab706f82..e5c194e683 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -33,7 +33,6 @@ #include <pbd/statefuldestructible.h> #include <ardour/ardour.h> -#include <ardour/state_manager.h> namespace ARDOUR { @@ -54,14 +53,15 @@ struct ControlEvent { }; - class AutomationList : public StateManager, public PBD::StatefulDestructible +class AutomationList : public PBD::StatefulDestructible { public: typedef std::list<ControlEvent*> AutomationEventList; typedef AutomationEventList::iterator iterator; typedef AutomationEventList::const_iterator const_iterator; - AutomationList(double default_value, bool no_state = false); + AutomationList (double default_value); + AutomationList (const XMLNode&); ~AutomationList(); AutomationList (const AutomationList&); @@ -85,8 +85,9 @@ struct ControlEvent { void reposition_for_rt_add (double when); void rt_add (double when, double value); - iterator add (double when, double value, iterator, bool ignore_mode = false); - void add (double when, double value, bool for_loading = false); + void add (double when, double value); + /* this should be private but old-school automation loading needs it in IO/Redirect */ + void fast_simple_add (double when, double value); void reset_range (double start, double end); void erase_range (double start, double end); @@ -151,13 +152,12 @@ struct ControlEvent { (obj.*method)(*this); } - UndoAction get_memento () const; - - virtual void store_state (XMLNode& node) const; - virtual void load_state (const XMLNode&); + sigc::signal<void> StateChanged; - XMLNode &get_state(void); + XMLNode& get_state(void); int set_state (const XMLNode &s); + XMLNode& state (bool full); + XMLNode& serialize_events (); void set_max_xval (double); double get_max_xval() const { return max_xval; } @@ -188,12 +188,6 @@ struct ControlEvent { protected: - struct State : public ARDOUR::StateManager::State { - AutomationEventList events; - - State (std::string why) : ARDOUR::StateManager::State (why) {} - }; - AutomationEventList events; mutable Glib::Mutex lock; bool _frozen; @@ -215,7 +209,6 @@ struct ControlEvent { double min_yval; double max_yval; double default_value; - bool no_state; iterator rt_insertion_point; double rt_pos; @@ -242,14 +235,12 @@ struct ControlEvent { virtual double unlocked_eval (double where); - Change restore_state (StateManager::State&); - StateManager::State* state_factory (std::string why) const; - virtual ControlEvent* point_factory (double,double) const; virtual ControlEvent* point_factory (const ControlEvent&) const; - AutomationList* cut_copy_clear (double, double, int op); + + int deserialize_events (const XMLNode&); }; } // namespace diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index fe65e9d433..8044190066 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -80,7 +80,6 @@ CONFIG_VARIABLE (std::string, click_emphasis_sound, "click-emphasis-sound", "") CONFIG_VARIABLE (bool, auto_play, "auto-play", false) CONFIG_VARIABLE (bool, auto_return, "auto-return", false) CONFIG_VARIABLE (bool, auto_input, "auto-input", true) -CONFIG_VARIABLE (bool, auto_loop, "auto-loop", false) CONFIG_VARIABLE (bool, punch_in, "punch-in", false) CONFIG_VARIABLE (bool, punch_out, "punch-out", false) CONFIG_VARIABLE (bool, plugins_stop_with_transport, "plugins-stop-with-transport", false) diff --git a/libs/ardour/ardour/coreaudiosource.h b/libs/ardour/ardour/coreaudiosource.h index 668fe61102..bd69c78e18 100644 --- a/libs/ardour/ardour/coreaudiosource.h +++ b/libs/ardour/ardour/coreaudiosource.h @@ -38,6 +38,8 @@ class CoreAudioSource : public AudioFileSource { int flush_header () {return 0;}; void set_header_timeline_position () {}; + static int get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg); + protected: nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const; nframes_t write_unlocked (Sample *dst, nframes_t cnt) { return 0; } @@ -50,7 +52,7 @@ class CoreAudioSource : public AudioFileSource { mutable nframes_t tmpbufsize; mutable Glib::Mutex _tmpbuf_lock; - void init (const string &str); + void init (string str); }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/curve.h b/libs/ardour/ardour/curve.h index 87893ca260..df984b74e0 100644 --- a/libs/ardour/ardour/curve.h +++ b/libs/ardour/ardour/curve.h @@ -51,9 +51,10 @@ class Curve : public AutomationList ~Curve (); Curve (const Curve& other); Curve (const Curve& other, double start, double end); + Curve (const XMLNode&); - bool rt_safe_get_vector (double x0, double x1, float *arg, size_t veclen); - void get_vector (double x0, double x1, float *arg, size_t veclen); + bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen); + void get_vector (double x0, double x1, float *arg, int32_t veclen); AutomationEventList::iterator closest_control_point_before (double xval); AutomationEventList::iterator closest_control_point_after (double xval); @@ -66,22 +67,20 @@ class Curve : public AutomationList ControlEvent* point_factory (double,double) const; ControlEvent* point_factory (const ControlEvent&) const; - Change restore_state (StateManager::State&); - private: AutomationList::iterator last_bound; double unlocked_eval (double where); double multipoint_eval (double x); - void _get_vector (double x0, double x1, float *arg, size_t veclen); + void _get_vector (double x0, double x1, float *arg, int32_t veclen); }; } // namespace ARDOUR extern "C" { - void curve_get_vector_from_c (void *arg, double, double, float*, size_t); + void curve_get_vector_from_c (void *arg, double, double, float*, int32_t); } #endif /* __ardour_curve_h__ */ diff --git a/libs/ardour/ardour/destructive_filesource.h b/libs/ardour/ardour/destructive_filesource.h index 1e75042ce9..2e6f5d0e57 100644 --- a/libs/ardour/ardour/destructive_filesource.h +++ b/libs/ardour/ardour/destructive_filesource.h @@ -48,6 +48,8 @@ class DestructiveFileSource : public SndFileSource { static void setup_standard_crossfades (nframes_t sample_rate); + int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const; + protected: nframes_t write_unlocked (Sample *src, nframes_t cnt); @@ -66,7 +68,7 @@ class DestructiveFileSource : public SndFileSource { void init (); nframes_t crossfade (Sample* data, nframes_t cnt, int dir); - void set_timeline_position (nframes_t); + void set_timeline_position (int64_t); }; } diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index 7a779b69bf..eb6d936222 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -90,7 +90,8 @@ class IO; virtual void set_record_enabled (bool yn) = 0; bool destructive() const { return _flags & Destructive; } - virtual void set_destructive (bool yn); + virtual int set_destructive (bool yn) { return -1; } + virtual bool can_become_destructive (bool& requires_bounce) const { return false; } bool hidden() const { return _flags & Hidden; } bool recordable() const { return _flags & Recordable; } @@ -137,6 +138,8 @@ class IO; void handle_input_change (IOChange, void *src); + void remove_region_from_last_capture (boost::weak_ptr<Region> wregion); + sigc::signal<void> RecordEnableChanged; sigc::signal<void> SpeedChanged; sigc::signal<void> ReverseChanged; @@ -223,6 +226,7 @@ class IO; virtual bool realtime_set_speed (double, bool global_change); std::list<boost::shared_ptr<Region> > _last_capture_regions; + virtual int use_pending_capture_data (XMLNode& node) = 0; virtual void get_input_sources () = 0; diff --git a/libs/ardour/ardour/insert.h b/libs/ardour/ardour/insert.h index 5d917a8c2c..217fd89885 100644 --- a/libs/ardour/ardour/insert.h +++ b/libs/ardour/ardour/insert.h @@ -89,15 +89,6 @@ class PortInsert : public Insert int32_t compute_output_streams (int32_t cnt) const; }; -struct PluginInsertState : public RedirectState -{ - PluginInsertState (std::string why) - : RedirectState (why) {} - ~PluginInsertState() {} - - PluginState plugin_state; -}; - class PluginInsert : public Insert { public: @@ -112,9 +103,6 @@ class PluginInsert : public Insert XMLNode& get_state(void); int set_state(const XMLNode&); - StateManager::State* state_factory (std::string why) const; - Change restore_state (StateManager::State&); - void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); void silence (nframes_t nframes, nframes_t offset); @@ -160,9 +148,7 @@ class PluginInsert : public Insert nframes_t latency(); void transport_stopped (nframes_t now); - - protected: - void store_state (PluginInsertState&) const; + void automation_snapshot (nframes_t now); private: diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 56566149c0..f7e1993bb2 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -36,7 +36,6 @@ #include <ardour/ardour.h> #include <ardour/utils.h> -#include <ardour/state_manager.h> #include <ardour/curve.h> #include <ardour/types.h> #include <ardour/data_type.h> @@ -65,7 +64,7 @@ class BufferSet; * An IO can contain ports of varying types, making routes/inserts/etc with * varied combinations of types (eg MIDI and audio) possible. */ -class IO : public PBD::StatefulDestructible, public ARDOUR::StateManager +class IO : public PBD::StatefulDestructible { public: @@ -74,7 +73,9 @@ class IO : public PBD::StatefulDestructible, public ARDOUR::StateManager IO (Session&, string name, int input_min = -1, int input_max = -1, int output_min = -1, int output_max = -1, - DataType default_type = DataType::AUDIO); + DataType default_type = DataType::AUDIO); + + IO (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); virtual ~IO(); @@ -82,18 +83,12 @@ class IO : public PBD::StatefulDestructible, public ARDOUR::StateManager ChanCount input_maximum() const { return _input_maximum; } ChanCount output_minimum() const { return _output_minimum; } ChanCount output_maximum() const { return _output_maximum; } - + void set_input_minimum (ChanCount n); void set_input_maximum (ChanCount n); void set_output_minimum (ChanCount n); void set_output_maximum (ChanCount n); - // Do not write any new code using these - void set_input_minimum (int n); - void set_input_maximum (int n); - void set_output_minimum (int n); - void set_output_maximum (int n); - DataType default_type() const { return _default_type; } void set_default_type(DataType t) { _default_type = t; } @@ -187,9 +182,6 @@ class IO : public PBD::StatefulDestructible, public ARDOUR::StateManager XMLNode& get_state (void); int set_state (const XMLNode&); - virtual UndoAction get_memento() const; - - static int disable_connecting (void); static int enable_connecting (void); @@ -224,6 +216,14 @@ public: /* automation */ + static void set_automation_interval (jack_nframes_t frames) { + _automation_interval = frames; + } + + static jack_nframes_t automation_interval() { + return _automation_interval; + } + void clear_automation (); bool gain_automation_recording() const { @@ -245,6 +245,7 @@ public: sigc::signal<void> gain_automation_style_changed; virtual void transport_stopped (nframes_t now); + void automation_snapshot (nframes_t now); ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; } @@ -304,10 +305,8 @@ public: GainControllable _gain_control; - /* state management */ - - Change restore_state (State&); - StateManager::State* state_factory (std::string why) const; + nframes_t last_automation_snapshot; + static nframes_t _automation_interval; AutoState _gain_automation_state; AutoStyle _gain_automation_style; @@ -315,11 +314,12 @@ public: bool apply_gain_automation; Curve _gain_automation_curve; - int save_automation (const string&); - int load_automation (const string&); - Glib::Mutex automation_lock; + virtual int set_automation_state (const XMLNode&); + virtual XMLNode& get_automation_state (); + virtual int load_automation (std::string path); + /* AudioTrack::deprecated_use_diskstream_connections() needs these */ int set_inputs (const string& str); diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 1f1c02d67c..94f70bb4e8 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -36,7 +36,6 @@ #include <pbd/statefuldestructible.h> #include <ardour/ardour.h> -#include <ardour/state_manager.h> using std::string; @@ -131,7 +130,7 @@ class Location : public PBD::StatefulDestructible bool set_flag_internal (bool yn, Flags flag); }; -class Locations : public StateManager, public PBD::StatefulDestructible +class Locations : public PBD::StatefulDestructible { public: typedef std::list<Location *> LocationList; @@ -169,6 +168,7 @@ class Locations : public StateManager, public PBD::StatefulDestructible sigc::signal<void> changed; sigc::signal<void,Location*> added; sigc::signal<void,Location*> removed; + sigc::signal<void,Change> StateChanged; template<class T> void apply (T& obj, void (T::*method)(LocationList&)) { Glib::Mutex::Lock lm (lock); @@ -180,26 +180,14 @@ class Locations : public StateManager, public PBD::StatefulDestructible (obj.*method)(locations, arg); } - UndoAction get_memento () const; - private: - struct State : public ARDOUR::StateManager::State { - LocationList locations; - LocationList states; - - State (std::string why) : ARDOUR::StateManager::State (why) {} - }; - LocationList locations; Location *current_location; mutable Glib::Mutex lock; int set_current_unlocked (Location *); void location_changed (Location*); - - Change restore_state (StateManager::State&); - StateManager::State* state_factory (std::string why) const; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index e62121672f..583cc23de5 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -87,7 +87,7 @@ class MidiDiskstream : public Diskstream boost::shared_ptr<SMFSource> write_source () { return _write_source; } - void set_destructive (bool yn); // doom! + int set_destructive (bool yn); // doom! protected: friend class Session; diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index a5c578b7cd..2fa39a37df 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -84,9 +84,6 @@ class MidiRegion : public Region friend class Playlist; private: - StateManager::State* state_factory (std::string why) const; - Change restore_state (StateManager::State&); - jack_nframes_t _read_at (const SourceList&, MidiRingBuffer& dst, jack_nframes_t position, jack_nframes_t dur, diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 6cffe01318..0347df5669 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -58,7 +58,7 @@ public: int use_diskstream (string name); int use_diskstream (const PBD::ID& id); - void set_mode (TrackMode m); + int set_mode (TrackMode m); void set_latency_delay (jack_nframes_t); diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 5231120840..79bff7d2a5 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -85,18 +85,18 @@ class StreamPanner : public sigc::trackable, public Stateful virtual Curve& automation() = 0; - virtual int load (istream&, string path, uint32_t&) = 0; - - virtual int save (ostream&) const = 0; - sigc::signal<void> Changed; /* for position */ sigc::signal<void> StateChanged; /* for mute */ int set_state (const XMLNode&); virtual XMLNode& state (bool full_state) = 0; - + Panner & get_parent() { return parent; } + /* old school automation loading */ + + virtual int load (istream&, string path, uint32_t&) = 0; + protected: friend class Panner; Panner& parent; @@ -145,8 +145,6 @@ class BaseStereoPanner : public StreamPanner void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes); - int load (istream&, string path, uint32_t&); - int save (ostream&) const; void snapshot (nframes_t now); void transport_stopped (nframes_t frame); void set_automation_state (AutoState); @@ -154,6 +152,10 @@ class BaseStereoPanner : public StreamPanner Curve& automation() { return _automation; } + /* old school automation loading */ + + int load (istream&, string path, uint32_t&); + protected: float left; float right; @@ -207,10 +209,7 @@ class Multi2dPanner : public StreamPanner void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes); void distribute_automated (AudioBuffer& src, BufferSet& obufs, - nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers); - - int load (istream&, string path, uint32_t&); - int save (ostream&) const; + nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers); static StreamPanner* factory (Panner&); static string name; @@ -219,6 +218,10 @@ class Multi2dPanner : public StreamPanner XMLNode& get_state (void); int set_state (const XMLNode&); + /* old school automation loading */ + + int load (istream&, string path, uint32_t&); + private: Curve _automation; void update (); @@ -244,8 +247,6 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc:: /// The fundamental Panner function void distribute(BufferSet& src, BufferSet& dest, nframes_t start_frame, nframes_t end_frames, nframes_t nframes, nframes_t offset); - void set_name (string); - bool bypassed() const { return _bypassed; } void set_bypassed (bool yn); @@ -265,9 +266,6 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc:: AutoStyle automation_style() const; bool touching() const; - int load (); - int save () const; - XMLNode& get_state (void); XMLNode& state (bool full); int set_state (const XMLNode&); @@ -304,12 +302,14 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc:: void set_position (float x, StreamPanner& orig); void set_position (float x, float y, StreamPanner& orig); void set_position (float x, float y, float z, StreamPanner& orig); - + + /* old school automation */ + + int load (); + private: void distribute_no_automation(BufferSet& src, BufferSet& dest, nframes_t nframes, nframes_t offset, gain_t gain_coeff); - - string automation_path; Session& _session; uint32_t current_outs; bool _linked; @@ -317,6 +317,11 @@ class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc:: LinkDirection _link_direction; static float current_automation_version_number; + + /* old school automation handling */ + + std::string automation_path; + void set_name (std::string); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 93b30f42ef..831c9b3905 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -63,8 +63,8 @@ class Playlist : public PBD::StatefulDestructible { void unref(); uint32_t refcnt() const { return _refcnt; } - const string& name() const { return _name; } - void set_name (const string& str); + std::string name() const { return _name; } + void set_name (std::string str); const DataType& data_type() const { return _type; } @@ -73,6 +73,7 @@ class Playlist : public PBD::StatefulDestructible { bool hidden() const { return _hidden; } bool empty() const; + uint32_t n_regions() const; nframes_t get_maximum_extent () const; layer_t top_layer() const; @@ -91,19 +92,15 @@ class Playlist : public PBD::StatefulDestructible { void duplicate (boost::shared_ptr<Region>, nframes_t position, float times); void nudge_after (nframes_t start, nframes_t distance, bool forwards); - boost::shared_ptr<Region> find_region (const PBD::ID&) const; - Playlist* cut (list<AudioRange>&, bool result_is_hidden = true); Playlist* copy (list<AudioRange>&, bool result_is_hidden = true); int paste (Playlist&, nframes_t position, float times); - uint32_t read_data_count() { return _read_data_count; } - - RegionList* regions_at (nframes_t frame); - RegionList* regions_touched (nframes_t start, nframes_t end); + RegionList* regions_at (nframes_t frame); + RegionList* regions_touched (nframes_t start, nframes_t end); + boost::shared_ptr<Region> find_region (const PBD::ID&) const; boost::shared_ptr<Region> top_region_at (nframes_t frame); - - boost::shared_ptr<Region> find_next_region (nframes_t frame, RegionPoint point, int dir); + boost::shared_ptr<Region> find_next_region (nframes_t frame, RegionPoint point, int dir); template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>, void *), void *arg); template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>)); diff --git a/libs/ardour/ardour/redirect.h b/libs/ardour/ardour/redirect.h index ae58fa9b70..79ae4516c5 100644 --- a/libs/ardour/ardour/redirect.h +++ b/libs/ardour/ardour/redirect.h @@ -47,14 +47,6 @@ namespace ARDOUR { class Session; -struct RedirectState : public StateManager::State { - RedirectState (string why) - : StateManager::State (why) {} - ~RedirectState () {} - - bool active; -}; - class Redirect : public IO { public: @@ -99,9 +91,6 @@ class Redirect : public IO XMLNode& get_state (void); int set_state (const XMLNode&); - StateManager::State* state_factory (string why) const; - Change restore_state (StateManager::State&); - void *get_gui () const { return _gui; } void set_gui (void *p) { _gui = p; } @@ -110,9 +99,6 @@ class Redirect : public IO return 1.0f; } - int load_automation (string path); - int save_automation (string path); - void what_has_automation (set<uint32_t>&) const; void what_has_visible_automation (set<uint32_t>&) const; const set<uint32_t>& what_can_be_automated () const { return can_automate_list; } @@ -137,15 +123,19 @@ class Redirect : public IO void can_automate (uint32_t); set<uint32_t> can_automate_list; - void store_state (RedirectState&) const; - virtual void automation_list_creation_callback (uint32_t, AutomationList&) {} + int set_automation_state (const XMLNode&); + XMLNode& get_automation_state (); + private: bool _active; Placement _placement; uint32_t _sort_key; void* _gui; /* generic, we don't know or care what this is */ + + int old_set_automation_state (const XMLNode&); + int load_automation (std::string path); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 627e556cad..88bb294e5d 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -144,6 +144,7 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro void special_set_position (nframes_t); void nudge_position (long, void *src); + bool at_natural_position () const; void move_to_natural_position (void *src); void trim_start (nframes_t new_position, void *src); @@ -171,7 +172,7 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro void set_playlist (ARDOUR::Playlist*); void source_deleted (boost::shared_ptr<Source>); - + boost::shared_ptr<Source> source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; } uint32_t n_channels() const { return _sources.size(); } @@ -185,7 +186,7 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro virtual int set_state (const XMLNode&); virtual int set_live_state (const XMLNode&, Change&, bool send); - boost::shared_ptr<Region> get_parent(); + virtual boost::shared_ptr<Region> get_parent() const; uint64_t last_layer_op() const { return _last_layer_op; } void set_last_layer_op (uint64_t when); @@ -214,10 +215,10 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro void maybe_uncopy (); void first_edit (); - bool verify_start (jack_nframes_t); - bool verify_start_and_length (jack_nframes_t, jack_nframes_t); - bool verify_start_mutable (jack_nframes_t&_start); - bool verify_length (jack_nframes_t); + virtual bool verify_start (jack_nframes_t); + virtual bool verify_start_and_length (jack_nframes_t, jack_nframes_t); + virtual bool verify_start_mutable (jack_nframes_t&_start); + virtual bool verify_length (jack_nframes_t); virtual void recompute_at_start () = 0; virtual void recompute_at_end () = 0; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 5c71bb16d3..6bc37ee51e 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -72,8 +72,7 @@ class Route : public IO Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max, Flag flags = Flag(0), DataType default_type = DataType::AUDIO); - - Route (Session&, const XMLNode&); + Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); virtual ~Route(); std::string comment() { return _comment; } @@ -205,11 +204,6 @@ class Route : public IO sigc::signal<void,void*> SelectedChanged; - /* undo */ - - UndoAction get_memento() const; - void set_state (state_id_t); - int set_control_outs (const vector<std::string>& ports); IO* control_outs() { return _control_outs; } @@ -238,6 +232,7 @@ class Route : public IO return _mute_control; } + void automation_snapshot (nframes_t now); void protect_automation (); void set_remote_control_id (uint32_t id); @@ -317,13 +312,14 @@ class Route : public IO sigc::connection input_signal_connection; - state_id_t _current_state_id; ChanCount redirect_max_outs; uint32_t _remote_control_id; uint32_t pans_required() const; ChanCount n_process_buffers (); + virtual int _set_state (const XMLNode&, bool call_base); + private: void init (); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index f06c4117a6..039bf92362 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -359,7 +359,10 @@ class Session : public PBD::StatefulDestructible void request_bounded_roll (nframes_t start, nframes_t end); void request_stop (bool abort = false); void request_locate (nframes_t frame, bool with_roll = false); + void request_play_loop (bool yn); + bool get_play_loop () const { return play_loop; } + nframes_t last_transport_start() const { return _last_roll_location; } void goto_end () { request_locate (end_location->start(), false);} void goto_start () { request_locate (start_location->start(), false); } @@ -562,7 +565,8 @@ class Session : public PBD::StatefulDestructible string new_region_name (string); string path_from_region_name (string name, string identifier); - boost::shared_ptr<Region> find_whole_file_parent (Region&); + boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>); + void find_equivalent_playlist_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >& result); boost::shared_ptr<Region> XMLRegionFactory (const XMLNode&, bool full); @@ -646,6 +650,7 @@ class Session : public PBD::StatefulDestructible uint32_t n_playlists() const; template<class T> void foreach_playlist (T *obj, void (T::*func)(Playlist *)); + void get_playlists (std::vector<Playlist*>&); /* named selections */ @@ -757,16 +762,19 @@ class Session : public PBD::StatefulDestructible /* History (for editors, mixers, UIs etc.) */ void undo (uint32_t n) { - history.undo (n); + _history.undo (n); } + void redo (uint32_t n) { - history.redo (n); + _history.redo (n); } - uint32_t undo_depth() const { return history.undo_depth(); } - uint32_t redo_depth() const { return history.redo_depth(); } - string next_undo() const { return history.next_undo(); } - string next_redo() const { return history.next_redo(); } + UndoHistory& history() { return _history; } + + uint32_t undo_depth() const { return _history.undo_depth(); } + uint32_t redo_depth() const { return _history.redo_depth(); } + string next_undo() const { return _history.next_undo(); } + string next_redo() const { return _history.next_redo(); } void begin_reversible_command (string cmd_name); void commit_reversible_command (Command* cmd = 0); @@ -775,11 +783,11 @@ class Session : public PBD::StatefulDestructible current_trans->add_command (cmd); } - std::map<PBD::ID, PBD::StatefulDestructible*> registry; + std::map<PBD::ID, PBD::StatefulThingWithGoingAway*> registry; // these commands are implemented in libs/ardour/session_command.cc Command *memento_command_factory(XMLNode *n); - void register_with_memento_command_factory(PBD::ID, PBD::StatefulDestructible *); + void register_with_memento_command_factory(PBD::ID, PBD::StatefulThingWithGoingAway *); class GlobalSoloStateCommand : public Command { @@ -944,6 +952,8 @@ class Session : public PBD::StatefulDestructible private: int create (bool& new_session, string* mix_template, nframes_t initial_length); + nframes_t compute_initial_length (); + static const char* _template_suffix; static const char* _statefile_suffix; static const char* _pending_suffix; @@ -1566,7 +1576,7 @@ class Session : public PBD::StatefulDestructible void reverse_diskstream_buffers (); - UndoHistory history; + UndoHistory _history; UndoTransaction* current_trans; GlobalRouteBooleanState get_global_route_boolean (bool (Route::*method)(void) const); @@ -1658,7 +1668,7 @@ class Session : public PBD::StatefulDestructible void* ptr, float opt); - /* number of hardware audio ports we're using, + /* number of hardware ports we're using, based on max (requested,available) */ diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 50fd5e6839..1d07f6888d 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -36,10 +36,7 @@ class SndFileSource : public AudioFileSource { /* constructor to be called for new in-session files */ SndFileSource (Session&, std::string path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate, - Flag flags = AudioFileSource::Flag (AudioFileSource::Writable| - AudioFileSource::Removable| - AudioFileSource::RemovableIfEmpty| - AudioFileSource::CanRename)); + Flag flags = SndFileSource::default_writable_flags); /* constructor to be called for existing in-session files */ @@ -53,6 +50,18 @@ class SndFileSource : public AudioFileSource { nframes_t natural_position () const; + nframes_t last_capture_start_frame() const; + void mark_capture_start (nframes_t); + void mark_capture_end (); + void clear_capture_marks(); + + bool set_destructive (bool yn); + + static void setup_standard_crossfades (nframes_t sample_rate); + static const AudioFileSource::Flag default_writable_flags; + + static int get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg); + protected: void set_header_timeline_position (); @@ -69,10 +78,32 @@ class SndFileSource : public AudioFileSource { mutable float *interleave_buf; mutable nframes_t interleave_bufsize; - void init (const string &str); + void init (string str); int open(); void close(); int setup_broadcast_info (nframes_t when, struct tm&, time_t); + + /* destructive */ + + static nframes_t xfade_frames; + static gain_t* out_coefficient; + static gain_t* in_coefficient; + + bool _capture_start; + bool _capture_end; + nframes_t capture_start_frame; + nframes_t file_pos; // unit is frames + nframes_t xfade_out_count; + nframes_t xfade_in_count; + Sample* xfade_buf; + + nframes_t crossfade (Sample* data, nframes_t cnt, int dir); + void set_timeline_position (int64_t); + nframes_t destructive_write_unlocked (Sample *dst, nframes_t cnt); + nframes_t nondestructive_write_unlocked (Sample *dst, nframes_t cnt); + void handle_header_position_change (); + + static int64_t get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 8bf66f8b8d..e94b1af54f 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -22,6 +22,7 @@ #define __ardour_source_h__ #include <string> +#include <set> #include <sigc++/signal.h> @@ -33,6 +34,7 @@ namespace ARDOUR { class Session; +class Playlist; class Source : public PBD::StatefulDestructible { @@ -57,22 +59,34 @@ class Source : public PBD::StatefulDestructible virtual void mark_for_remove() = 0; virtual void mark_streaming_write_completed () = 0; - + XMLNode& get_state (); int set_state (const XMLNode&); + void use () { _in_use++; } + void disuse () { if (_in_use) { _in_use--; } } + + void add_playlist (ARDOUR::Playlist*); + void remove_playlist (ARDOUR::Playlist*); + + uint32_t used() const; + + static sigc::signal<void,Source*> SourceCreated; protected: void update_length (jack_nframes_t pos, jack_nframes_t cnt); - + Session& _session; string _name; DataType _type; time_t _timestamp; jack_nframes_t _length; + std::set<ARDOUR::Playlist*> _playlists; + private: + uint32_t _in_use; }; } diff --git a/libs/ardour/ardour/state_manager.h b/libs/ardour/ardour/state_manager.h deleted file mode 100644 index e123b2cb37..0000000000 --- a/libs/ardour/ardour/state_manager.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef __ardour_state_manager_h__ -#define __ardour_state_manager_h__ - -#include <list> -#include <string> -#include <set> - -#include <sigc++/signal.h> - -#include <ardour/ardour.h> - -namespace ARDOUR { - -typedef uint32_t state_id_t; - -class StateManager : public virtual sigc::trackable -{ - public: - struct State { - std::string operation; - State (std::string why) : operation (why) {} - virtual ~State() {} - }; - - typedef std::list<State*> StateMap; - - StateManager (); - virtual ~StateManager (); - - virtual void drop_all_states (); - virtual void use_state (state_id_t); - virtual void save_state (std::string why); - - sigc::signal<void,Change> StateChanged; - - state_id_t _current_state_id; - - virtual bool should_save_state () const { return true; } - - static void prohibit_save (); - static void allow_save (const char* why, bool dosave); - - protected: - static bool _allow_save; - static sigc::signal<void,const char*> SaveAllowed; - - StateMap states; - - virtual Change restore_state (State&) = 0; - virtual State* state_factory (std::string why) const = 0; - virtual void send_state_changed (Change); -}; - -} // namespace ARDOUR - -#endif /* __ardour_state_manager_h__ */ diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 7cdbe56cf2..f8751b5d2b 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -34,7 +34,6 @@ #include <sigc++/signal.h> #include <ardour/ardour.h> -#include <ardour/state_manager.h> class XMLNode; @@ -162,17 +161,7 @@ class TempoSection : public MetricSection, public Tempo { typedef list<MetricSection*> Metrics; -class TempoMapState : public StateManager::State { - public: - TempoMapState (std::string why) - : StateManager::State (why) { - metrics = new Metrics; - } - - Metrics *metrics; -}; - -class TempoMap : public StateManager, public PBD::StatefulDestructible +class TempoMap : public PBD::StatefulDestructible { public: @@ -246,8 +235,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible void dump (std::ostream&) const; void clear (); - UndoAction get_memento() const; - /* this is a helper class that we use to be able to keep track of which meter *AND* tempo are in effect at a given point in time. @@ -279,6 +266,8 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible Metric metric_at (nframes_t) const; void bbt_time_with_metric (nframes_t, BBT_Time&, const Metric&) const; + sigc::signal<void,ARDOUR::Change> StateChanged; + private: static Tempo _default_tempo; static Meter _default_meter; @@ -309,16 +298,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible int move_metric_section (MetricSection&, const BBT_Time& to); void do_insert (MetricSection* section); - - Change restore_state (StateManager::State&); - StateManager::State* state_factory (std::string why) const; - - bool in_set_state; - - /* override state_manager::save_state so we can check in_set_state */ - - void save_state (std::string why); - }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 243d0db46d..d7a2da2f46 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -39,6 +39,11 @@ class Track : public Route int set_name (string str, void *src); + TrackMode mode () const { return _mode; } + virtual int set_mode (TrackMode m) { return false; } + virtual bool can_use_mode (TrackMode m, bool& bounce_required) { return false; } + sigc::signal<void> TrackModeChanged; + virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, bool can_record, bool rec_monitors_input) = 0; @@ -57,9 +62,6 @@ class Track : public Route virtual int use_diskstream (string name) = 0; virtual int use_diskstream (const PBD::ID& id) = 0; - TrackMode mode() const { return _mode; } - void set_mode (TrackMode m); - nframes_t update_total_latency(); void set_latency_delay (nframes_t); @@ -88,7 +90,6 @@ class Track : public Route void set_meter_point (MeterPoint, void* src); - sigc::signal<void> ModeChanged; sigc::signal<void> DiskstreamChanged; sigc::signal<void> FreezeChange; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 61e5f35c95..1138b5208f 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -89,11 +89,17 @@ namespace ARDOUR { Play = 0x4 }; + std::string auto_state_to_string (AutoState); + AutoState string_to_auto_state (std::string); + enum AutoStyle { Absolute = 0x1, Trim = 0x2 }; + std::string auto_style_to_string (AutoStyle); + AutoStyle string_to_auto_style (std::string); + enum AlignStyle { CaptureTime, ExistingMaterial diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index 7b8246868c..8034341bcc 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -31,7 +31,6 @@ #include <sigc++/signal.h> #include <pbd/stateful.h> #include <jack/types.h> -#include <ardour/plugin_state.h> #include <ardour/plugin.h> using std::string; diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index f08d38a9b9..9c5f5233b7 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -383,7 +383,7 @@ AudioDiskstream::setup_destructive_playlist () void AudioDiskstream::use_destructive_playlist () { - /* this is called from the XML-based constructor. when its done, + /* this is called from the XML-based constructor or ::set_destructive. when called, we already have a playlist and a region, but we need to set up our sources for write. we use the sources associated with the (presumed single, full-extent) region. @@ -402,6 +402,10 @@ AudioDiskstream::use_destructive_playlist () throw failed_constructor(); } + /* be sure to stretch the region out to the maximum length */ + + region->set_length (max_frames - region->position(), this); + uint32_t n; ChannelList::iterator chan; @@ -409,6 +413,10 @@ AudioDiskstream::use_destructive_playlist () (*chan).write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n)); assert((*chan).write_source); (*chan).write_source->set_allow_remove_if_empty (false); + + /* this might be false if we switched modes, so force it */ + + (*chan).write_source->set_destructive (true); } /* the source list will never be reset for a destructive track */ @@ -1544,6 +1552,9 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca } else { + string whole_file_region_name; + whole_file_region_name = region_name_from_path (channels[0].write_source->name()); + /* Register a new region with the Session that describes the entire source. Do this first so that any sub-regions will obviously be @@ -1552,7 +1563,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca try { boost::shared_ptr<Region> rx (RegionFactory::create (srcs, channels[0].write_source->last_capture_start_frame(), total_capture, - region_name_from_path (channels[0].write_source->name()), + whole_file_region_name, 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile))); region = boost::dynamic_pointer_cast<AudioRegion> (rx); @@ -1575,7 +1586,8 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca for (buffer_position = channels[0].write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { string region_name; - _session.region_name (region_name, channels[0].write_source->name(), false); + + _session.region_name (region_name, whole_file_region_name, false); // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; @@ -1589,9 +1601,9 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca continue; /* XXX is this OK? */ } - _last_capture_regions.push_back (region); + region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr<Region>(region))); - // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; + _last_capture_regions.push_back (region); i_am_the_modifier++; _playlist->add_region (region, (*ci)->start); @@ -2234,3 +2246,70 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) return 0; } + +int +AudioDiskstream::set_destructive (bool yn) +{ + bool bounce_ignored; + + if (yn != destructive()) { + + if (yn) { + /* requestor should already have checked this and + bounced if necessary and desired + */ + if (!can_become_destructive (bounce_ignored)) { + return -1; + } + _flags |= Destructive; + use_destructive_playlist (); + } else { + _flags &= ~Destructive; + reset_write_sources (true, true); + } + } + + return 0; +} + +bool +AudioDiskstream::can_become_destructive (bool& requires_bounce) const +{ + if (!_playlist) { + requires_bounce = false; + return false; + } + + /* is there only one region ? */ + + if (_playlist->n_regions() != 1) { + requires_bounce = true; + return false; + } + + boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1); + assert (first); + + /* do the source(s) for the region cover the session start position ? */ + + if (first->position() != _session.current_start_frame()) { + if (first->start() > _session.current_start_frame()) { + requires_bounce = true; + return false; + } + } + + /* is the source used by only 1 playlist ? */ + + boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first); + + assert (afirst); + + if (afirst->source()->used() > 1) { + requires_bounce = true; + return false; + } + + requires_bounce = false; + return true; +} diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 94b80df178..335cb020ae 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -546,7 +546,7 @@ AudioPlaylist::set_state (const XMLNode& node) } thaw (); - in_set_state++; + in_set_state--; return 0; } @@ -639,12 +639,10 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region) { RegionLock rlock (this); - RegionList::iterator i; - RegionList::iterator tmp; - for (i = regions.begin(); i != regions.end(); ) { + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { - tmp = i; + RegionList::iterator tmp = i; ++tmp; if ((*i) == region) { @@ -654,6 +652,21 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region) i = tmp; } + + for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) { + + set<boost::shared_ptr<Region> >::iterator xtmp = x; + ++xtmp; + + if ((*x) == region) { + all_regions.erase (x); + changed = true; + } + + x = xtmp; + } + + region->set_playlist (0); } for (c = _crossfades.begin(); c != _crossfades.end(); ) { diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index a6cbce2c1e..f2681aceba 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -65,7 +65,7 @@ AudioTrack::AudioTrack (Session& sess, string name, Route::Flag flag, TrackMode AudioTrack::AudioTrack (Session& sess, const XMLNode& node) : Track (sess, node) { - set_state (node); + _set_state (node, false); } AudioTrack::~AudioTrack () @@ -73,6 +73,37 @@ AudioTrack::~AudioTrack () } int +AudioTrack::set_mode (TrackMode m) +{ + if (m != _mode) { + + if (_diskstream->set_destructive (m == Destructive)) { + return -1; + } + + _mode = m; + + TrackModeChanged (); /* EMIT SIGNAL */ + } + + return 0; +} + +bool +AudioTrack::can_use_mode (TrackMode m, bool& bounce_required) +{ + switch (m) { + case Normal: + bounce_required = false; + return true; + + case Destructive: + default: + return _diskstream->can_become_destructive (bounce_required); + } +} + +int AudioTrack::deprecated_use_diskstream_connections () { boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); @@ -88,10 +119,10 @@ AudioTrack::deprecated_use_diskstream_connections () diskstream->deprecated_io_node = 0; - set_input_minimum (-1); - set_input_maximum (-1); - set_output_minimum (-1); - set_output_maximum (-1); + set_input_minimum (ChanCount::ZERO); + set_input_maximum (ChanCount::INFINITE); + set_output_minimum (ChanCount::ZERO); + set_output_maximum (ChanCount::INFINITE); if ((prop = node.property ("gain")) != 0) { set_gain (atof (prop->value().c_str()), this); @@ -188,11 +219,19 @@ AudioTrack::audio_diskstream() const int AudioTrack::set_state (const XMLNode& node) { + return _set_state (node, true); +} + +int +AudioTrack::_set_state (const XMLNode& node, bool call_base) +{ const XMLProperty *prop; XMLNodeConstIterator iter; - if (Route::set_state (node)) { - return -1; + if (call_base) { + if (Route::_set_state (node, call_base)) { + return -1; + } } if ((prop = node.property (X_("mode"))) != 0) { @@ -494,6 +533,16 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t transport_frame; boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); + { + Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + if (lm.locked()) { + // automation snapshot can also be called from the non-rt context + // and it uses the redirect list, so we take the lock out here + automation_snapshot (start_frame); + } + } + + if (n_outputs().get_total() == 0 && _redirects.empty()) { return 0; } @@ -761,7 +810,7 @@ AudioTrack::freeze (InterThreadInfo& itt) return; } - if (_session.write_one_audio_track (*this, 0, _session.current_end_frame(), true, srcs, itt)) { + if (_session.write_one_audio_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt)) { return; } @@ -780,8 +829,7 @@ AudioTrack::freeze (InterThreadInfo& itt) FreezeRecordInsertInfo* frii = new FreezeRecordInsertInfo ((*r)->get_state(), insert); frii->id = insert->id(); - frii->memento = (*r)->get_memento(); - + _freeze_record.insert_info.push_back (frii); /* now deactivate the insert */ @@ -802,7 +850,7 @@ AudioTrack::freeze (InterThreadInfo& itt) false)); new_playlist->set_orig_diskstream_id (diskstream->id()); - new_playlist->add_region (region, 0); + new_playlist->add_region (region, _session.current_start_frame()); new_playlist->set_frozen (true); region->set_locked (true); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 70541fce55..b0cd64c8d1 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -21,6 +21,7 @@ #include <unistd.h> #include <cerrno> #include <vector> +#include <exception> #include <glibmm/timer.h> #include <pbd/pthread_utils.h> diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 3078521466..16cb990ec2 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -36,7 +36,6 @@ #include <ardour/audiofilesource.h> #include <ardour/sndfile_helpers.h> #include <ardour/sndfilesource.h> -#include <ardour/destructive_filesource.h> #include <ardour/session.h> #include <ardour/source_factory.h> @@ -66,6 +65,7 @@ AudioFileSource::AudioFileSource (Session& s, string idstr, Flag flags) : AudioSource (s, idstr), _flags (flags) { /* constructor used for existing external to session files. file must exist already */ + _is_embedded = AudioFileSource::determine_embeddedness (idstr); if (init (idstr, true)) { throw failed_constructor (); @@ -77,6 +77,7 @@ AudioFileSource::AudioFileSource (Session& s, std::string path, Flag flags, Samp : AudioSource (s, path), _flags (flags) { /* constructor used for new internal-to-session files. file cannot exist */ + _is_embedded = false; if (init (path, false)) { throw failed_constructor (); @@ -106,6 +107,12 @@ AudioFileSource::~AudioFileSource () } bool +AudioFileSource::determine_embeddedness (std::string path) +{ + return (path.find("/") == 0); +} + +bool AudioFileSource::removable () const { return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0)); @@ -170,80 +177,16 @@ bool AudioFileSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg) { #ifdef HAVE_COREAUDIO - OSStatus err = noErr; - FSRef ref; - ExtAudioFileRef af = 0; - size_t size; - CFStringRef name; - - err = FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0); - if (err != noErr) { - ExtAudioFileDispose (af); - goto libsndfile; - } - - err = ExtAudioFileOpen(&ref, &af); - if (err != noErr) { - ExtAudioFileDispose (af); - goto libsndfile; + if (CoreAudioSource::get_soundfile_info (path, _info, error_msg) == 0) { + return true; } - - AudioStreamBasicDescription absd; - memset(&absd, 0, sizeof(absd)); - size = sizeof(AudioStreamBasicDescription); - err = ExtAudioFileGetProperty(af, - kExtAudioFileProperty_FileDataFormat, &size, &absd); - if (err != noErr) { - ExtAudioFileDispose (af); - goto libsndfile; - } - - _info.samplerate = absd.mSampleRate; - _info.channels = absd.mChannelsPerFrame; - - size = sizeof(_info.length); - err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length); - if (err != noErr) { - ExtAudioFileDispose (af); - goto libsndfile; - } - - size = sizeof(CFStringRef); - err = AudioFormatGetProperty( - kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name); - if (err != noErr) { - ExtAudioFileDispose (af); - goto libsndfile; - } - - _info.format_name = CFStringRefToStdString(name); - - ExtAudioFileDispose (af); - return true; - -libsndfile: #endif // HAVE_COREAUDIO - SNDFILE *sf; - SF_INFO sf_info; - - sf_info.format = 0; // libsndfile says to clear this before sf_open(). - - if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) { - char errbuf[256]; - error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1); - return false; + if (SndFileSource::get_soundfile_info (path, _info, error_msg) != 0) { + return true; } - sf_close (sf); - - _info.samplerate = sf_info.samplerate; - _info.channels = sf_info.channels; - _info.length = sf_info.frames; - _info.format_name = string_compose("Format: %1, %2", - sndfile_major_format(sf_info.format), - sndfile_minor_format(sf_info.format)); - return true; + return false; } XMLNode& @@ -277,6 +220,17 @@ AudioFileSource::set_state (const XMLNode& node) } + if ((prop = node.property (X_("name"))) != 0) { + _is_embedded = AudioFileSource::determine_embeddedness (prop->value()); + } else { + _is_embedded = false; + } + + if ((prop = node.property (X_("destructive"))) != 0) { + /* old style, from the period when we had DestructiveFileSource */ + _flags = Flag (_flags | Destructive); + } + return 0; } @@ -318,6 +272,11 @@ AudioFileSource::mark_take (string id) int AudioFileSource::move_to_trash (const string trash_dir_name) { + if (is_embedded()) { + cerr << "tried to move an embedded region to trash" << endl; + return -1; + } + string newpath; if (!writable()) { @@ -465,7 +424,11 @@ AudioFileSource::find (string pathstr, bool must_exist, bool& isnew) /* external files and/or very very old style sessions include full paths */ _path = pathstr; - _name = pathstr.substr (pathstr.find_last_of ('/') + 1); + if (is_embedded()) { + _name = pathstr; + } else { + _name = pathstr.substr (pathstr.find_last_of ('/') + 1); + } if (access (_path.c_str(), R_OK) != 0) { @@ -521,7 +484,7 @@ AudioFileSource::handle_header_position_change () } void -AudioFileSource::set_timeline_position (nframes_t pos) +AudioFileSource::set_timeline_position (int64_t pos) { timeline_position = pos; } diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 0f4e3807b1..939f9c02dd 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -75,7 +75,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n set_default_fades (); set_default_envelope (); - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); } /* Basic AudioRegion constructor (one channel) */ @@ -95,7 +95,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n set_default_fades (); set_default_envelope (); - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); } /* Basic AudioRegion constructor (many channels) */ @@ -110,7 +110,7 @@ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, c set_default_fades (); set_default_envelope (); - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); } @@ -148,7 +148,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t _scale_amplitude = other->_scale_amplitude; - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); assert(_type == DataType::AUDIO); } @@ -165,7 +165,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) _fade_in_disabled = 0; _fade_out_disabled = 0; - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); assert(_type == DataType::AUDIO); } @@ -187,7 +187,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod throw failed_constructor(); } - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); assert(_type == DataType::AUDIO); } @@ -205,16 +205,69 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) throw failed_constructor(); } - _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + listen_to_my_curves (); assert(_type == DataType::AUDIO); } AudioRegion::~AudioRegion () { - cerr << "====== " << _name << " DESTRUCTOR @ " << this << endl; - notify_callbacks (); - GoingAway (); /* EMIT SIGNAL */ +} + +void +AudioRegion::listen_to_my_curves () +{ + _envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed)); + _fade_in.StateChanged.connect (mem_fun (*this, &AudioRegion::fade_in_changed)); + _fade_out.StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed)); +} + +bool +AudioRegion::verify_length (nframes_t len) +{ + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(source()); + + if (afs && afs->destructive()) { + return true; + } else { + return Region::verify_length(len); + } +} + +bool +AudioRegion::verify_start_and_length (nframes_t new_start, nframes_t new_length) +{ + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(source()); + + if (afs && afs->destructive()) { + return true; + } else { + return verify_start_and_length(new_start, new_length); + } +} + +bool +AudioRegion::verify_start (nframes_t pos) +{ + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(source()); + + if (afs && afs->destructive()) { + return true; + } else { + return verify_start(pos); + } +} + +bool +AudioRegion::verify_start_mutable (nframes_t& new_start) +{ + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(source()); + + if (afs && afs->destructive()) { + return true; + } else { + return verify_start_mutable(new_start); + } } void @@ -239,7 +292,7 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra if (chan_n >= _sources.size()) { return 0; } - + if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_unit)) { return 0; } else { @@ -450,7 +503,7 @@ AudioRegion::state (bool full) if ((_flags & DefaultFadeIn)) { child->add_property (X_("default"), X_("yes")); } else { - _fade_in.store_state (*child); + child->add_child_nocopy (_fade_in.get_state ()); } child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes")); @@ -460,9 +513,9 @@ AudioRegion::state (bool full) if ((_flags & DefaultFadeOut)) { child->add_property (X_("default"), X_("yes")); } else { - _fade_out.store_state (*child); + child->add_child_nocopy (_fade_out.get_state ()); } - + child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes")); } @@ -473,6 +526,7 @@ AudioRegion::state (bool full) // If there are only two points, the points are in the start of the region and the end of the region // so, if they are both at 1.0f, that means the default region. + if (_envelope.size() == 2 && _envelope.front()->value == 1.0f && _envelope.back()->value==1.0f) { @@ -484,7 +538,7 @@ AudioRegion::state (bool full) if (default_env) { child->add_property ("default", "yes"); } else { - _envelope.store_state (*child); + child->add_child_nocopy (_envelope.get_state ()); } } else { @@ -545,34 +599,28 @@ AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool sen _envelope.clear (); - if ((prop = child->property ("default")) != 0) { + if ((prop = child->property ("default")) != 0 || _envelope.set_state (*child)) { set_default_envelope (); - } else { - _envelope.load_state (*child); } _envelope.set_max_xval (_length); _envelope.truncate_end (_length); - + } else if (child->name() == "FadeIn") { _fade_in.clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { + if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0 || _fade_in.set_state (*child)) { set_default_fade_in (); - } else { - _fade_in.load_state (*child); - } + } } else if (child->name() == "FadeOut") { _fade_out.clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { + if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0 || _fade_out.set_state (*child)) { set_default_fade_out (); - } else { - _fade_out.load_state (*child); - } + } } } @@ -1030,7 +1078,19 @@ AudioRegion::normalize_to (float target_dB) } void -AudioRegion::envelope_changed (Change ignored) +AudioRegion::fade_in_changed () +{ + send_change (FadeInChanged); +} + +void +AudioRegion::fade_out_changed () +{ + send_change (FadeOutChanged); +} + +void +AudioRegion::envelope_changed () { send_change (EnvelopeChanged); } @@ -1083,8 +1143,10 @@ AudioRegion::speed_mismatch (float sr) const void AudioRegion::source_offset_changed () { - if (boost::dynamic_pointer_cast<DestructiveFileSource>(_sources.front())) { - set_start (source()->natural_position(), this); + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front()); + + if (afs && afs->destructive()) { + // set_start (source()->natural_position(), this); set_position (source()->natural_position(), this); } } diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 419fe9240c..93165b7fe4 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -368,26 +368,29 @@ AudioSource::initialize_peakfile (bool newfile, string audio_path) error << string_compose(_("AudioSource: cannot stat peakfile \"%1\""), peakpath) << endmsg; return -1; } - } else { - /* we found it in the peaks dir */ - } - - if (statbuf.st_size == 0) { _peaks_built = false; + } else { - // Check if the audio file has changed since the peakfile was built. - struct stat stat_file; - int err = stat (audio_path.c_str(), &stat_file); - if (!err && stat_file.st_mtime > statbuf.st_mtime){ + /* we found it in the peaks dir, so check it out */ + + if (statbuf.st_size == 0) { _peaks_built = false; } else { - _peaks_built = true; + // Check if the audio file has changed since the peakfile was built. + struct stat stat_file; + int err = stat (audio_path.c_str(), &stat_file); + + if (!err && stat_file.st_mtime > statbuf.st_mtime){ + _peaks_built = false; + } else { + _peaks_built = true; + } } } } - + if (!newfile && !_peaks_built && _build_missing_peakfiles && _build_peakfiles) { build_peaks_from_scratch (); } diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index f286b11607..5cc2f50e38 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -22,9 +22,11 @@ #include <climits> #include <float.h> #include <cmath> +#include <sstream> #include <algorithm> #include <sigc++/bind.h> #include <ardour/automation_event.h> +#include <pbd/convert.h> #include "i18n.h" @@ -46,14 +48,13 @@ static void dumpit (const AutomationList& al, string prefix = "") } #endif -AutomationList::AutomationList (double defval, bool with_state) +AutomationList::AutomationList (double defval) { _frozen = false; changed_when_thawed = false; _state = Off; _style = Absolute; _touching = false; - no_state = with_state; min_yval = FLT_MIN; max_yval = FLT_MAX; max_xval = 0; // means "no limit" @@ -63,10 +64,6 @@ AutomationList::AutomationList (double defval, bool with_state) lookup_cache.left = -1; lookup_cache.range.first = events.end(); - if (!no_state) { - save_state (_("initial")); - } - AutomationListCreated(this); } @@ -83,7 +80,6 @@ AutomationList::AutomationList (const AutomationList& other) _touching = other._touching; _dirty = false; rt_insertion_point = events.end(); - no_state = other.no_state; lookup_cache.left = -1; lookup_cache.range.first = events.end(); @@ -111,7 +107,6 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl _touching = other._touching; _dirty = false; rt_insertion_point = events.end(); - no_state = other.no_state; lookup_cache.left = -1; lookup_cache.range.first = events.end(); @@ -128,32 +123,36 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl delete section; mark_dirty (); + AutomationListCreated(this); } -AutomationList::~AutomationList() +AutomationList::AutomationList (const XMLNode& node) { - std::set<ControlEvent*> all_events; - AutomationList::State* asp; + _frozen = false; + changed_when_thawed = false; + _touching = false; + min_yval = FLT_MIN; + max_yval = FLT_MAX; + max_xval = 0; // means "no limit" + _dirty = false; + _state = Off; + _style = Absolute; + rt_insertion_point = events.end(); + lookup_cache.left = -1; + lookup_cache.range.first = events.end(); + + set_state (node); - GoingAway (); + AutomationListCreated(this); +} +AutomationList::~AutomationList() +{ + GoingAway (); + for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) { - all_events.insert (*x); - } - - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - - if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) { - - for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) { - all_events.insert (*x); - } - } - } - - for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) { - delete (*i); + delete (*x); } } @@ -194,7 +193,7 @@ AutomationList::maybe_signal_changed () if (_frozen) { changed_when_thawed = true; } else { - StateChanged (Change (0)); + StateChanged (); } } @@ -236,9 +235,6 @@ AutomationList::clear () { Glib::Mutex::Lock lm (lock); events.clear (); - if (!no_state) { - save_state (_("cleared")); - } mark_dirty (); } @@ -270,7 +266,6 @@ void AutomationList::_x_scale (double factor) (*i)->when = floor ((*i)->when * factor); } - save_state ("x-scaled"); mark_dirty (); } @@ -370,12 +365,19 @@ AutomationList::rt_add (double when, double value) maybe_signal_changed (); } +void +AutomationList::fast_simple_add (double when, double value) +{ + /* to be used only for loading pre-sorted data from saved state */ + events.insert (events.end(), point_factory (when, value)); +} + #undef last_rt_insertion_point void -AutomationList::add (double when, double value, bool for_loading) +AutomationList::add (double when, double value) { - /* this is for graphical editing and loading data from storage */ + /* this is for graphical editing */ { Glib::Mutex::Lock lm (lock); @@ -407,15 +409,9 @@ AutomationList::add (double when, double value, bool for_loading) } mark_dirty (); - - if (!no_state && !for_loading) { - save_state (_("added event")); - } } - if (!for_loading) { - maybe_signal_changed (); - } + maybe_signal_changed (); } void @@ -425,9 +421,6 @@ AutomationList::erase (AutomationList::iterator i) Glib::Mutex::Lock lm (lock); events.erase (i); reposition_for_rt_add (0); - if (!no_state) { - save_state (_("removed event")); - } mark_dirty (); } maybe_signal_changed (); @@ -440,9 +433,6 @@ AutomationList::erase (AutomationList::iterator start, AutomationList::iterator Glib::Mutex::Lock lm (lock); events.erase (start, end); reposition_for_rt_add (0); - if (!no_state) { - save_state (_("removed multiple events")); - } mark_dirty (); } maybe_signal_changed (); @@ -471,10 +461,6 @@ AutomationList::reset_range (double start, double endt) reset = true; - if (!no_state) { - save_state (_("removed range")); - } - mark_dirty (); } } @@ -502,9 +488,6 @@ AutomationList::erase_range (double start, double endt) events.erase (s, e); reposition_for_rt_add (0); erased = true; - if (!no_state) { - save_state (_("removed range")); - } mark_dirty (); } @@ -532,10 +515,6 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double ++start; } - if (!no_state) { - save_state (_("event range adjusted")); - } - mark_dirty (); } @@ -554,10 +533,6 @@ AutomationList::modify (iterator iter, double when, double val) Glib::Mutex::Lock lm (lock); (*iter)->when = when; (*iter)->value = val; - if (!no_state) { - save_state (_("event adjusted")); - } - mark_dirty (); } @@ -609,44 +584,10 @@ AutomationList::thaw () { _frozen = false; if (changed_when_thawed) { - StateChanged(Change(0)); /* EMIT SIGNAL */ + StateChanged(); /* EMIT SIGNAL */ } } -StateManager::State* -AutomationList::state_factory (std::string why) const -{ - State* state = new State (why); - - for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) { - state->events.push_back (point_factory (**x)); - } - - return state; -} - -Change -AutomationList::restore_state (StateManager::State& state) -{ - { - Glib::Mutex::Lock lm (lock); - State* lstate = dynamic_cast<State*> (&state); - - events.clear (); - for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) { - events.push_back (point_factory (**x)); - } - } - - return Change (0); -} - -UndoAction -AutomationList::get_memento () const -{ - return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id); -} - void AutomationList::set_max_xval (double x) { @@ -670,10 +611,6 @@ AutomationList::truncate_end (double last_coordinate) double last_val; if (events.empty()) { - fatal << _("programming error:") - << "AutomationList::truncate_end() called on an empty list" - << endmsg; - /*NOTREACHED*/ return; } @@ -1083,9 +1020,6 @@ AutomationList::cut_copy_clear (double start, double end, int op) if (changed) { reposition_for_rt_add (0); - if (!no_state) { - save_state (_("cut/copy/clear")); - } } mark_dirty (); @@ -1115,10 +1049,6 @@ AutomationList::copy (iterator start, iterator end) x = tmp; } - - if (!no_state) { - save_state (_("copy")); - } } return nal; @@ -1183,11 +1113,6 @@ AutomationList::paste (AutomationList& alist, double pos, float times) } reposition_for_rt_add (0); - - if (!no_state) { - save_state (_("paste")); - } - mark_dirty (); } @@ -1207,64 +1132,220 @@ AutomationList::point_factory (const ControlEvent& other) const return new ControlEvent (other); } -void -AutomationList::store_state (XMLNode& node) const +XMLNode& +AutomationList::get_state () { + return state (true); +} + +XMLNode& +AutomationList::state (bool full) +{ + XMLNode* root = new XMLNode (X_("AutomationList")); + char buf[64]; LocaleGuard lg (X_("POSIX")); - for (const_iterator i = const_begin(); i != const_end(); ++i) { - char buf[64]; - - XMLNode *pointnode = new XMLNode ("point"); - - snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*i)->when)); - pointnode->add_property ("x", buf); - snprintf (buf, sizeof (buf), "%.12g", (*i)->value); - pointnode->add_property ("y", buf); + root->add_property ("id", _id.to_s()); - node.add_child_nocopy (*pointnode); + snprintf (buf, sizeof (buf), "%.12g", default_value); + root->add_property ("default", buf); + snprintf (buf, sizeof (buf), "%.12g", min_yval); + root->add_property ("min_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", max_yval); + root->add_property ("max_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", max_xval); + root->add_property ("max_xval", buf); + + if (full) { + root->add_property ("state", auto_state_to_string (_state)); + } else { + /* never save anything but Off for automation state to a template */ + root->add_property ("state", auto_state_to_string (Off)); } + + root->add_property ("style", auto_style_to_string (_style)); + + if (!events.empty()) { + root->add_child_nocopy (serialize_events()); + } + + return *root; } -void -AutomationList::load_state (const XMLNode& node) +XMLNode& +AutomationList::serialize_events () { - const XMLNodeList& elist = node.children(); - XMLNodeConstIterator i; - XMLProperty* prop; - nframes_t x; - double y; + XMLNode* node = new XMLNode (X_("events")); + stringstream str; + + for (iterator xx = events.begin(); xx != events.end(); ++xx) { + str << (double) (*xx)->when; + str << ' '; + str <<(double) (*xx)->value; + str << '\n'; + } + + /* XML is a bit wierd */ + + XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */ + content_node->set_content (str.str()); + + node->add_child_nocopy (*content_node); + + return *node; +} + +int +AutomationList::deserialize_events (const XMLNode& node) +{ + if (node.children().empty()) { + return -1; + } + + XMLNode* content_node = node.children().front(); + if (content_node->content().empty()) { + return -1; + } + + freeze (); clear (); - for (i = elist.begin(); i != elist.end(); ++i) { - - if ((prop = (*i)->property ("x")) == 0) { - error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg; - continue; + stringstream str (content_node->content()); + + double x; + double y; + bool ok = true; + + while (str) { + str >> x; + if (!str) { + break; } - x = atoi (prop->value().c_str()); - - if ((prop = (*i)->property ("y")) == 0) { - error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg; - continue; + str >> y; + if (!str) { + ok = false; + break; } - y = atof (prop->value().c_str()); - - add (x, y); + fast_simple_add (x, y); + } + + if (!ok) { + clear (); + error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg; + } else { + mark_dirty (); + reposition_for_rt_add (0); + maybe_signal_changed (); } -} -XMLNode &AutomationList::get_state () -{ - XMLNode *node = new XMLNode("AutomationList"); - store_state(*node); - return *node; + thaw (); + return 0; } -int AutomationList::set_state(const XMLNode &s) +int +AutomationList::set_state (const XMLNode& node) { - load_state(s); - return 0; + XMLNodeList nlist = node.children(); + XMLNode* nsos; + XMLNodeIterator niter; + const XMLProperty* prop; + + if (node.name() == X_("events")) { + /* partial state setting*/ + return deserialize_events (node); + } + + if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) { + + if ((nsos = node.child (X_("AutomationList")))) { + /* new school in old school clothing */ + return set_state (*nsos); + } + + /* old school */ + + const XMLNodeList& elist = node.children(); + XMLNodeConstIterator i; + XMLProperty* prop; + jack_nframes_t x; + double y; + + clear (); + + for (i = elist.begin(); i != elist.end(); ++i) { + + if ((prop = (*i)->property ("x")) == 0) { + error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg; + continue; + } + x = atoi (prop->value().c_str()); + + if ((prop = (*i)->property ("y")) == 0) { + error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg; + continue; + } + y = atof (prop->value().c_str()); + + add (x, y); + } + + return 0; + } + + if (node.name() != X_("AutomationList") ) { + error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg; + return -1; + } + + if ((prop = node.property ("id")) != 0) { + _id = prop->value (); + /* update session AL list */ + AutomationListCreated(this); + } + + if ((prop = node.property (X_("default"))) != 0){ + default_value = atof (prop->value()); + } else { + default_value = 0.0; + } + + if ((prop = node.property (X_("style"))) != 0) { + _style = string_to_auto_style (prop->value()); + } else { + _style = Absolute; + } + + if ((prop = node.property (X_("state"))) != 0) { + _state = string_to_auto_state (prop->value()); + } else { + _state = Off; + } + + if ((prop = node.property (X_("min_yval"))) != 0) { + min_yval = atof (prop->value ()); + } else { + min_yval = FLT_MIN; + } + + if ((prop = node.property (X_("max_yval"))) != 0) { + max_yval = atof (prop->value ()); + } else { + max_yval = FLT_MAX; + } + + if ((prop = node.property (X_("max_xval"))) != 0) { + max_xval = atof (prop->value ()); + } else { + max_xval = 0; // means "no limit ; + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == X_("events")) { + deserialize_events (*(*niter)); + } + } + + return 0; } diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc index 1ff6c28ef3..a715254747 100644 --- a/libs/ardour/control_protocol_manager.cc +++ b/libs/ardour/control_protocol_manager.cc @@ -36,6 +36,13 @@ ControlProtocolManager::~ControlProtocolManager() } control_protocols.clear (); + + + for (list<ControlProtocolInfo*>::iterator p = control_protocol_info.begin(); p != control_protocol_info.end(); ++p) { + delete (*p); + } + + control_protocol_info.clear(); } @@ -68,6 +75,12 @@ ControlProtocolManager::drop_session () delete *p; } control_protocols.clear (); + + for (list<ControlProtocolInfo*>::iterator p = control_protocol_info.begin(); p != control_protocol_info.end(); ++p) { + delete *p; + } + + control_protocol_info.clear(); } } @@ -122,6 +135,15 @@ ControlProtocolManager::teardown (ControlProtocolInfo& cpi) list<ControlProtocol*>::iterator p = find (control_protocols.begin(), control_protocols.end(), cpi.protocol); if (p != control_protocols.end()) { control_protocols.erase (p); + } else { + cerr << "Programming error: ControlProtocolManager::teardown() called for " << cpi.name << ", but it was not found in control_protocols" << endl; + } + + list<ControlProtocolInfo*>::iterator p2 = find (control_protocol_info.begin(), control_protocol_info.end(), &cpi); + if (p2 != control_protocol_info.end()) { + control_protocol_info.erase (p2); + } else { + cerr << "Programming error: ControlProtocolManager::teardown() called for " << cpi.name << ", but it was not found in control_protocol_info" << endl; } } diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index c8cbb7a40d..3c81b18fd4 100644 --- a/libs/ardour/coreaudiosource.cc +++ b/libs/ardour/coreaudiosource.cc @@ -43,7 +43,7 @@ CoreAudioSource::CoreAudioSource (Session& s, const string& idstr, Flag flags) } void -CoreAudioSource::init (const string& idstr) +CoreAudioSource::init (string idstr) { string::size_type pos; @@ -84,10 +84,6 @@ CoreAudioSource::init (const string& idstr) error << string_compose ("CoreAudioSource: %1 (%2)", cax.mOperation, name()) << endmsg; throw failed_constructor (); } - - if (_build_peakfiles) { - _need_peakfile = true; - } } CoreAudioSource::~CoreAudioSource () @@ -189,3 +185,53 @@ CoreAudioSource::update_header (nframes_t when, struct tm&, time_t) { return 0; } + +int +CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg) +{ + FSRef ref; + ExtAudioFileRef af = 0; + size_t size; + CFStringRef name; + int ret = -1; + + if (FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0) != noErr) { + goto out; + } + + if (ExtAudioFileOpen(&ref, &af) != noErr) { + goto out; + } + + AudioStreamBasicDescription absd; + memset(&absd, 0, sizeof(absd)); + size = sizeof(AudioStreamBasicDescription); + if (ExtAudioFileGetProperty (af, kExtAudioFileProperty_FileDataFormat, &size, &absd) != noErr) { + goto out; + } + + _info.samplerate = absd.mSampleRate; + _info.channels = absd.mChannelsPerFrame; + + size = sizeof(_info.length); + if (ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length) != noErr) { + goto out; + } + + size = sizeof(CFStringRef); + if (AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name) != noErr) { + goto out; + } + + _info.format_name = CFStringRefToStdString(name); + + // XXX it would be nice to find a way to get this information if it exists + + _info.timecode = 0; + ret = 0; + + out: + ExtAudioFileDispose (af); + return ret; + +} diff --git a/libs/ardour/curve.cc b/libs/ardour/curve.cc index 7d62c5bc94..8465094775 100644 --- a/libs/ardour/curve.cc +++ b/libs/ardour/curve.cc @@ -40,14 +40,11 @@ using namespace ARDOUR; using namespace sigc; using namespace PBD; -sigc::signal<void, Curve*> Curve::CurveCreated; - Curve::Curve (double minv, double maxv, double canv, bool nostate) - : AutomationList (canv, nostate) + : AutomationList (canv) { min_yval = minv; max_yval = maxv; - CurveCreated(this); } Curve::Curve (const Curve& other) @@ -55,7 +52,6 @@ Curve::Curve (const Curve& other) { min_yval = other.min_yval; max_yval = other.max_yval; - CurveCreated(this); } Curve::Curve (const Curve& other, double start, double end) @@ -63,7 +59,11 @@ Curve::Curve (const Curve& other, double start, double end) { min_yval = other.min_yval; max_yval = other.max_yval; - CurveCreated(this); +} + +Curve::Curve (const XMLNode& node) + : AutomationList (node) +{ } Curve::~Curve () @@ -73,7 +73,7 @@ Curve::~Curve () void Curve::solve () { - size_t npoints; + uint32_t npoints; if (!_dirty) { return; @@ -88,7 +88,7 @@ Curve::solve () double x[npoints]; double y[npoints]; - size_t i; + uint32_t i; AutomationEventList::iterator xx; for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) { @@ -207,7 +207,7 @@ Curve::solve () } bool -Curve::rt_safe_get_vector (double x0, double x1, float *vec, size_t veclen) +Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) { Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK); @@ -220,19 +220,19 @@ Curve::rt_safe_get_vector (double x0, double x1, float *vec, size_t veclen) } void -Curve::get_vector (double x0, double x1, float *vec, size_t veclen) +Curve::get_vector (double x0, double x1, float *vec, int32_t veclen) { Glib::Mutex::Lock lm (lock); _get_vector (x0, x1, vec, veclen); } void -Curve::_get_vector (double x0, double x1, float *vec, size_t veclen) +Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) { double rx, dx, lx, hx, max_x, min_x; - size_t i; - size_t original_veclen; - size_t npoints; + int32_t i; + int32_t original_veclen; + int32_t npoints; if ((npoints = events.size()) == 0) { for (i = 0; i < veclen; ++i) { @@ -263,7 +263,7 @@ Curve::_get_vector (double x0, double x1, float *vec, size_t veclen) */ double frac = (min_x - x0) / (x1 - x0); - size_t subveclen = (size_t) floor (veclen * frac); + int32_t subveclen = (int32_t) floor (veclen * frac); subveclen = min (subveclen, veclen); @@ -281,7 +281,7 @@ Curve::_get_vector (double x0, double x1, float *vec, size_t veclen) double frac = (x1 - max_x) / (x1 - x0); - size_t subveclen = lrintf (original_veclen * frac); + int32_t subveclen = (int32_t) floor (original_veclen * frac); float val; @@ -435,18 +435,10 @@ Curve::point_factory (const ControlEvent& other) const return new CurvePoint (other.when, other.value); } -Change -Curve::restore_state (StateManager::State& state) -{ - mark_dirty (); - return AutomationList::restore_state (state); -} - - extern "C" { void -curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, size_t vecsize) +curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, int32_t vecsize) { static_cast<Curve*>(arg)->get_vector (x0, x1, vec, vecsize); } diff --git a/libs/ardour/destructive_filesource.cc b/libs/ardour/destructive_filesource.cc index e160ffd608..fcd85bfe8f 100644 --- a/libs/ardour/destructive_filesource.cc +++ b/libs/ardour/destructive_filesource.cc @@ -55,8 +55,10 @@ typedef off_t off64_t; #include <fcntl.h> #include <pbd/error.h> +#include <pbd/stacktrace.h> #include <ardour/destructive_filesource.h> #include <ardour/utils.h> +#include <ardour/session.h> #include "i18n.h" @@ -289,7 +291,7 @@ DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt) _capture_end = false; /* move to the correct location place */ - file_pos = capture_start_frame; + file_pos = capture_start_frame - timeline_position; // split cnt in half nframes_t subcnt = cnt / 2; @@ -343,17 +345,16 @@ DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt) } else { /* in the middle of recording */ - if (write_float (data, file_pos, cnt) != cnt) { return 0; } } - + old_file_pos = file_pos; update_length (file_pos, cnt); file_pos += cnt; - + if (_build_peakfiles) { PeakBuildRecord *pbr = 0; @@ -409,7 +410,15 @@ DestructiveFileSource::handle_header_position_change () } void -DestructiveFileSource::set_timeline_position (nframes_t pos) +DestructiveFileSource::set_timeline_position (int64_t) { //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes } + +int +DestructiveFileSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const +{ + // cerr << _name << " read peaks at " << start << " for " << cnt << " tpos = " << timeline_position << endl; + return AudioFileSource::read_peaks (peaks, npeaks, start, cnt, samples_per_unit); +} + diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index e9f8499981..5f6f3956cf 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -390,14 +390,14 @@ Diskstream::set_name (string str) } void -Diskstream::set_destructive (bool yn) +Diskstream::remove_region_from_last_capture (boost::weak_ptr<Region> wregion) { - if (yn != destructive()) { - reset_write_sources (true, true); - if (yn) { - _flags |= Destructive; - } else { - _flags &= ~Destructive; - } + boost::shared_ptr<Region> region (wregion.lock()); + + if (!region) { + return; } + + _last_capture_regions.remove (region); } + diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 9b5bea9d3a..f92660470c 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -128,6 +128,8 @@ setup_midi (AudioEngine& engine ) } MIDI::Manager::instance()->add_port (request); + + nports++; } if (nports > 1) { diff --git a/libs/ardour/i18n.h b/libs/ardour/i18n.h index 71a3dccab8..5d68c79edd 100644 --- a/libs/ardour/i18n.h +++ b/libs/ardour/i18n.h @@ -2,6 +2,7 @@ #define __i18n_h__ #include <pbd/compose.h> +#include <pbd/convert.h> #include "gettext.h" #include <vector> @@ -10,5 +11,6 @@ #define _(Text) dgettext (PACKAGE,Text) #define N_(Text) gettext_noop (Text) #define X_(Text) Text +#define I18N(Array) PBD::internationalize (PACKAGE, Array) #endif // __i18n_h__ diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index b557017ec7..034b043763 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -85,8 +85,6 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug, Placemen init (); - save_state (_("initial state")); - { Glib::Mutex::Lock em (_session.engine().process_lock()); IO::MoreChannels (max(input_streams(), output_streams())); @@ -104,8 +102,6 @@ PluginInsert::PluginInsert (Session& s, const XMLNode& node) set_automatable (); - save_state (_("initial state")); - _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); { @@ -129,8 +125,6 @@ PluginInsert::PluginInsert (const PluginInsert& other) init (); - save_state (_("initial state")); - RedirectCreated (this); /* EMIT SIGNAL */ } @@ -325,6 +319,23 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off } void +PluginInsert::automation_snapshot (nframes_t now) +{ + map<uint32_t,AutomationList*>::iterator li; + + for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { + + AutomationList *alist = ((*li).second); + if (alist != 0 && alist->automation_write ()) { + + float val = _plugins[0]->get_parameter ((*li).first); + alist->rt_add (now, val); + last_automation_snapshot = now; + } + } +} + +void PluginInsert::transport_stopped (nframes_t now) { map<uint32_t,AutomationList*>::iterator li; @@ -785,35 +796,6 @@ PluginInsert::latency() return _plugins[0]->latency (); } -void -PluginInsert::store_state (PluginInsertState& state) const -{ - Redirect::store_state (state); - _plugins[0]->store_state (state.plugin_state); -} - -Change -PluginInsert::restore_state (StateManager::State& state) -{ - PluginInsertState* pistate = dynamic_cast<PluginInsertState*> (&state); - - Redirect::restore_state (state); - - _plugins[0]->restore_state (pistate->plugin_state); - - return Change (0); -} - -StateManager::State* -PluginInsert::state_factory (std::string why) const -{ - PluginInsertState* state = new PluginInsertState (why); - - store_state (*state); - - return state; -} - ARDOUR::PluginType PluginInsert::type () { @@ -851,7 +833,6 @@ PortInsert::PortInsert (Session& s, Placement p) : Insert (s, p, 1, -1, 1, -1) { init (); - save_state (_("initial state")); RedirectCreated (this); /* EMIT SIGNAL */ } @@ -859,7 +840,6 @@ PortInsert::PortInsert (const PortInsert& other) : Insert (other._session, other.placement(), 1, -1, 1, -1) { init (); - save_state (_("initial state")); RedirectCreated (this); /* EMIT SIGNAL */ } @@ -1013,10 +993,10 @@ PortInsert::configure_io (int32_t ignored_magic, int32_t in, int32_t out) to the number of input ports we need. */ - set_output_maximum (in); - set_output_minimum (in); - set_input_maximum (out); - set_input_minimum (out); + set_output_maximum (ChanCount(_default_type, in)); + set_output_minimum (ChanCount(_default_type, in)); + set_input_maximum (ChanCount(_default_type, out)); + set_input_minimum (ChanCount(_default_type, out)); if (in < 0) { in = n_outputs ().get(_default_type); @@ -1026,8 +1006,7 @@ PortInsert::configure_io (int32_t ignored_magic, int32_t in, int32_t out) out = n_inputs ().get(_default_type); } - // FIXME - return ensure_io (ChanCount(_default_type, in), ChanCount(_default_type, out), false, this); + return ensure_io (ChanCount(_default_type, out), ChanCount(_default_type, in), false, this); } int32_t diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index af5473368b..60e7ec3f42 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -60,7 +60,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -static float current_automation_version_number = 1.0; +nframes_t IO::_automation_interval = 0; const string IO::state_node_name = "IO"; bool IO::connecting_legal = false; @@ -137,6 +137,8 @@ IO::IO (Session& s, string name, apply_gain_automation = false; + last_automation_snapshot = 0; + _gain_automation_state = Off; _gain_automation_style = Absolute; @@ -149,6 +151,43 @@ IO::IO (Session& s, string name, // Connect to our own MoreChannels signal to connect output buffers IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); + + _session.add_controllable (&_gain_control); +} + +IO::IO (Session& s, const XMLNode& node, DataType dt) + : _session (s), + _output_buffers(new BufferSet()), + _default_type (dt), + _gain_control (X_("gaincontrol"), *this), + _gain_automation_curve (0, 0, 0) // all reset in set_state() +{ + // FIXME: hack + _meter = new PeakMeter (_session); + + _panner = 0; + deferred_state = 0; + no_panner_reset = false; + _desired_gain = 1.0; + _gain = 1.0; + _input_connection = 0; + _output_connection = 0; + + apply_gain_automation = false; + + set_state (node); + + { + // IO::Meter is emitted from another thread so the + // Meter signal must be protected. + Glib::Mutex::Lock guard (m_meter_signal_lock); + m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter)); + } + + // Connect to our own MoreChannels signal to connect output buffers + IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); + + _session.add_controllable (&_gain_control); } IO::~IO () @@ -1234,67 +1273,21 @@ IO::state (bool full_state) /* automation */ if (full_state) { + + XMLNode* autonode = new XMLNode (X_("Automation")); + autonode->add_child_nocopy (get_automation_state()); + node->add_child_nocopy (*autonode); + snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state()); } else { /* never store anything except Off for automation state in a template */ snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); } - node->add_property ("automation-state", buf); - snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style()); - node->add_property ("automation-style", buf); - - /* XXX same for pan etc. */ return *node; } int -IO::connecting_became_legal () -{ - int ret; - - if (pending_state_node == 0) { - fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg; - /*NOTREACHED*/ - return -1; - } - - connection_legal_c.disconnect (); - - ret = make_connections (*pending_state_node); - - if (ports_legal) { - delete pending_state_node; - pending_state_node = 0; - } - - return ret; -} - -int -IO::ports_became_legal () -{ - int ret; - - if (pending_state_node == 0) { - fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg; - /*NOTREACHED*/ - return -1; - } - - port_legal_c.disconnect (); - - ret = create_ports (*pending_state_node); - - if (connecting_legal) { - delete pending_state_node; - pending_state_node = 0; - } - - return ret; -} - -int IO::set_state (const XMLNode& node) { const XMLProperty* prop; @@ -1312,7 +1305,7 @@ IO::set_state (const XMLNode& node) if ((prop = node.property ("name")) != 0) { _name = prop->value(); - _panner->set_name (_name); + /* used to set panner name with this, but no more */ } if ((prop = node.property ("id")) != 0) { @@ -1338,32 +1331,29 @@ IO::set_state (const XMLNode& node) _gain = _desired_gain; } + if ((prop = node.property ("automation-state")) != 0 || (prop = node.property ("automation-style")) != 0) { + /* old school automation handling */ + } + for (iter = node.children().begin(); iter != node.children().end(); ++iter) { if ((*iter)->name() == "Panner") { + if (_panner == 0) { + _panner = new Panner (_name, _session); + } _panner->set_state (**iter); } + if ((*iter)->name() == X_("Automation")) { + + set_automation_state (*(*iter)->children().front()); + } + if ((*iter)->name() == X_("gaincontrol")) { _gain_control.set_state (**iter); - _session.add_controllable (&_gain_control); } } - if ((prop = node.property ("automation-state")) != 0) { - - long int x; - x = strtol (prop->value().c_str(), 0, 16); - set_gain_automation_state (AutoState (x)); - } - - if ((prop = node.property ("automation-style")) != 0) { - - long int x; - x = strtol (prop->value().c_str(), 0, 16); - set_gain_automation_style (AutoStyle (x)); - } - if (ports_legal) { if (create_ports (node)) { @@ -1396,10 +1386,147 @@ IO::set_state (const XMLNode& node) pending_state_node = new XMLNode (node); } + last_automation_snapshot = 0; + return 0; } int +IO::set_automation_state (const XMLNode& node) +{ + return _gain_automation_curve.set_state (node); +} + +XMLNode& +IO::get_automation_state () +{ + return (_gain_automation_curve.get_state ()); +} + +int +IO::load_automation (string path) +{ + string fullpath; + ifstream in; + char line[128]; + uint32_t linecnt = 0; + float version; + LocaleGuard lg (X_("POSIX")); + + fullpath = _session.automation_dir(); + fullpath += path; + + in.open (fullpath.c_str()); + + if (!in) { + fullpath = _session.automation_dir(); + fullpath += _session.snap_name(); + fullpath += '-'; + fullpath += path; + + in.open (fullpath.c_str()); + + if (!in) { + error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg; + return -1; + } + } + + clear_automation (); + + while (in.getline (line, sizeof(line), '\n')) { + char type; + jack_nframes_t when; + double value; + + if (++linecnt == 1) { + if (memcmp (line, "version", 7) == 0) { + if (sscanf (line, "version %f", &version) != 1) { + error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg; + return -1; + } + } else { + error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg; + return -1; + } + + continue; + } + + if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) { + warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg; + continue; + } + + switch (type) { + case 'g': + _gain_automation_curve.fast_simple_add (when, value); + break; + + case 's': + break; + + case 'm': + break; + + case 'p': + /* older (pre-1.0) versions of ardour used this */ + break; + + default: + warning << _("dubious automation event found (and ignored)") << endmsg; + } + } + + return 0; +} + +int +IO::connecting_became_legal () +{ + int ret; + + if (pending_state_node == 0) { + fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg; + /*NOTREACHED*/ + return -1; + } + + connection_legal_c.disconnect (); + + ret = make_connections (*pending_state_node); + + if (ports_legal) { + delete pending_state_node; + pending_state_node = 0; + } + + return ret; +} +int +IO::ports_became_legal () +{ + int ret; + + if (pending_state_node == 0) { + fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg; + /*NOTREACHED*/ + return -1; + } + + port_legal_c.disconnect (); + + ret = create_ports (*pending_state_node); + + if (connecting_legal) { + delete pending_state_node; + pending_state_node = 0; + } + + return ret; +} + +int IO::create_ports (const XMLNode& node) { const XMLProperty* prop; @@ -1702,42 +1829,6 @@ IO::set_name (string name, void* src) } void -IO::set_input_minimum (int n) -{ - if (n < 0) - _input_minimum = ChanCount::ZERO; - else - _input_minimum = ChanCount(_default_type, n); -} - -void -IO::set_input_maximum (int n) -{ - if (n < 0) - _input_maximum = ChanCount::INFINITE; - else - _input_maximum = ChanCount(_default_type, n); -} - -void -IO::set_output_minimum (int n) -{ - if (n < 0) - _output_minimum = ChanCount::ZERO; - else - _output_minimum = ChanCount(_default_type, n); -} - -void -IO::set_output_maximum (int n) -{ - if (n < 0) - _output_maximum = ChanCount::INFINITE; - else - _output_maximum = ChanCount(_default_type, n); -} - -void IO::set_input_minimum (ChanCount n) { _input_minimum = n; @@ -2046,25 +2137,6 @@ IO::GainControllable::get_value (void) const return direct_gain_to_control (io.effective_gain()); } -UndoAction -IO::get_memento() const -{ - return sigc::bind (mem_fun (*(const_cast<IO *>(this)), &StateManager::use_state), _current_state_id); -} - -Change -IO::restore_state (StateManager::State& state) -{ - return Change (0); -} - -StateManager::State* -IO::state_factory (std::string why) const -{ - StateManager::State* state = new StateManager::State (why); - return state; -} - void IO::setup_peak_meters() { @@ -2096,118 +2168,6 @@ IO::meter () _meter->meter(); } -int -IO::save_automation (const string& path) -{ - string fullpath; - ofstream out; - - fullpath = _session.automation_dir(); - fullpath += path; - - out.open (fullpath.c_str()); - - if (!out) { - error << string_compose(_("%1: could not open automation event file \"%2\""), _name, fullpath) << endmsg; - return -1; - } - - out << X_("version ") << current_automation_version_number << endl; - - /* XXX use apply_to_points to get thread safety */ - - for (AutomationList::iterator i = _gain_automation_curve.begin(); i != _gain_automation_curve.end(); ++i) { - out << "g " << (nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl; - } - - _panner->save (); - - return 0; -} - -int -IO::load_automation (const string& path) -{ - string fullpath; - ifstream in; - char line[128]; - uint32_t linecnt = 0; - float version; - LocaleGuard lg (X_("POSIX")); - - fullpath = _session.automation_dir(); - fullpath += path; - - in.open (fullpath.c_str()); - - if (!in) { - fullpath = _session.automation_dir(); - fullpath += _session.snap_name(); - fullpath += '-'; - fullpath += path; - in.open (fullpath.c_str()); - if (!in) { - error << string_compose(_("%1: cannot open automation event file \"%2\" (%2)"), _name, fullpath, strerror (errno)) << endmsg; - return -1; - } - } - - clear_automation (); - - while (in.getline (line, sizeof(line), '\n')) { - char type; - nframes_t when; - double value; - - if (++linecnt == 1) { - if (memcmp (line, "version", 7) == 0) { - if (sscanf (line, "version %f", &version) != 1) { - error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg; - return -1; - } - } else { - error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg; - return -1; - } - - if (version != current_automation_version_number) { - error << string_compose(_("mismatched automation event file version (%1)"), version) << endmsg; - return -1; - } - - continue; - } - - if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) { - warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg; - continue; - } - - switch (type) { - case 'g': - _gain_automation_curve.add (when, value, true); - break; - - case 's': - break; - - case 'm': - break; - - case 'p': - /* older (pre-1.0) versions of ardour used this */ - break; - - default: - warning << _("dubious automation event found (and ignored)") << endmsg; - } - } - - _gain_automation_curve.save_state (_("loaded from disk")); - - return 0; -} - void IO::clear_automation () { @@ -2226,6 +2186,7 @@ IO::set_gain_automation_state (AutoState state) if (state != _gain_automation_curve.automation_state()) { changed = true; + last_automation_snapshot = 0; _gain_automation_curve.set_automation_state (state); if (state != Off) { @@ -2324,16 +2285,27 @@ IO::end_pan_touch (uint32_t which) } void +IO::automation_snapshot (nframes_t now) +{ + if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) { + + if (gain_automation_recording()) { + _gain_automation_curve.rt_add (now, gain()); + } + + _panner->snapshot (now); + + last_automation_snapshot = now; + } +} + +void IO::transport_stopped (nframes_t frame) { _gain_automation_curve.reposition_for_rt_add (frame); if (_gain_automation_curve.automation_state() != Off) { - if (gain_automation_recording()) { - _gain_automation_curve.save_state (_("automation write/touch")); - } - /* the src=0 condition is a special signal to not propagate automation gain changes into the mix group when locating. */ diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index 39331cfda6..e09a59d42f 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -372,27 +372,16 @@ Locations::Locations () { current_location = 0; - save_state (_("initial")); } Locations::~Locations () { - std::set<Location*> all_locations; - - for (StateMap::iterator siter = states.begin(); siter != states.end(); ++siter) { - - State* lstate = dynamic_cast<State*> (*siter); - - for (LocationList::iterator liter = lstate->locations.begin(); liter != lstate->locations.end(); ++liter) { - all_locations.insert (*liter); - } - - for (LocationList::iterator siter = lstate->states.begin(); siter != lstate->states.end(); ++siter) { - all_locations.insert (*siter); - } + for (LocationList::iterator i = locations.begin(); i != locations.end(); ) { + LocationList::iterator tmp = i; + ++tmp; + delete *i; + i = tmp; } - - set_delete (&all_locations); } int @@ -431,22 +420,22 @@ Locations::clear () { { Glib::Mutex::Lock lm (lock); - LocationList::iterator tmp; + for (LocationList::iterator i = locations.begin(); i != locations.end(); ) { - tmp = i; + + LocationList::iterator tmp = i; ++tmp; + if (!(*i)->is_end() && !(*i)->is_start()) { locations.erase (i); } + i = tmp; } - locations.clear (); current_location = 0; } - save_state (_("clear")); - changed (); /* EMIT SIGNAL */ current_changed (0); /* EMIT SIGNAL */ } @@ -470,8 +459,6 @@ Locations::clear_markers () } } - save_state (_("clear markers")); - changed (); /* EMIT SIGNAL */ } @@ -498,8 +485,6 @@ Locations::clear_ranges () current_location = 0; } - save_state (_("clear ranges")); - changed (); /* EMIT SIGNAL */ current_changed (0); /* EMIT SIGNAL */ } @@ -516,8 +501,6 @@ Locations::add (Location *loc, bool make_current) } } - save_state (_("add")); - added (loc); /* EMIT SIGNAL */ if (make_current) { @@ -554,9 +537,8 @@ Locations::remove (Location *loc) } if (was_removed) { - save_state (_("remove")); - - removed (loc); /* EMIT SIGNAL */ + + removed (loc); /* EMIT SIGNAL */ if (was_current) { current_changed (0); /* EMIT SIGNAL */ @@ -569,7 +551,6 @@ Locations::remove (Location *loc) void Locations::location_changed (Location* loc) { - save_state (X_("location changed")); changed (); /* EMIT SIGNAL */ } @@ -599,7 +580,10 @@ Locations::set_state (const XMLNode& node) } nlist = node.children(); - + + locations.clear (); + current_location = 0; + { Glib::Mutex::Lock lm (lock); @@ -809,45 +793,6 @@ Locations::auto_punch_location () const return 0; } -StateManager::State* -Locations::state_factory (std::string why) const -{ - State* state = new State (why); - - state->locations = locations; - - for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { - state->states.push_back (new Location (**i)); - } - - return state; -} - -Change -Locations::restore_state (StateManager::State& state) -{ - { - Glib::Mutex::Lock lm (lock); - State* lstate = dynamic_cast<State*> (&state); - - locations = lstate->locations; - LocationList& states = lstate->states; - LocationList::iterator l, s; - - for (l = locations.begin(), s = states.begin(); s != states.end(); ++s, ++l) { - (*l) = (*s); - } - } - - return Change (0); -} - -UndoAction -Locations::get_memento () const -{ - return sigc::bind (mem_fun (*(const_cast<Locations*> (this)), &StateManager::use_state), _current_state_id); -} - uint32_t Locations::num_range_markers () const { diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 8247aac217..b1ec7da965 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -295,11 +295,12 @@ MidiDiskstream::use_copy_playlist () /** Overloaded from parent to die horribly */ -void +int MidiDiskstream::set_destructive (bool yn) { assert( ! destructive()); assert( ! yn); + return -1; } void diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index 36e5e05116..d33d19ce67 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -106,7 +106,6 @@ MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node) MidiRegion::~MidiRegion () { - GoingAway (); /* EMIT SIGNAL */ } jack_nframes_t diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index f6d0a22019..a18d0c20ce 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -619,14 +619,21 @@ MidiTrack::unfreeze () FreezeChange (); /* EMIT SIGNAL */ } -void +int MidiTrack::set_mode (TrackMode m) { - if (_diskstream) { - if (_mode != m) { - _mode = m; - _diskstream->set_destructive (m == Destructive); - ModeChanged(); + assert(_diskstream); + + if (m != _mode) { + + if (_diskstream->set_destructive (m == Destructive)) { + return -1; } + + _mode = m; + + TrackModeChanged (); /* EMIT SIGNAL */ } + + return 0; } diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 0f6e78f84b..ee8e100e4a 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -75,6 +75,8 @@ StreamPanner::StreamPanner (Panner& p) { _muted = false; + parent.session().add_controllable (&_control); + x = 0.5; y = 0.5; z = 0.5; @@ -210,11 +212,6 @@ BaseStereoPanner::transport_stopped (nframes_t frame) _automation.reposition_for_rt_add (frame); if (_automation.automation_state() != Off) { - - if (_automation.automation_write()) { - _automation.save_state (_("automation write pass")); - } - set_position (_automation.eval (frame)); } } @@ -239,29 +236,6 @@ BaseStereoPanner::set_automation_state (AutoState state) } int -BaseStereoPanner::save (ostream& out) const -{ - LocaleGuard lg (X_("POSIX")); - - /* force a single format for numeric data to ease session interchange - across national boundaries. - */ - - out << "begin" << endl; - - for (AutomationList::const_iterator i = _automation.const_begin(); i != _automation.const_end(); ++i) { - out << '\t' << (nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl; - if (!out) { - error << string_compose (_("error writing pan automation file (%s)"), strerror (errno)) << endmsg; - return -1; - } - } - out << "end" << endl; - - return 0; -} - -int BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) { char line[128]; @@ -270,7 +244,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) _automation.clear (); while (in.getline (line, sizeof (line), '\n')) { - nframes_t when; + jack_nframes_t when; double value; ++linecnt; @@ -284,13 +258,12 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt) continue; } - _automation.add (when, value, true); + _automation.fast_simple_add (when, value); } /* now that we are done loading */ - _automation.save_state (_("loaded from disk")); - _automation.StateChanged (Change (0)); + _automation.StateChanged (); return 0; } @@ -543,17 +516,13 @@ EqualPowerStereoPanner::state (bool full_state) snprintf (buf, sizeof (buf), "%.12g", x); root->add_property (X_("x"), buf); root->add_property (X_("type"), EqualPowerStereoPanner::name); - if (full_state) { - snprintf (buf, sizeof (buf), "0x%x", _automation.automation_state()); - } else { - /* never store automation states other than off in a template */ - snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); - } - root->add_property (X_("automation-state"), buf); - snprintf (buf, sizeof (buf), "0x%x", _automation.automation_style()); - root->add_property (X_("automation-style"), buf); + + XMLNode* autonode = new XMLNode (X_("Automation")); + autonode->add_child_nocopy (_automation.state (full_state)); + root->add_child_nocopy (*autonode); StreamPanner::add_state (*root); + root->add_child_nocopy (_control.get_state ()); return *root; @@ -563,7 +532,6 @@ int EqualPowerStereoPanner::set_state (const XMLNode& node) { const XMLProperty* prop; - int x; float pos; LocaleGuard lg (X_("POSIX")); @@ -572,29 +540,24 @@ EqualPowerStereoPanner::set_state (const XMLNode& node) set_position (pos, true); } - if ((prop = node.property (X_("automation-state")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_state ((AutoState) x); - - if (x != Off) { - set_position (_automation.eval (parent.session().transport_frame())); - } - } - - if ((prop = node.property (X_("automation-style")))) { - sscanf (prop->value().c_str(), "0x%x", &x); - _automation.set_automation_style ((AutoStyle) x); - } - StreamPanner::set_state (node); for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) { + if ((*iter)->name() == X_("panner")) { + _control.set_state (**iter); - parent.session().add_controllable (&_control); + + } else if ((*iter)->name() == X_("Automation")) { + + _automation.set_state (*((*iter)->children().front())); + + if (_automation.automation_state() != Off) { + set_position (_automation.eval (parent.session().transport_frame())); + } } } - + return 0; } @@ -765,12 +728,6 @@ Multi2dPanner::load (istream& in, string path, uint32_t& linecnt) return 0; } -int -Multi2dPanner::save (ostream& out) const -{ - return 0; -} - XMLNode& Multi2dPanner::get_state (void) { @@ -790,6 +747,8 @@ Multi2dPanner::state (bool full_state) root->add_property (X_("y"), buf); root->add_property (X_("type"), Multi2dPanner::name); + /* XXX no meaningful automation yet */ + return *root; } @@ -827,6 +786,7 @@ Panner::Panner (string name, Session& s) : _session (s) { set_name (name); + _linked = false; _link_direction = SameDirection; _bypassed = false; @@ -857,17 +817,6 @@ Panner::set_link_direction (LinkDirection ld) } void -Panner::set_name (string str) -{ - automation_path = _session.automation_dir(); - automation_path += _session.snap_name(); - automation_path += "-pan-"; - automation_path += legalize_for_path (str); - automation_path += ".automation"; -} - - -void Panner::set_bypassed (bool yn) { if (yn != _bypassed) { @@ -883,7 +832,6 @@ Panner::reset (uint32_t nouts, uint32_t npans) uint32_t n; bool changed = false; - if (nouts < 2 || (nouts == outputs.size() && npans == size())) { return; } @@ -1095,102 +1043,6 @@ Panner::clear_automation () _session.set_dirty (); } -int -Panner::save () const -{ - ofstream out (automation_path.c_str()); - - if (!out) { - error << string_compose (_("cannot open pan automation file \"%1\" for saving (%2)"), automation_path, strerror (errno)) - << endmsg; - return -1; - } - - out << X_("version ") << current_automation_version_number << endl; - - for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) { - if ((*i)->save (out)) { - return -1; - } - } - - return 0; -} - -int -Panner::load () -{ - char line[128]; - uint32_t linecnt = 0; - float version; - iterator sp; - LocaleGuard lg (X_("POSIX")); - - if (automation_path.length() == 0) { - return 0; - } - - if (access (automation_path.c_str(), F_OK)) { - return 0; - } - - ifstream in (automation_path.c_str()); - - if (!in) { - error << string_compose (_("cannot open pan automation file %1 (%2)"), - automation_path, strerror (errno)) - << endmsg; - return -1; - } - - sp = begin(); - - while (in.getline (line, sizeof(line), '\n')) { - - if (++linecnt == 1) { - if (memcmp (line, X_("version"), 7) == 0) { - if (sscanf (line, "version %f", &version) != 1) { - error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg; - return -1; - } - } else { - error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"), - automation_path, line) << endmsg; - return -1; - } - - if (version != current_automation_version_number) { - error << string_compose(_("mismatched pan automation event file version (%1)"), version) << endmsg; - return -1; - } - - continue; - } - - if (strlen (line) == 0 || line[0] == '#') { - continue; - } - - if (strcmp (line, "begin") == 0) { - - if (sp == end()) { - error << string_compose (_("too many panner states found in pan automation file %1"), - automation_path) - << endmsg; - return -1; - } - - if ((*sp)->load (in, automation_path, linecnt)) { - return -1; - } - - ++sp; - } - } - - return 0; -} - struct PanPlugins { string name; uint32_t nouts; @@ -1215,10 +1067,6 @@ Panner::state (bool full) XMLNode* root = new XMLNode (X_("Panner")); char buf[32]; - for (iterator p = begin(); p != end(); ++p) { - root->add_child_nocopy ((*p)->state (full)); - } - root->add_property (X_("linked"), (_linked ? "yes" : "no")); snprintf (buf, sizeof (buf), "%d", _link_direction); root->add_property (X_("link_direction"), buf); @@ -1235,10 +1083,8 @@ Panner::state (bool full) root->add_child_nocopy (*onode); } - if (full) { - if (save () == 0) { - root->add_property (X_("automation"), Glib::path_get_basename (automation_path)); - } + for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) { + root->add_child_nocopy ((*i)->state (full)); } return *root; @@ -1329,7 +1175,7 @@ Panner::set_state (const XMLNode& node) } } - /* don't try to load automation if it wasn't marked as existing */ + /* don't try to do old-school automation loading if it wasn't marked as existing */ if ((prop = node.property (X_("automation")))) { @@ -1629,3 +1475,83 @@ Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame } } +/* old school automation handling */ + +void +Panner::set_name (string str) +{ + automation_path = _session.automation_dir(); + automation_path += _session.snap_name(); + automation_path += "-pan-"; + automation_path += legalize_for_path (str); + automation_path += ".automation"; +} + +int +Panner::load () +{ + char line[128]; + uint32_t linecnt = 0; + float version; + iterator sp; + LocaleGuard lg (X_("POSIX")); + + if (automation_path.length() == 0) { + return 0; + } + + if (access (automation_path.c_str(), F_OK)) { + return 0; + } + + ifstream in (automation_path.c_str()); + + if (!in) { + error << string_compose (_("cannot open pan automation file %1 (%2)"), + automation_path, strerror (errno)) + << endmsg; + return -1; + } + + sp = begin(); + + while (in.getline (line, sizeof(line), '\n')) { + + if (++linecnt == 1) { + if (memcmp (line, X_("version"), 7) == 0) { + if (sscanf (line, "version %f", &version) != 1) { + error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg; + return -1; + } + } else { + error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"), + automation_path, line) << endmsg; + return -1; + } + + continue; + } + + if (strlen (line) == 0 || line[0] == '#') { + continue; + } + + if (strcmp (line, "begin") == 0) { + + if (sp == end()) { + error << string_compose (_("too many panner states found in pan automation file %1"), + automation_path) + << endmsg; + return -1; + } + + if ((*sp)->load (in, automation_path, linecnt)) { + return -1; + } + + ++sp; + } + } + + return 0; +} diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index ee60a53d66..d439cf1265 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -90,10 +90,8 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide init (hide); _name = "unnamed"; /* reset by set_state */ - - if (set_state (node)) { - throw failed_constructor(); - } + + /* derived class calls set_state() */ } Playlist::Playlist (const Playlist& other, string namestr, bool hide) @@ -264,11 +262,19 @@ Playlist::Playlist (Playlist& pl) Playlist::~Playlist () { + { + RegionLock rl (this); + + for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) { + (*i)->set_playlist (0); + } + } + /* GoingAway must be emitted by derived classes */ } void -Playlist::set_name (const string& str) +Playlist::set_name (string str) { /* in a typical situation, a playlist is being used by one diskstream and also is referenced by the @@ -563,8 +569,6 @@ Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_so RegionList::iterator i; nframes_t old_length = 0; - cerr << "removing region " << region->name() << " holding = " << holding_state() << endl; - if (!holding_state()) { old_length = _get_maximum_extent(); } @@ -1140,6 +1144,7 @@ Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> wea return; } + /* this makes a virtual call to the right kind of playlist ... */ region_changed (what_changed, region); @@ -1259,6 +1264,7 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir) boost::shared_ptr<Region> ret; nframes_t closest = max_frames; + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { nframes_t distance; @@ -1280,7 +1286,7 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir) switch (dir) { case 1: /* forwards */ - if (pos > frame) { + if (pos >= frame) { if ((distance = pos - frame) < closest) { closest = distance; ret = r; @@ -1291,7 +1297,7 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir) default: /* backwards */ - if (pos < frame) { + if (pos <= frame) { if ((distance = frame - pos) < closest) { closest = distance; ret = r; @@ -1455,13 +1461,21 @@ Playlist::state (bool full_state) bool Playlist::empty() const { + RegionLock rlock (const_cast<Playlist *>(this), false); return regions.empty(); } -ARDOUR::nframes_t +uint32_t +Playlist::n_regions() const +{ + RegionLock rlock (const_cast<Playlist *>(this), false); + return regions.size(); +} + +nframes_t Playlist::get_maximum_extent () const { - RegionLock rlock (const_cast<Playlist *>(this)); + RegionLock rlock (const_cast<Playlist *>(this), false); return _get_maximum_extent (); } @@ -1488,7 +1502,7 @@ Playlist::bump_name (string name, Session &session) do { newname = Playlist::bump_name_once (newname); - } while (session.playlist_by_name(newname)!=NULL); + } while (session.playlist_by_name (newname)!=NULL); return newname; } diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index bdd4d0ada6..b24b2619d3 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -86,7 +86,6 @@ PluginManager::PluginManager () } refresh (); - if (_manager == 0) { _manager = this; } @@ -109,7 +108,7 @@ PluginManager::ladspa_refresh () _ladspa_plugin_info.clear (); if (ladspa_path.length() == 0) { - ladspa_path = "/usr/local/lib/ladspa:/usr/lib/ladspa"; + ladspa_path = "/usr/local/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib64/ladspa:/usr/lib/ladspa:/Library/Audio/Plug-Ins/LADSPA"; } ladspa_discover_from_path (ladspa_path); diff --git a/libs/ardour/po/sv_SE.po b/libs/ardour/po/sv_SE.po new file mode 100644 index 0000000000..ddc7f108bb --- /dev/null +++ b/libs/ardour/po/sv_SE.po @@ -0,0 +1,2025 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR "Paul Davis" +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: ardour\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-10-03 00:39+0200\n" +"PO-Revision-Date: 2006-10-03 01:09+GMT+1\n" +"Last-Translator: Petter Sundlöf <petter.sundlof@findus.dhs.org>\n" +"Language-Team: Swedish <sv@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: libs/ardour/diskstream.cc:258 +msgid "Location \"%1\" not valid for track loop (start >= end)" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:298 +msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:349 +msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:924 libs/ardour/audio_diskstream.cc:935 +msgid "" +"AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1069 +msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1412 libs/ardour/audio_diskstream.cc:1429 +msgid "AudioDiskstream %1: cannot write to disk" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1473 +msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1563 +msgid "%1: could not create region for complete audio file" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1587 +msgid "AudioDiskstream: could not create region for captured audio!" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1643 +msgid "programmer error: %1" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1929 +msgid "AudioDiskstream: channel %1 out of range" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:1952 +msgid "%1:%2 new capture file not initialized correctly" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:2178 +msgid "%1: cannot restore pending capture source file %2" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:2200 +msgid "%1: incorrect number of pending sources listed - ignoring them all" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:2215 +msgid "%1: cannot create whole-file region from pending capture sources" +msgstr "" + +#: libs/ardour/audio_diskstream.cc:2227 +msgid "%1: cannot create region from pending capture sources" +msgstr "" + +#: libs/ardour/audio_library.cc:92 +msgid "channels" +msgstr "" + +#: libs/ardour/audio_library.cc:93 +msgid "samplerate" +msgstr "" + +#: libs/ardour/audio_library.cc:94 +msgid "resolution" +msgstr "" + +#: libs/ardour/audio_library.cc:95 +msgid "format" +msgstr "" + +#: libs/ardour/audio_library.cc:102 +msgid "Could not open %1. Audio Library not saved" +msgstr "" + +#: libs/ardour/audio_playlist.cc:53 libs/ardour/audio_playlist.cc:63 +#: libs/ardour/audio_playlist.cc:74 libs/ardour/audio_playlist.cc:121 +#: libs/ardour/insert.cc:84 libs/ardour/insert.cc:103 +#: libs/ardour/insert.cc:128 libs/ardour/insert.cc:862 +#: libs/ardour/insert.cc:870 libs/ardour/send.cc:39 libs/ardour/send.cc:53 +#: libs/ardour/send.cc:62 libs/ardour/session_state.cc:1128 +#: libs/ardour/session_state.cc:1170 +msgid "initial state" +msgstr "" + +#: libs/ardour/audio_playlist.cc:261 libs/ardour/audio_playlist.cc:743 +msgid "" +"programming error: non-audio Region passed to remove_overlap in audio " +"playlist" +msgstr "" + +#: libs/ardour/audio_playlist.cc:388 +msgid "" +"programming error: non-audio Region tested for overlap in audio playlist" +msgstr "" + +#: libs/ardour/audio_playlist.cc:851 +msgid "xfade change" +msgstr "" + +#: libs/ardour/audio_playlist.cc:874 +msgid "region modified" +msgstr "" + +#: libs/ardour/audio_track.cc:105 libs/ardour/io.cc:1696 +#: libs/ardour/io.cc:1762 +msgid "Unknown connection \"%1\" listed for input of %2" +msgstr "" + +#: libs/ardour/audio_track.cc:107 libs/ardour/io.cc:1698 +#: libs/ardour/io.cc:1764 +msgid "in 1" +msgstr "" + +#: libs/ardour/audio_track.cc:108 libs/ardour/io.cc:1699 +#: libs/ardour/io.cc:1765 +msgid "No input connections available as a replacement" +msgstr "" + +#: libs/ardour/audio_track.cc:112 libs/ardour/io.cc:1703 +#: libs/ardour/io.cc:1769 +msgid "Connection %1 was not available - \"in 1\" used instead" +msgstr "" + +#: libs/ardour/audio_track.cc:121 libs/ardour/io.cc:1778 +msgid "improper input channel list in XML node (%1)" +msgstr "" + +#: libs/ardour/audio_track.cc:162 libs/ardour/audio_track.cc:175 +msgid "AudioTrack: audio diskstream \"%1\" not known by session" +msgstr "" + +#: libs/ardour/audio_track.cc:216 +msgid "programming error: AudioTrack given state without diskstream!" +msgstr "" + +#: libs/ardour/audioengine.cc:146 +msgid "cannot activate JACK client" +msgstr "" + +#: libs/ardour/audioengine.cc:421 +msgid "register input port called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:457 +msgid "register output port called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:539 +msgid "connect called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:555 +msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)" +msgstr "" + +#: libs/ardour/audioengine.cc:568 libs/ardour/audioengine.cc:597 +msgid "disconnect called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:655 +msgid "get_port_by_name() called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:699 +msgid "get_ports called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:817 +msgid "get_nth_physical called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:845 +msgid "get_port_total_latency() called with no JACK client connection" +msgstr "" + +#: libs/ardour/audioengine.cc:851 +msgid "get_port_total_latency() called before engine was started" +msgstr "" + +#: libs/ardour/audioengine.cc:982 +msgid "Unable to connect to JACK server" +msgstr "" + +#: libs/ardour/audioengine.cc:985 +msgid "Could not connect to JACK server as \"%1\"" +msgstr "" + +#: libs/ardour/audioengine.cc:990 +msgid "JACK server started" +msgstr "" + +#: libs/ardour/audioengine.cc:1024 +msgid "cannot shutdown connection to JACK" +msgstr "" + +#: libs/ardour/audioengine.cc:1049 +msgid "failed to connect to JACK" +msgstr "" + +#: libs/ardour/audioengine.cc:1067 +msgid "could not reregister %1" +msgstr "" + +#: libs/ardour/audioengine.cc:1125 +msgid "could not reconnect %1 and %2 (err = %3)" +msgstr "" + +#: libs/ardour/audiofilesource.cc:355 libs/ardour/session_state.cc:2575 +msgid "" +"there are already 1000 files with names like %1; versioning discontinued" +msgstr "" + +#: libs/ardour/audiofilesource.cc:369 libs/ardour/session_state.cc:2589 +msgid "cannot rename audio file source from %1 to %2 (%3)" +msgstr "" + +#: libs/ardour/audiofilesource.cc:376 libs/ardour/session_state.cc:2604 +msgid "cannot remove peakfile %1 for %2 (%3)" +msgstr "" + +#: libs/ardour/audiofilesource.cc:420 +msgid "FileSource: search path not set" +msgstr "" + +#: libs/ardour/audiofilesource.cc:444 +msgid "" +"FileSource: \"%1\" is ambigous when searching %2\n" +"\t" +msgstr "" + +#: libs/ardour/audiofilesource.cc:450 +msgid "Filesource: cannot find required file (%1): while searching %2" +msgstr "" + +#: libs/ardour/audiofilesource.cc:473 +msgid "Filesource: cannot find required file (%1): %2" +msgstr "" + +#: libs/ardour/audiofilesource.cc:478 +msgid "Filesource: cannot check for existing file (%1): %2" +msgstr "" + +#: libs/ardour/audiofilesource.cc:534 libs/ardour/insert.cc:532 +#: libs/ardour/session.cc:1967 libs/ardour/sndfilesource.cc:109 +msgid "programming error: %1" +msgstr "" + +#: libs/ardour/audiofilesource.cc:540 +msgid "" +"Programming error! Ardour tried to rename a file over another file! It's " +"safe to continue working, but please report this to the developers." +msgstr "" + +#: libs/ardour/audiofilesource.cc:545 +msgid "cannot rename audio file for %1 to %2" +msgstr "" + +#: libs/ardour/audiofilter.cc:47 +msgid "audiofilter: error creating name for new audio file based on %1" +msgstr "" + +#: libs/ardour/audiofilter.cc:57 +msgid "audiofilter: error creating new audio file %1 (%2)" +msgstr "" + +#: libs/ardour/audioregion.cc:888 libs/ardour/audioregion.cc:950 +msgid "fade in change" +msgstr "" + +#: libs/ardour/audioregion.cc:1321 +#, c-format +msgid "normalized to %.2fdB" +msgstr "" + +#: libs/ardour/audioregion.cc:1339 +msgid "envelope change" +msgstr "" + +#: libs/ardour/audiosource.cc:144 +msgid "poll on peak request pipe failed (%1)" +msgstr "" + +#: libs/ardour/audiosource.cc:151 +msgid "Error on peak thread request pipe" +msgstr "" + +#: libs/ardour/audiosource.cc:184 +msgid "Error reading from peak request pipe" +msgstr "" + +#: libs/ardour/audiosource.cc:216 libs/ardour/session_butler.cc:80 +#: libs/ardour/session_midi.cc:1073 +msgid "Cannot create transport request signal pipe (%1)" +msgstr "" + +#: libs/ardour/audiosource.cc:221 libs/ardour/audiosource.cc:226 +msgid "UI: cannot set O_NONBLOCK on peak request pipe (%1)" +msgstr "" + +#: libs/ardour/audiosource.cc:231 +msgid "AudioSource: could not create peak thread" +msgstr "" + +#: libs/ardour/audiosource.cc:326 +msgid "cannot rename peakfile for %1 from %2 to %3 (%4)" +msgstr "" + +#: libs/ardour/audiosource.cc:368 +msgid "AudioSource: cannot stat peakfile \"%1\"" +msgstr "" + +#: libs/ardour/audiosource.cc:466 +msgid "cannot read sample data for unscaled peak computation" +msgstr "" + +#: libs/ardour/audiosource.cc:486 libs/ardour/audiosource.cc:557 +#: libs/ardour/audiosource.cc:793 libs/ardour/audiosource.cc:882 +msgid "AudioSource: cannot open peakpath \"%1\" (%2)" +msgstr "" + +#: libs/ardour/audiosource.cc:657 +msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3" +msgstr "" + +#: libs/ardour/audiosource.cc:804 +msgid "%1: could not write read raw data for peak computation (%2)" +msgstr "" + +#: libs/ardour/audiosource.cc:829 +msgid "%1: could not write peak file data (%2)" +msgstr "" + +#: libs/ardour/auditioner.cc:118 +msgid "Auditioning of non-audio regions not yet supported" +msgstr "" + +#: libs/ardour/automation_event.cc:67 libs/ardour/location.cc:375 +#: libs/ardour/tempo.cc:226 +msgid "initial" +msgstr "" + +#: libs/ardour/automation_event.cc:240 +msgid "cleared" +msgstr "" + +#: libs/ardour/automation_event.cc:412 +msgid "added event" +msgstr "" + +#: libs/ardour/automation_event.cc:429 +msgid "removed event" +msgstr "" + +#: libs/ardour/automation_event.cc:444 +msgid "removed multiple events" +msgstr "" + +#: libs/ardour/automation_event.cc:475 libs/ardour/automation_event.cc:506 +msgid "removed range" +msgstr "" + +#: libs/ardour/automation_event.cc:536 +msgid "event range adjusted" +msgstr "" + +#: libs/ardour/automation_event.cc:558 +msgid "event adjusted" +msgstr "" + +#: libs/ardour/automation_event.cc:673 libs/ardour/automation_event.cc:778 +#: libs/ardour/panner.cc:889 +msgid "programming error:" +msgstr "" + +#: libs/ardour/automation_event.cc:1087 +msgid "cut/copy/clear" +msgstr "" + +#: libs/ardour/automation_event.cc:1120 +msgid "copy" +msgstr "" + +#: libs/ardour/automation_event.cc:1188 libs/ardour/playlist.cc:960 +msgid "paste" +msgstr "" + +#: libs/ardour/automation_event.cc:1243 +msgid "" +"automation list: no x-coordinate stored for control point (point ignored)" +msgstr "" + +#: libs/ardour/automation_event.cc:1249 +msgid "" +"automation list: no y-coordinate stored for control point (point ignored)" +msgstr "" + +#: libs/ardour/configuration.cc:87 +msgid "loading system configuration file %1" +msgstr "" + +#: libs/ardour/configuration.cc:90 +msgid "Ardour: cannot read system configuration file \"%1\"" +msgstr "" + +#: libs/ardour/configuration.cc:97 +msgid "Ardour: system configuration file \"%1\" not loaded successfully." +msgstr "" + +#: libs/ardour/configuration.cc:111 +msgid "loading user configuration file %1" +msgstr "" + +#: libs/ardour/configuration.cc:114 +msgid "Ardour: cannot read configuration file \"%1\"" +msgstr "" + +#: libs/ardour/configuration.cc:121 +msgid "Ardour: user configuration file \"%1\" not loaded successfully." +msgstr "" + +#: libs/ardour/configuration.cc:141 +msgid "Config file %1 not saved" +msgstr "" + +#: libs/ardour/configuration.cc:226 +msgid "ill-formed MIDI port specification in ardour rcfile (ignored)" +msgstr "" + +#: libs/ardour/connection.cc:183 +msgid "Node for Connection has no \"name\" property" +msgstr "" + +#: libs/ardour/connection.cc:191 +msgid "Node for Connection has no \"connections\" property" +msgstr "" + +#: libs/ardour/connection.cc:227 libs/ardour/io.cc:1838 +msgid "IO: badly formed string in XML node for inputs \"%1\"" +msgstr "" + +#: libs/ardour/connection.cc:232 libs/ardour/io.cc:1843 +msgid "bad input string in XML node \"%1\"" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:84 +msgid "control protocol name \"%1\" has no descriptor" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:89 +msgid "control protocol name \"%1\" could not be initialized" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:145 +msgid "Instantiating mandatory control protocol %1" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:179 +msgid "Control protocol %1 not usable" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:192 +msgid "Control surface protocol discovered: \"%1\"" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:210 +msgid "ControlProtocolManager: cannot load module \"%1\" (%2)" +msgstr "" + +#: libs/ardour/control_protocol_manager.cc:218 +msgid "ControlProtocolManager: module \"%1\" has no descriptor function." +msgstr "" + +#: libs/ardour/crossfade.cc:120 +msgid "Crossfade: no \"in\" region in state" +msgstr "" + +#: libs/ardour/crossfade.cc:127 +msgid "Crossfade: no \"in\" region %1 found in playlist %2" +msgstr "" + +#: libs/ardour/crossfade.cc:137 +msgid "Crossfade: no \"out\" region in state" +msgstr "" + +#: libs/ardour/crossfade.cc:144 +msgid "Crossfade: no \"out\" region %1 found in playlist %2" +msgstr "" + +#: libs/ardour/crossfade.cc:491 +msgid "active changed" +msgstr "" + +#: libs/ardour/crossfade.cc:740 +msgid "old-style crossfade information - no position information" +msgstr "" + +#: libs/ardour/curve.cc:117 libs/ardour/globals.cc:348 +#: libs/ardour/insert.cc:454 libs/ardour/session.cc:2432 +#: libs/ardour/session.cc:2486 +msgid "programming error: " +msgstr "" + +#: libs/ardour/cycle_timer.cc:37 +msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo" +msgstr "" + +#: libs/ardour/cycle_timer.cc:49 +msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo" +msgstr "" + +#: libs/ardour/cycle_timer.cc:72 +msgid "cannot locate cpu MHz in /proc/cpuinfo" +msgstr "" + +#: libs/ardour/destructive_filesource.cc:200 +msgid "DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)" +msgstr "" + +#: libs/ardour/destructive_filesource.cc:213 +#: libs/ardour/destructive_filesource.cc:258 +#: libs/ardour/destructive_filesource.cc:265 +msgid "DestructiveFileSource: \"%1\" bad write (%2)" +msgstr "" + +#: libs/ardour/destructive_filesource.cc:403 +msgid "" +"Filesource: start time is already set for existing file (%1): Cannot change " +"start time." +msgstr "" + +#: libs/ardour/globals.cc:110 +msgid "no MIDI ports specified: no MMC or MTC control possible" +msgstr "" + +#: libs/ardour/globals.cc:125 +msgid "MIDI port specifications for \"%1\" are not understandable." +msgstr "" + +#: libs/ardour/globals.cc:138 libs/ardour/globals.cc:142 +#: libs/ardour/globals.cc:146 +msgid "default" +msgstr "" + +#: libs/ardour/globals.cc:174 +msgid "No MMC control (MIDI port \"%1\" not available)" +msgstr "" + +#: libs/ardour/globals.cc:180 +msgid "No MTC support (MIDI port \"%1\" not available)" +msgstr "" + +#: libs/ardour/globals.cc:185 +msgid "No MIDI parameter support (MIDI port \"%1\" not available)" +msgstr "" + +#: libs/ardour/import.cc:77 +msgid "Import: cannot open input sound file \"%1\"" +msgstr "" + +#: libs/ardour/import.cc:82 +msgid "resampling audio" +msgstr "" + +#: libs/ardour/import.cc:86 +msgid "Import: cannot open converted sound file \"%1\"" +msgstr "" + +#: libs/ardour/import.cc:91 +msgid "Import: error while resampling sound file \"%1\"" +msgstr "" + +#: libs/ardour/import.cc:145 +msgid "Session::import_audiofile: cannot open new file source for channel %1" +msgstr "" + +#: libs/ardour/import.cc:163 +msgid "converting audio" +msgstr "" + +#: libs/ardour/import.cc:195 +msgid "building region" +msgstr "" + +#: libs/ardour/import.cc:197 +msgid "building regions" +msgstr "" + +#: libs/ardour/import.cc:309 +msgid "Import/SRC: could not open input file: %1" +msgstr "" + +#: libs/ardour/import.cc:317 +msgid "Import/SRC: could not open output file: %1" +msgstr "" + +#: libs/ardour/import.cc:326 +msgid "Import: src_new() failed : %1" +msgstr "" + +#: libs/ardour/import.cc:354 +msgid "Import: %1" +msgstr "" + +#: libs/ardour/insert.cc:651 libs/ardour/insert.cc:960 +msgid "XML node describing insert is missing the `type' field" +msgstr "" + +#: libs/ardour/insert.cc:660 +msgid "unknown plugin type %1 in plugin insert state" +msgstr "" + +#: libs/ardour/insert.cc:672 +msgid "XML node describing insert is missing the `id' field" +msgstr "" + +#: libs/ardour/insert.cc:685 +msgid "" +"Found a reference to a plugin (\"%1\") that is unknown.\n" +"Perhaps it was removed or moved since it was last used." +msgstr "" + +#: libs/ardour/insert.cc:723 libs/ardour/insert.cc:977 +msgid "XML node describing insert is missing a Redirect node" +msgstr "" + +#: libs/ardour/insert.cc:728 +msgid "XML node describing a plugin insert is missing the `%1' information" +msgstr "" + +#: libs/ardour/insert.cc:752 +msgid "PluginInsert: Auto: no ladspa port number" +msgstr "" + +#: libs/ardour/insert.cc:759 +msgid "PluginInsert: Auto: port id out of range" +msgstr "" + +#: libs/ardour/insert.cc:775 +msgid "XML node describing a port automation is missing the `%1' information" +msgstr "" + +#: libs/ardour/insert.cc:878 +msgid "PortInsert: cannot add input port" +msgstr "" + +#: libs/ardour/insert.cc:883 +msgid "PortInsert: cannot add output port" +msgstr "" + +#: libs/ardour/insert.cc:965 +msgid "non-port insert XML used for port plugin insert" +msgstr "" + +#: libs/ardour/io.cc:603 +msgid "IO: cannot disconnect input port %1 from %2" +msgstr "" + +#: libs/ardour/io.cc:671 +msgid "IO: cannot disconnect output port %1 from %2" +msgstr "" + +#: libs/ardour/io.cc:822 libs/ardour/io.cc:1177 libs/ardour/io.cc:1303 +#, c-format +msgid "%s/out" +msgstr "" + +#: libs/ardour/io.cc:824 libs/ardour/io.cc:1179 libs/ardour/io.cc:1305 +#: libs/ardour/io.cc:2688 +#, c-format +msgid "%s/out %u" +msgstr "" + +#: libs/ardour/io.cc:828 libs/ardour/io.cc:1184 libs/ardour/io.cc:1309 +msgid "IO: cannot register output port %1" +msgstr "" + +#: libs/ardour/io.cc:934 libs/ardour/io.cc:1037 libs/ardour/io.cc:1143 +#, c-format +msgid "%s/in" +msgstr "" + +#: libs/ardour/io.cc:936 libs/ardour/io.cc:1040 libs/ardour/io.cc:1146 +#: libs/ardour/io.cc:2658 +#, c-format +msgid "%s/in %u" +msgstr "" + +#: libs/ardour/io.cc:940 libs/ardour/io.cc:1046 libs/ardour/io.cc:1151 +msgid "IO: cannot register input port %1" +msgstr "" + +#: libs/ardour/io.cc:1551 +msgid "IO::connecting_became_legal() called without a pending state node" +msgstr "" + +#: libs/ardour/io.cc:1574 +msgid "IO::ports_became_legal() called without a pending state node" +msgstr "" + +#: libs/ardour/io.cc:1603 +msgid "incorrect XML node \"%1\" passed to IO object" +msgstr "" + +#: libs/ardour/io.cc:1719 libs/ardour/io.cc:1787 +msgid "Unknown connection \"%1\" listed for output of %2" +msgstr "" + +#: libs/ardour/io.cc:1721 libs/ardour/io.cc:1789 +msgid "out 1" +msgstr "" + +#: libs/ardour/io.cc:1722 libs/ardour/io.cc:1790 +msgid "No output connections available as a replacement" +msgstr "" + +#: libs/ardour/io.cc:1726 libs/ardour/io.cc:1794 +msgid "Connection %1 was not available - \"out 1\" used instead" +msgstr "" + +#: libs/ardour/io.cc:1740 +msgid "%1: cannot create I/O ports" +msgstr "" + +#: libs/ardour/io.cc:1803 +msgid "improper output channel list in XML node (%1)" +msgstr "" + +#: libs/ardour/io.cc:1888 +msgid "IO: badly formed string in XML node for outputs \"%1\"" +msgstr "" + +#: libs/ardour/io.cc:1893 +msgid "IO: bad output string in XML node \"%1\"" +msgstr "" + +#: libs/ardour/io.cc:2391 +msgid "%1: could not open automation event file \"%2\"" +msgstr "" + +#: libs/ardour/io.cc:2430 +msgid "%1: cannot open automation event file \"%2\" (%2)" +msgstr "" + +#: libs/ardour/io.cc:2445 +msgid "badly formed version number in automation event file \"%1\"" +msgstr "" + +#: libs/ardour/io.cc:2449 +msgid "no version information in automation event file \"%1\"" +msgstr "" + +#: libs/ardour/io.cc:2454 +msgid "mismatched automation event file version (%1)" +msgstr "" + +#: libs/ardour/io.cc:2462 +msgid "badly formatted automation event record at line %1 of %2 (ignored)" +msgstr "" + +#: libs/ardour/io.cc:2482 +msgid "dubious automation event found (and ignored)" +msgstr "" + +#: libs/ardour/io.cc:2486 libs/ardour/panner.cc:288 +#: libs/ardour/redirect.cc:148 +msgid "loaded from disk" +msgstr "" + +#: libs/ardour/io.cc:2630 +msgid "automation write/touch" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:87 +msgid "LADSPA: module has no descriptor function." +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:92 +msgid "LADSPA: plugin has gone away since discovery!" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:99 +msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:315 +msgid "" +"illegal parameter number used with plugin \"%1\". This mayindicate a change " +"in the plugin design, and presets may beinvalid" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:394 +msgid "Bad node sent to LadspaPlugin::set_state" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:407 +msgid "LADSPA: no ladspa port number" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:413 +msgid "LADSPA: no ladspa port data" +msgstr "" + +#: libs/ardour/ladspa_plugin.cc:653 +msgid "LADSPA: cannot load module from \"%1\"" +msgstr "" + +#: libs/ardour/location.cc:295 +msgid "incorrect XML node passed to Location::set_state" +msgstr "" + +#: libs/ardour/location.cc:300 +msgid "XML node for Location has no ID information" +msgstr "" + +#: libs/ardour/location.cc:306 +msgid "XML node for Location has no name information" +msgstr "" + +#: libs/ardour/location.cc:313 +msgid "XML node for Location has no start information" +msgstr "" + +#: libs/ardour/location.cc:324 +msgid "XML node for Location has no end information" +msgstr "" + +#: libs/ardour/location.cc:333 +msgid "XML node for Location has no flags information" +msgstr "" + +#: libs/ardour/location.cc:421 +msgid "Locations: attempt to use unknown location as selected location" +msgstr "" + +#: libs/ardour/location.cc:448 libs/ardour/playlist.cc:1204 +msgid "clear" +msgstr "" + +#: libs/ardour/location.cc:473 +msgid "clear markers" +msgstr "" + +#: libs/ardour/location.cc:501 +msgid "clear ranges" +msgstr "" + +#: libs/ardour/location.cc:519 +msgid "add" +msgstr "" + +#: libs/ardour/location.cc:557 +msgid "remove" +msgstr "" + +#: libs/ardour/location.cc:597 +msgid "incorrect XML mode passed to Locations::set_state" +msgstr "" + +#: libs/ardour/location.cc:615 +msgid "could not load location from session file - ignored" +msgstr "" + +#: libs/ardour/mtc_slave.cc:196 +msgid "MTC Slave: atomic read of current time failed, sleeping!" +msgstr "" + +#: libs/ardour/named_selection.cc:77 +msgid "Chunk %1 uses an unknown playlist \"%2\"" +msgstr "" + +#: libs/ardour/named_selection.cc:80 +msgid "Chunk %1 contains misformed playlist information" +msgstr "" + +#: libs/ardour/panner.cc:211 +msgid "automation write pass" +msgstr "" + +#: libs/ardour/panner.cc:251 +#, c-format +msgid "error writing pan automation file (%s)" +msgstr "" + +#: libs/ardour/panner.cc:279 +msgid "" +"badly formatted pan automation event record at line %1 of %2 (ignored) [%3]" +msgstr "" + +#: libs/ardour/panner.cc:794 +msgid "badly-formed positional data for Multi2dPanner - ignored" +msgstr "" + +#: libs/ardour/panner.cc:1083 +msgid "cannot open pan automation file \"%1\" for saving (%2)" +msgstr "" + +#: libs/ardour/panner.cc:1119 +msgid "cannot open pan automation file %1 (%2)" +msgstr "" + +#: libs/ardour/panner.cc:1132 +msgid "badly formed version number in pan automation event file \"%1\"" +msgstr "" + +#: libs/ardour/panner.cc:1136 +msgid "" +"no version information in pan automation event file \"%1\" (first line = %2)" +msgstr "" + +#: libs/ardour/panner.cc:1142 +msgid "mismatched pan automation event file version (%1)" +msgstr "" + +#: libs/ardour/panner.cc:1156 +msgid "too many panner states found in pan automation file %1" +msgstr "" + +#: libs/ardour/panner.cc:1297 +msgid "Unknown panner plugin \"%1\" found in pan state - ignored" +msgstr "" + +#: libs/ardour/panner.cc:1303 +msgid "panner plugin node has no type information!" +msgstr "" + +#: libs/ardour/playlist.cc:251 +msgid "playlist const copy constructor called" +msgstr "" + +#: libs/ardour/playlist.cc:257 +msgid "playlist non-const copy constructor called" +msgstr "" + +#: libs/ardour/playlist.cc:498 +msgid "add region" +msgstr "" + +#: libs/ardour/playlist.cc:550 +msgid "replace region" +msgstr "" + +#: libs/ardour/playlist.cc:563 +msgid "remove region" +msgstr "" + +#: libs/ardour/playlist.cc:635 +msgid "separate" +msgstr "" + +#: libs/ardour/playlist.cc:899 +msgid "cut" +msgstr "" + +#: libs/ardour/playlist.cc:989 +msgid "duplicate" +msgstr "" + +#: libs/ardour/playlist.cc:1044 +msgid "split" +msgstr "" + +#: libs/ardour/playlist.cc:1121 +msgid "%1: bounds changed received for region (%2)not in playlist" +msgstr "" + +#: libs/ardour/playlist.cc:1377 +msgid "Playlist: cannot create region from state file" +msgstr "" + +#: libs/ardour/playlist.cc:1737 +msgid "nudged" +msgstr "" + +#: libs/ardour/playlist_factory.cc:40 +msgid "" +"programming error: Playlist::copyPlaylist called with unknown Playlist type" +msgstr "" + +#: libs/ardour/plugin.cc:218 +msgid "Could not locate HOME. Preset not saved." +msgstr "" + +#: libs/ardour/plugin.cc:228 libs/ardour/plugin.cc:234 +msgid "Could not create %1. Preset not saved. (%2)" +msgstr "" + +#: libs/ardour/plugin.cc:239 +msgid "Error saving presets file %1." +msgstr "" + +#: libs/ardour/plugin_manager.cc:192 +msgid "Could not parse rdf file: %1" +msgstr "" + +#: libs/ardour/plugin_manager.cc:232 +msgid "LADSPA: cannot load module \"%1\" (%2)" +msgstr "" + +#: libs/ardour/plugin_manager.cc:239 +msgid "LADSPA: module \"%1\" has no descriptor function." +msgstr "" + +#: libs/ardour/plugin_manager.cc:295 libs/ardour/plugin_manager.cc:307 +msgid "Unknown" +msgstr "" + +#: libs/ardour/plugin_manager.cc:380 +msgid "" +"VST plugin %1 does not support processReplacing, and so cannot be used in " +"ardour at this time" +msgstr "" + +#: libs/ardour/recent_sessions.cc:44 +msgid "cannot open recent session file %1 (%2)" +msgstr "" + +#: libs/ardour/redirect.cc:77 +msgid "programming error: unknown Redirect type in Redirect::Clone!\n" +msgstr "" + +#: libs/ardour/redirect.cc:102 libs/ardour/utils.cc:194 +msgid "pre" +msgstr "" + +#: libs/ardour/redirect.cc:104 libs/ardour/utils.cc:197 +msgid "post" +msgstr "" + +#: libs/ardour/redirect.cc:107 +msgid "Redirect: unknown placement string \"%1\" (ignored)" +msgstr "" + +#: libs/ardour/redirect.cc:125 +msgid "%1: cannot open %2 to load automation data (%3)" +msgstr "" + +#: libs/ardour/redirect.cc:154 +msgid "%1: cannot load automation data from %2" +msgstr "" + +#: libs/ardour/redirect.cc:175 +msgid "%1: cannot open %2 to store automation data (%3)" +msgstr "" + +#: libs/ardour/redirect.cc:194 libs/ardour/redirect.cc:201 +msgid "%1: could not save automation state to %2" +msgstr "" + +#: libs/ardour/redirect.cc:246 +msgid "Could not get state from Redirect (%1). Problem with save_automation" +msgstr "" + +#: libs/ardour/redirect.cc:296 +msgid "incorrect XML node \"%1\" passed to Redirect object" +msgstr "" + +#: libs/ardour/redirect.cc:318 +msgid "%1: Automation node has no path property" +msgstr "" + +#: libs/ardour/redirect.cc:343 +msgid "XML node describing an IO is missing an IO node" +msgstr "" + +#: libs/ardour/redirect.cc:348 +msgid "XML node describing a redirect is missing the `active' field" +msgstr "" + +#: libs/ardour/redirect.cc:358 +msgid "XML node describing a redirect is missing the `placement' field" +msgstr "" + +#: libs/ardour/redirect.cc:467 +msgid "active_changed" +msgstr "" + +#: libs/ardour/region.cc:901 +msgid "Session: XMLNode describing a Region is incomplete (no id)" +msgstr "" + +#: libs/ardour/region.cc:908 +msgid "Session: XMLNode describing a Region is incomplete (no name)" +msgstr "" + +#: libs/ardour/region_factory.cc:53 libs/ardour/region_factory.cc:70 +msgid "" +"programming error: RegionFactory::create() called with unknown Region type" +msgstr "" + +#: libs/ardour/route.cc:81 libs/ardour/session.cc:1434 +#: libs/ardour/session.cc:1440 libs/ardour/session.cc:3064 +msgid "signal" +msgstr "" + +#: libs/ardour/route.cc:1407 +msgid "Could not get state of route. Problem with save_automation" +msgstr "" + +#: libs/ardour/route.cc:1459 +msgid "Send construction failed" +msgstr "" + +#: libs/ardour/route.cc:1481 +msgid "unknown Insert type \"%1\"; ignored" +msgstr "" + +#: libs/ardour/route.cc:1487 +msgid "Insert XML node has no type property" +msgstr "" + +#: libs/ardour/route.cc:1492 +msgid "insert could not be created. Ignored." +msgstr "" + +#: libs/ardour/route.cc:1508 +msgid "Bad node sent to Route::set_state() [%1]" +msgstr "" + +#: libs/ardour/route.cc:1572 +msgid "Route %1: unknown edit group \"%2 in saved state (ignored)" +msgstr "" + +#: libs/ardour/route.cc:1588 libs/ardour/route.cc:1592 +msgid "badly formed order key string in state file! [%1] ... ignored." +msgstr "" + +#: libs/ardour/route.cc:1673 libs/ardour/route.cc:1761 +msgid "[control]" +msgstr "" + +#: libs/ardour/route.cc:1693 +msgid "Route %1: unknown mix group \"%2 in saved state (ignored)" +msgstr "" + +#: libs/ardour/send.cc:99 +msgid "XML node describing a send is missing a Redirect node" +msgstr "" + +#: libs/ardour/session.cc:111 +msgid "Could not resolve path: %1 (%2)" +msgstr "" + +#: libs/ardour/session.cc:123 +msgid "cannot check session path %1 (%2)" +msgstr "" + +#: libs/ardour/session.cc:153 +msgid "cannot check statefile %1 (%2)" +msgstr "" + +#: libs/ardour/session.cc:189 +msgid "%1 is not an Ardour snapshot file" +msgstr "" + +#: libs/ardour/session.cc:206 +msgid "cannot determine current working directory (%1)" +msgstr "" + +#: libs/ardour/session.cc:223 +msgid "unknown file type for session %1" +msgstr "" + +#: libs/ardour/session.cc:343 +msgid "monitor" +msgstr "" + +#: libs/ardour/session.cc:351 +msgid "master" +msgstr "" + +#: libs/ardour/session.cc:633 +msgid "could not setup Click I/O" +msgstr "" + +#: libs/ardour/session.cc:654 +msgid "cannot setup Click I/O" +msgstr "" + +#: libs/ardour/session.cc:676 +msgid "cannot create Auditioner: no auditioning of regions possible" +msgstr "" + +#: libs/ardour/session.cc:688 +#, c-format +msgid "out %<PRIu32>" +msgstr "" + +#: libs/ardour/session.cc:700 +#, c-format +msgid "in %<PRIu32>" +msgstr "" + +#: libs/ardour/session.cc:714 +#, c-format +msgid "out %<PRIu32>+%<PRIu32>" +msgstr "" + +#: libs/ardour/session.cc:728 +#, c-format +msgid "in %<PRIu32>+%<PRIu32>" +msgstr "" + +#: libs/ardour/session.cc:761 +msgid "cannot setup master inputs" +msgstr "" + +#: libs/ardour/session.cc:769 +msgid "cannot setup master outputs" +msgstr "" + +#: libs/ardour/session.cc:780 +msgid "Master Out" +msgstr "" + +#: libs/ardour/session.cc:852 +msgid "cannot setup control inputs" +msgstr "" + +#: libs/ardour/session.cc:860 +msgid "cannot set up master outputs" +msgstr "" + +#: libs/ardour/session.cc:1043 +msgid "Session: you can't use that location for auto punch (start <= end)" +msgstr "" + +#: libs/ardour/session.cc:1080 +msgid "Session: you can't use a mark for auto loop" +msgstr "" + +#: libs/ardour/session.cc:1452 +msgid "feedback loop setup between %1 and %2" +msgstr "" + +#: libs/ardour/session.cc:1629 libs/ardour/session.cc:1750 +msgid "cannot configure %1 in/%2 out configuration for new audio track" +msgstr "" + +#: libs/ardour/session.cc:1687 +msgid "Session: could not create new audio track." +msgstr "" + +#: libs/ardour/session.cc:1800 +msgid "Session: could not create new audio route." +msgstr "" + +#: libs/ardour/session.cc:2319 +msgid "cannot create new name for region \"%1\"" +msgstr "" + +#: libs/ardour/session.cc:2383 +msgid "too many regions with names like %1" +msgstr "" + +#: libs/ardour/session.cc:2883 +msgid "There are already %1 recordings for %2, which I consider too many." +msgstr "" + +#: libs/ardour/session.cc:3085 +msgid "Cannot compile tape track regexp for use (%1)" +msgstr "" + +#: libs/ardour/session.cc:3232 +msgid "programming error: unknown type of Insert created!" +msgstr "" + +#: libs/ardour/session.cc:3238 +msgid "programming error: unknown type of Redirect created!" +msgstr "" + +#: libs/ardour/session.cc:3261 +msgid "programming error: unknown type of Insert deleted!" +msgstr "" + +#: libs/ardour/session.cc:3267 +msgid "programming error: unknown type of Redirect deleted!" +msgstr "" + +#: libs/ardour/session.cc:3573 +msgid "too many bounced versions of playlist \"%1\"" +msgstr "" + +#: libs/ardour/session.cc:3582 +msgid "cannot create new audio file \"%1\" for %2" +msgstr "" + +#: libs/ardour/session_butler.cc:85 libs/ardour/session_butler.cc:90 +msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)" +msgstr "" + +#: libs/ardour/session_butler.cc:95 +msgid "Session: could not create butler thread" +msgstr "" + +#: libs/ardour/session_butler.cc:183 +msgid "poll on butler request pipe failed (%1)" +msgstr "" + +#: libs/ardour/session_butler.cc:190 +msgid "Error on butler thread request pipe: fd=%1 err=%2" +msgstr "" + +#: libs/ardour/session_butler.cc:231 +msgid "Error reading from butler request pipe" +msgstr "" + +#: libs/ardour/session_butler.cc:268 +msgid "Butler read ahead failure on dstream %1" +msgstr "" + +#: libs/ardour/session_butler.cc:311 +msgid "Butler write-behind failure on dstream %1" +msgstr "" + +#: libs/ardour/session_click.cc:160 +msgid "cannot open click soundfile %1 (%2)" +msgstr "" + +#: libs/ardour/session_click.cc:169 +msgid "cannot read data from click soundfile" +msgstr "" + +#: libs/ardour/session_click.cc:196 +msgid "cannot open click emphasis soundfile %1 (%2)" +msgstr "" + +#: libs/ardour/session_click.cc:204 +msgid "cannot read data from click emphasis soundfile" +msgstr "" + +#: libs/ardour/session_command.cc:49 +msgid "Tried to reconstitute a MementoCommand with no contents, failing. id=" +msgstr "" + +#: libs/ardour/session_command.cc:95 +msgid "could not reconstitute MementoCommand from XMLNode. id=" +msgstr "" + +#: libs/ardour/session_events.cc:161 +msgid "Session: cannot have two events of type %1 at the same frame (%2)." +msgstr "" + +#: libs/ardour/session_events.cc:422 +msgid "Programming error: illegal event type in process_event (%1)" +msgstr "" + +#: libs/ardour/session_export.cc:63 +msgid "Export: no output file specified" +msgstr "" + +#: libs/ardour/session_export.cc:164 libs/ardour/session_export.cc:169 +msgid "illegal frame range in export specification" +msgstr "" + +#: libs/ardour/session_export.cc:174 +msgid "Bad data width size. Report me!" +msgstr "" + +#: libs/ardour/session_export.cc:204 +msgid "Export: cannot open output file \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_export.cc:214 +msgid "cannot initialize sample rate conversion: %1" +msgstr "" + +#: libs/ardour/session_export.cc:316 +msgid "an error occured during sample rate conversion: %1" +msgstr "" + +#: libs/ardour/session_export.cc:327 +msgid "warning, leftover frames overflowed, glitches might occur in output" +msgstr "" + +#: libs/ardour/session_export.cc:418 +msgid "Export: could not write data to output file (%1)" +msgstr "" + +#: libs/ardour/session_export.cc:502 +msgid "%1: cannot seek to %2 for export" +msgstr "" + +#: libs/ardour/session_midi.cc:95 +msgid "Ardour is slaved to MTC - port cannot be reset" +msgstr "" + +#: libs/ardour/session_midi.cc:110 +msgid "unknown port %1 requested for MTC" +msgstr "" + +#: libs/ardour/session_midi.cc:435 +msgid "Error reading from MIDI port %1" +msgstr "" + +#: libs/ardour/session_midi.cc:804 +msgid "Session: could not send full MIDI time code" +msgstr "" + +#: libs/ardour/session_midi.cc:863 +msgid "Session: cannot send quarter-frame MTC message (%1)" +msgstr "" + +#: libs/ardour/session_midi.cc:971 +msgid "MMC: cannot send command %1%2%3" +msgstr "" + +#: libs/ardour/session_midi.cc:1078 +msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)" +msgstr "" + +#: libs/ardour/session_midi.cc:1083 +msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)" +msgstr "" + +#: libs/ardour/session_midi.cc:1088 +msgid "Session: could not create transport thread" +msgstr "" + +#: libs/ardour/session_midi.cc:1117 +msgid "cannot send signal to midi thread! (%1)" +msgstr "" + +#: libs/ardour/session_midi.cc:1212 +msgid "MIDI thread poll failed (%1)" +msgstr "" + +#: libs/ardour/session_midi.cc:1224 +msgid "Error on transport thread request pipe" +msgstr "" + +#: libs/ardour/session_midi.cc:1251 +msgid "Error reading from transport request pipe" +msgstr "" + +#: libs/ardour/session_process.cc:104 +msgid "Session: error in no roll for %1" +msgstr "" + +#: libs/ardour/session_state.cc:103 +msgid "Could not use path %1 (%s)" +msgstr "" + +#: libs/ardour/session_state.cc:131 +msgid "end" +msgstr "" + +#: libs/ardour/session_state.cc:132 +msgid "start" +msgstr "" + +#: libs/ardour/session_state.cc:443 +msgid "Session: cannot create session dir \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:450 +msgid "Session: cannot create session peakfile dir \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:457 +msgid "Session: cannot create session sounds dir \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:464 +msgid "Session: cannot create session dead sounds dir \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:471 +msgid "Session: cannot create session automation dir \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:500 +msgid "Could not open %1 for writing mix template" +msgstr "" + +#: libs/ardour/session_state.cc:506 +msgid "Could not open mix template %1 for reading" +msgstr "" + +#: libs/ardour/session_state.cc:548 +msgid "Session: could not load diskstream via XML state" +msgstr "" + +#: libs/ardour/session_state.cc:597 +msgid "could not backup old state file, current state not saved." +msgstr "" + +#: libs/ardour/session_state.cc:612 +msgid "state could not be saved to %1" +msgstr "" + +#: libs/ardour/session_state.cc:619 +msgid "could not remove corrupt state file %1" +msgstr "" + +#: libs/ardour/session_state.cc:623 +msgid "could not restore state file from backup %1" +msgstr "" + +#: libs/ardour/session_state.cc:693 +msgid "%1: session state information file \"%2\" doesn't exist!" +msgstr "" + +#: libs/ardour/session_state.cc:704 libs/ardour/session_state.cc:2824 +msgid "Could not understand ardour file %1" +msgstr "" + +#: libs/ardour/session_state.cc:988 +msgid "programming error: Session: incorrect XML node sent to set_state()" +msgstr "" + +#: libs/ardour/session_state.cc:1047 +msgid "Session: XML state has no options section" +msgstr "" + +#: libs/ardour/session_state.cc:1051 +msgid "Session: XML state has no sources section" +msgstr "" + +#: libs/ardour/session_state.cc:1058 +msgid "Session: XML state has no Regions section" +msgstr "" + +#: libs/ardour/session_state.cc:1065 +msgid "Session: XML state has no playlists section" +msgstr "" + +#: libs/ardour/session_state.cc:1084 +msgid "Session: XML state has no diskstreams section" +msgstr "" + +#: libs/ardour/session_state.cc:1091 +msgid "Session: XML state has no connections section" +msgstr "" + +#: libs/ardour/session_state.cc:1098 +msgid "Session: XML state has no locations section" +msgstr "" + +#: libs/ardour/session_state.cc:1131 +msgid "Session: XML state has no edit groups section" +msgstr "" + +#: libs/ardour/session_state.cc:1138 +msgid "Session: XML state has no mix groups section" +msgstr "" + +#: libs/ardour/session_state.cc:1145 +msgid "Session: XML state has no Tempo Map section" +msgstr "" + +#: libs/ardour/session_state.cc:1152 +msgid "Session: XML state has no routes section" +msgstr "" + +#: libs/ardour/session_state.cc:1159 +msgid "Session: XML state has no click section" +msgstr "" + +#: libs/ardour/session_state.cc:1202 +msgid "Session: cannot create Route from XML description." +msgstr "" + +#: libs/ardour/session_state.cc:1243 +msgid "Session: cannot create Region from XML description." +msgstr "" + +#: libs/ardour/session_state.cc:1271 +msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)" +msgstr "" + +#: libs/ardour/session_state.cc:1279 libs/ardour/session_state.cc:1300 +msgid "" +"Session: XMLNode describing a AudioRegion references an unknown source id =%1" +msgstr "" + +#: libs/ardour/session_state.cc:1285 libs/ardour/session_state.cc:1306 +msgid "" +"Session: XMLNode describing a AudioRegion references a non-audio source id =%" +"1" +msgstr "" + +#: libs/ardour/session_state.cc:1377 +msgid "Session: cannot create Source from XML description." +msgstr "" + +#: libs/ardour/session_state.cc:1396 +msgid "" +"Found a sound file that cannot be used by Ardour. Talk to the progammers." +msgstr "" + +#: libs/ardour/session_state.cc:1418 +msgid "Could not create mix templates directory \"%1\" (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:1432 +msgid "Template \"%1\" already exists - new version not created" +msgstr "" + +#: libs/ardour/session_state.cc:1439 +msgid "mix template not saved" +msgstr "" + +#: libs/ardour/session_state.cc:1498 +msgid "cannot create session directory \"%1\"; ignored" +msgstr "" + +#: libs/ardour/session_state.cc:1509 +msgid "cannot create sounds directory \"%1\"; ignored" +msgstr "" + +#: libs/ardour/session_state.cc:1518 +msgid "cannot create dead sounds directory \"%1\"; ignored" +msgstr "" + +#: libs/ardour/session_state.cc:1527 +msgid "cannot create peak file directory \"%1\"; ignored" +msgstr "" + +#: libs/ardour/session_state.cc:1659 libs/ardour/session_state.cc:1680 +msgid "Session: cannot create Playlist from XML description." +msgstr "" + +#: libs/ardour/session_state.cc:1719 +msgid "Session: cannot create Named Selection from XML description." +msgstr "" + +#: libs/ardour/session_state.cc:1872 +msgid "Unknown node \"%1\" found in Connections list from state file" +msgstr "" + +#: libs/ardour/session_state.cc:2677 +msgid "cannot remove dead sound file %1 (%2)" +msgstr "" + +#: libs/ardour/session_state.cc:2778 +msgid "could not backup old history file, current history not saved." +msgstr "" + +#: libs/ardour/session_state.cc:2786 +msgid "history could not be saved to %1" +msgstr "" + +#: libs/ardour/session_state.cc:2794 +msgid "could not remove corrupt history file %1" +msgstr "" + +#: libs/ardour/session_state.cc:2798 +msgid "could not restore history file from backup %1" +msgstr "" + +#: libs/ardour/session_state.cc:2816 +msgid "Loading history from '%1'." +msgstr "" + +#: libs/ardour/session_state.cc:2819 +msgid "%1: session history file \"%2\" doesn't exist!" +msgstr "" + +#: libs/ardour/session_state.cc:2861 +msgid "Couldn't figure out how to make a Command out of a %1 XMLNode." +msgstr "" + +#: libs/ardour/session_time.cc:370 +msgid "Unknown JACK transport state %1 in sync callback" +msgstr "" + +#: libs/ardour/session_timefx.cc:79 +msgid "tempoize: error creating name for new audio file based on %1" +msgstr "" + +#: libs/ardour/session_timefx.cc:88 +msgid "tempoize: error creating new audio file %1 (%2)" +msgstr "" + +#: libs/ardour/session_timefx.cc:113 +msgid "tempoize: error reading data from %1" +msgstr "" + +#: libs/ardour/session_timefx.cc:126 libs/ardour/session_timefx.cc:138 +msgid "error writing tempo-adjusted data to %1" +msgstr "" + +#: libs/ardour/session_timefx.cc:144 +msgid "timefx code failure. please notify ardour-developers." +msgstr "" + +#: libs/ardour/session_transport.cc:117 +msgid "Cannot loop - no loop range defined" +msgstr "" + +#: libs/ardour/session_transport.cc:474 +msgid "" +"Seamless looping cannot be supported while Ardour is using JACK transport.\n" +"Recommend changing the configured options" +msgstr "" + +#: libs/ardour/session_transport.cc:743 +msgid "" +"Global varispeed cannot be supported while Ardour is connected to JACK " +"transport control" +msgstr "" + +#: libs/ardour/session_transport.cc:933 +msgid "please stop the transport before adjusting slave settings" +msgstr "" + +#: libs/ardour/session_transport.cc:966 +msgid "No MTC port defined: MTC slaving is impossible." +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:15 +msgid "WAV" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:16 +msgid "AIFF" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:17 +msgid "raw (no header)" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:18 +msgid "PAF (Ensoniq Paris)" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:19 +msgid "AU (Sun/NeXT)" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:20 +msgid "IRCAM" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:21 +msgid "W64 (64 bit WAV)" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:26 +msgid ".wav" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:27 +msgid ".aiff" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:28 +msgid ".raw" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:29 +msgid ".paf" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:30 +msgid ".au" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:31 +msgid ".ircam" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:32 +msgid ".w64" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:47 +msgid "16 bit" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:48 +msgid "24 bit" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:49 +msgid "32 bit" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:50 +msgid "8 bit" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:51 +msgid "float" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:64 +msgid "Little-endian (Intel)" +msgstr "" + +#: libs/ardour/sndfile_helpers.cc:65 +msgid "Big-endian (Mac)" +msgstr "" + +#: libs/ardour/sndfilesource.cc:143 +msgid "FileSource: cannot get host information for BWF header (%1)" +msgstr "" + +#: libs/ardour/sndfilesource.cc:167 +msgid "" +"cannot set broadcast info for audio file %1 (%2); dropping broadcast info " +"for this file" +msgstr "" + +#: libs/ardour/sndfilesource.cc:216 +msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)" +msgstr "" + +#: libs/ardour/sndfilesource.cc:222 +msgid "" +"SndFileSource: file only contains %1 channels; %2 is invalid as a channel " +"number" +msgstr "" + +#: libs/ardour/sndfilesource.cc:327 +msgid "SndFileSource: could not seek to frame %1 within %2 (%3)" +msgstr "" + +#: libs/ardour/sndfilesource.cc:378 +msgid "programming error: %1 %2" +msgstr "" + +#: libs/ardour/sndfilesource.cc:486 libs/ardour/sndfilesource.cc:507 +msgid "" +"cannot set broadcast info for audio file %1; Dropping broadcast info for " +"this file" +msgstr "" + +#: libs/ardour/sndfilesource.cc:521 +msgid "%1: cannot seek to %2" +msgstr "" + +#: libs/ardour/state_manager.cc:47 +msgid "cleared history" +msgstr "" + +#: libs/ardour/state_manager.cc:60 +msgid "" +"programming error: illegal state ID (%1) passed to StateManager::set_state() " +"(range = 0-%2)" +msgstr "" + +#: libs/ardour/tempo.cc:67 +msgid "TempoSection XML node has no \"start\" property" +msgstr "" + +#: libs/ardour/tempo.cc:75 +msgid "TempoSection XML node has an illegal \"start\" value" +msgstr "" + +#: libs/ardour/tempo.cc:82 +msgid "TempoSection XML node has no \"beats-per-minute\" property" +msgstr "" + +#: libs/ardour/tempo.cc:87 +msgid "TempoSection XML node has an illegal \"beats_per_minute\" value" +msgstr "" + +#: libs/ardour/tempo.cc:92 +msgid "TempoSection XML node has no \"movable\" property" +msgstr "" + +#: libs/ardour/tempo.cc:131 +msgid "MeterSection XML node has no \"start\" property" +msgstr "" + +#: libs/ardour/tempo.cc:139 +msgid "MeterSection XML node has an illegal \"start\" value" +msgstr "" + +#: libs/ardour/tempo.cc:146 +msgid "MeterSection XML node has no \"beats-per-bar\" property" +msgstr "" + +#: libs/ardour/tempo.cc:151 +msgid "MeterSection XML node has an illegal \"beats-per-bar\" value" +msgstr "" + +#: libs/ardour/tempo.cc:156 +msgid "MeterSection XML node has no \"note-type\" property" +msgstr "" + +#: libs/ardour/tempo.cc:161 +msgid "MeterSection XML node has an illegal \"note-type\" value" +msgstr "" + +#: libs/ardour/tempo.cc:166 +msgid "MeterSection XML node has no \"movable\" property" +msgstr "" + +#: libs/ardour/tempo.cc:259 +msgid "move metric" +msgstr "" + +#: libs/ardour/tempo.cc:330 +msgid "metric removed" +msgstr "" + +#: libs/ardour/tempo.cc:373 +msgid "add tempo" +msgstr "" + +#: libs/ardour/tempo.cc:402 +msgid "replace tempo" +msgstr "" + +#: libs/ardour/tempo.cc:435 +msgid "add meter" +msgstr "" + +#: libs/ardour/tempo.cc:463 +msgid "replaced meter" +msgstr "" + +#: libs/ardour/tempo.cc:483 libs/ardour/tempo.cc:499 +msgid "programming error: no tempo section in tempo map!" +msgstr "" + +#: libs/ardour/tempo.cc:538 +msgid "programming error: unhandled MetricSection type" +msgstr "" + +#: libs/ardour/tempo.cc:1265 libs/ardour/tempo.cc:1277 +msgid "Tempo map: could not set new state, restoring old one." +msgstr "" + +#: libs/ardour/tempo.cc:1301 +msgid "load XML data" +msgstr "" + +#: libs/ardour/utils.cc:237 +msgid "illegal or badly-formed string used for path (%1)" +msgstr "" + +#: libs/ardour/utils.cc:242 +msgid "path (%1) is ambiguous" +msgstr "" + +#: libs/ardour/utils.cc:304 libs/ardour/utils.cc:323 +msgid "Splice Edit" +msgstr "Fogredigering" + +#: libs/ardour/utils.cc:306 libs/ardour/utils.cc:319 +msgid "Slide Edit" +msgstr "Glidredigering" + +#: libs/ardour/utils.cc:309 +msgid "programming error: unknown edit mode string \"%1\"" +msgstr "" + +#: libs/ardour/utils.cc:330 libs/ardour/utils.cc:359 +msgid "Internal" +msgstr "Intern" + +#: libs/ardour/utils.cc:334 libs/ardour/utils.cc:355 +msgid "MTC" +msgstr "" + +#: libs/ardour/utils.cc:338 libs/ardour/utils.cc:352 +msgid "JACK" +msgstr "" + +#: libs/ardour/utils.cc:342 +msgid "programming error: unknown slave source string \"%1\"" +msgstr "" + +#: libs/ardour/vst_plugin.cc:178 +msgid "cannot create VST chunk directory: %1" +msgstr "" + +#: libs/ardour/vst_plugin.cc:186 +msgid "cannot check VST chunk directory: %1" +msgstr "" + +#: libs/ardour/vst_plugin.cc:193 +msgid "%1 exists but is not a directory" +msgstr "" + +#: libs/ardour/vst_plugin.cc:231 +msgid "Bad node sent to VSTPlugin::set_state" +msgstr "" + +#: libs/ardour/vst_plugin.cc:334 libs/ardour/vst_plugin.cc:345 +msgid "no support for presets using chunks at this time" +msgstr "" + +#: libs/ardour/vst_plugin.cc:495 +msgid "VST: cannot load module from \"%1\"" +msgstr "" + +#: libs/ardour/vst_plugin.cc:500 +msgid "You asked ardour to not use any VST plugins" +msgstr "" + +#: libs/ardour/audio_unit.cc:48 +msgid "AudioUnit: Could not convert CAComponent to CAAudioUnit" +msgstr "" diff --git a/libs/ardour/redirect.cc b/libs/ardour/redirect.cc index 09d650b069..adad79e2a3 100644 --- a/libs/ardour/redirect.cc +++ b/libs/ardour/redirect.cc @@ -108,101 +108,77 @@ Redirect::set_placement (const string& str, void *src) } } +/* NODE STRUCTURE + + <Automation [optionally with visible="...." ]> + <parameter-N> + <AutomationList id=N> + <events> + X1 Y1 + X2 Y2 + .... + </events> + </parameter-N> + <Automation> +*/ + int -Redirect::load_automation (string path) -{ - string fullpath; +Redirect::set_automation_state (const XMLNode& node) +{ + Glib::Mutex::Lock lm (_automation_lock); - if (path[0] == '/') { // legacy - fullpath = path; - } else { - fullpath = _session.automation_dir(); - fullpath += path; - } - ifstream in (fullpath.c_str()); + parameter_automation.clear (); - if (!in) { - warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg; - return 1; - } + XMLNodeList nlist = node.children(); + XMLNodeIterator niter; - Glib::Mutex::Lock lm (_automation_lock); - set<uint32_t> tosave; - parameter_automation.clear (); + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + uint32_t param; - while (in) { - double when; - double value; - uint32_t port; + if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) { + error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg; + continue; + } - in >> port; if (!in) break; - in >> when; if (!in) goto bad; - in >> value; if (!in) goto bad; - - AutomationList& al = automation_list (port); - al.add (when, value); - tosave.insert (port); - } - - for (set<uint32_t>::iterator i = tosave.begin(); i != tosave.end(); ++i) { - automation_list (*i).save_state (_("loaded from disk")); + AutomationList& al = automation_list (param); + if (al.set_state (*(*niter)->children().front())) { + goto bad; + } } - + return 0; bad: - error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg; + error << string_compose(_("%1: cannot load automation data from XML"), _name) << endmsg; parameter_automation.clear (); return -1; } -int -Redirect::save_automation (string path) +XMLNode& +Redirect::get_automation_state () { Glib::Mutex::Lock lm (_automation_lock); + XMLNode* node = new XMLNode (X_("Automation")); string fullpath; if (parameter_automation.empty()) { - return 1; - } - - fullpath = _session.automation_dir(); - fullpath += path; - - ofstream out (fullpath.c_str()); - - if (!out) { - error << string_compose(_("%1: cannot open %2 to store automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg; - return -1; + return *node; } - AutomationList::const_iterator i; map<uint32_t,AutomationList*>::iterator li; for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { - for (i = (*li).second->begin(); i != (*li).second->end(); ++i) { - - out << (*li).first << ' ' << (*i)->when << ' ' << (*i)->value << endl; - - if (!out) { - break; - } - } + + XMLNode* child; - if (i != (*li).second->end()) { - unlink (fullpath.c_str()); - error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg; - return -1; - } - } - - if (li != parameter_automation.end()) { - unlink (fullpath.c_str()); - error << string_compose(_("%1: could not save automation state to %2"), _name, fullpath) << endmsg; - return -1; + char buf[64]; + stringstream str; + snprintf (buf, sizeof (buf), "parameter-%" PRIu32, li->first); + child = new XMLNode (buf); + child->add_child_nocopy (li->second->get_state ()); } - return 0; + return *node; } XMLNode& @@ -214,7 +190,6 @@ Redirect::get_state (void) XMLNode& Redirect::state (bool full_state) { - char buf[64]; XMLNode* node = new XMLNode (state_node_name); stringstream sstr; @@ -228,65 +203,24 @@ Redirect::state (bool full_state) if (full_state) { - string path; - string legal_name; + XMLNode& automation = get_automation_state(); - path = _session.snap_name(); - path += "-redirect-"; - id().print (buf, sizeof (buf)); - path += buf; - path += ".automation"; - - /* XXX we didn't ask for a state save, we asked for the current state. - FIX ME! - */ - - switch (save_automation (path)) { - case -1: - error << string_compose(_("Could not get state from Redirect (%1). Problem with save_automation"), _name) << endmsg; - break; - - case 0: - XMLNode *aevents = node->add_child("Automation"); - - for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) { - if (x != visible_parameter_automation.begin()) { - sstr << ' '; - } - sstr << *x; + for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) { + if (x != visible_parameter_automation.begin()) { + sstr << ' '; } - - aevents->add_property ("path", path); - aevents->add_property ("visible", sstr.str()); - break; + sstr << *x; } - } - return *node; -} + automation.add_property ("visible", sstr.str()); -void -Redirect::what_has_automation (set<uint32_t>& s) const -{ - Glib::Mutex::Lock lm (_automation_lock); - map<uint32_t,AutomationList*>::const_iterator li; - - for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { - s.insert ((*li).first); + node->add_child_nocopy (automation); } -} -void -Redirect::what_has_visible_automation (set<uint32_t>& s) const -{ - Glib::Mutex::Lock lm (_automation_lock); - set<uint32_t>::const_iterator li; - - for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) { - s.insert (*li); - } + return *node; } + int Redirect::set_state (const XMLNode& node) { @@ -308,14 +242,15 @@ Redirect::set_state (const XMLNode& node) IO::set_state (**niter); have_io = true; - } else if ((*niter)->name() == "Automation") { + } else if ((*niter)->name() == X_("Automation")) { + XMLProperty *prop; if ((prop = (*niter)->property ("path")) != 0) { - load_automation (prop->value()); + old_set_automation_state (*(*niter)); } else { - warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg; + set_automation_state (*(*niter)); } if ((prop = (*niter)->property ("visible")) != 0) { @@ -364,6 +299,102 @@ Redirect::set_state (const XMLNode& node) return 0; } +int +Redirect::old_set_automation_state (const XMLNode& node) +{ + const XMLProperty *prop; + + if ((prop = node.property ("path")) != 0) { + load_automation (prop->value()); + } else { + warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg; + } + + if ((prop = node.property ("visible")) != 0) { + uint32_t what; + stringstream sstr; + + visible_parameter_automation.clear (); + + sstr << prop->value(); + while (1) { + sstr >> what; + if (sstr.fail()) { + break; + } + mark_automation_visible (what, true); + } + } + + return 0; +} + +int +Redirect::load_automation (string path) +{ + string fullpath; + + if (path[0] == '/') { // legacy + fullpath = path; + } else { + fullpath = _session.automation_dir(); + fullpath += path; + } + ifstream in (fullpath.c_str()); + + if (!in) { + warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg; + return 1; + } + + Glib::Mutex::Lock lm (_automation_lock); + set<uint32_t> tosave; + parameter_automation.clear (); + + while (in) { + double when; + double value; + uint32_t port; + + in >> port; if (!in) break; + in >> when; if (!in) goto bad; + in >> value; if (!in) goto bad; + + AutomationList& al = automation_list (port); + al.add (when, value); + tosave.insert (port); + } + + return 0; + + bad: + error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg; + parameter_automation.clear (); + return -1; +} + + +void +Redirect::what_has_automation (set<uint32_t>& s) const +{ + Glib::Mutex::Lock lm (_automation_lock); + map<uint32_t,AutomationList*>::const_iterator li; + + for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { + s.insert ((*li).first); + } +} + +void +Redirect::what_has_visible_automation (set<uint32_t>& s) const +{ + Glib::Mutex::Lock lm (_automation_lock); + set<uint32_t>::const_iterator li; + + for (li = visible_parameter_automation.begin(); li != visible_parameter_automation.end(); ++li) { + s.insert (*li); + } +} AutomationList& Redirect::automation_list (uint32_t parameter) { @@ -437,34 +468,9 @@ Redirect::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_even } void -Redirect::store_state (RedirectState& state) const -{ - state.active = _active; -} - -Change -Redirect::restore_state (StateManager::State& state) -{ - RedirectState* rstate = dynamic_cast<RedirectState*> (&state); - set_active (rstate->active, this); - return Change (0); -} - -StateManager::State* -Redirect::state_factory (std::string why) const -{ - RedirectState* state = new RedirectState (why); - - store_state (*state); - - return state; -} - -void Redirect::set_active (bool yn, void* src) { _active = yn; - save_state (_("active_changed")); active_changed (this, src); _session.set_dirty (); } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 3853559e10..6d8c71b563 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -265,13 +265,43 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node) Region::~Region () { - /* derived classes must call notify_callbacks() and then emit GoingAway */ + if (_playlist) { + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + (*i)->remove_playlist (_playlist); + } + } + + notify_callbacks (); + GoingAway (); /* EMIT SIGNAL */ } void Region::set_playlist (Playlist* pl) { - _playlist = pl; + if (pl == _playlist) { + return; + } + + Playlist* old_playlist = _playlist; + + if (pl) { + if (old_playlist) { + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + (*i)->remove_playlist (old_playlist); + (*i)->add_playlist (_playlist); + } + } else { + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + (*i)->add_playlist (_playlist); + } + } + } else { + if (old_playlist) { + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + (*i)->remove_playlist (old_playlist); + } + } + } } void @@ -337,6 +367,24 @@ Region::first_edit () } } +bool +Region::at_natural_position () const +{ + if (!_playlist) { + return false; + } + + boost::shared_ptr<Region> whole_file_region = get_parent(); + + if (whole_file_region) { + if (_position == whole_file_region->position() + _start) { + return true; + } + } + + return false; +} + void Region::move_to_natural_position (void *src) { @@ -1180,14 +1228,17 @@ Region::verify_start_mutable (jack_nframes_t& new_start) } boost::shared_ptr<Region> -Region::get_parent() +Region::get_parent() const { - boost::shared_ptr<Region> r; - if (_playlist) { - r = _playlist->session().find_whole_file_parent (*this); + boost::shared_ptr<Region> r; + boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this()); + + if (grrr2 && (r = _playlist->session().find_whole_file_parent (grrr2))) { + return boost::static_pointer_cast<Region> (r); + } } - return r; + return boost::shared_ptr<Region>(); } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 308dbb57cb..5314c99632 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -63,13 +63,13 @@ Route::Route (Session& sess, string name, int input_min, int input_max, int outp init (); } -Route::Route (Session& sess, const XMLNode& node) - : IO (sess, "route"), +Route::Route (Session& sess, const XMLNode& node, DataType default_type) + : IO (sess, *node.child ("IO"), default_type), _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) { init (); - set_state (node); + _set_state (node, false); } void @@ -1313,7 +1313,6 @@ XMLNode& Route::state(bool full_state) { XMLNode *node = new XMLNode("Route"); - XMLNode *aevents; RedirectList:: iterator i; char buf[32]; @@ -1374,26 +1373,6 @@ Route::state(bool full_state) cmt->add_content (_comment); } - if (full_state) { - string path; - - path = _session.snap_name(); - path += "-gain-"; - path += legalize_for_path (_name); - path += ".automation"; - - /* XXX we didn't ask for a state save, we asked for the current state. - FIX ME! - */ - - if (save_automation (path)) { - error << _("Could not get state of route. Problem with save_automation") << endmsg; - } - - aevents = node->add_child ("Automation"); - aevents->add_property ("path", path); - } - for (i = _redirects.begin(); i != _redirects.end(); ++i) { node->add_child_nocopy((*i)->state (full_state)); } @@ -1481,6 +1460,12 @@ Route::add_redirect_from_xml (const XMLNode& node) int Route::set_state (const XMLNode& node) { + return _set_state (node, true); +} + +int +Route::_set_state (const XMLNode& node, bool call_base) +{ XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; @@ -1492,7 +1477,7 @@ Route::set_state (const XMLNode& node) return -1; } - if ((prop = node.property ("flags")) != 0) { + if ((prop = node.property (X_("flags"))) != 0) { int x; sscanf (prop->value().c_str(), "0x%x", &x); _flags = Flag (x); @@ -1500,20 +1485,20 @@ Route::set_state (const XMLNode& node) _flags = Flag (0); } - if ((prop = node.property ("default-type")) != 0) { + if ((prop = node.property (X_("default-type"))) != 0) { _default_type = DataType(prop->value()); assert(_default_type != DataType::NIL); } - if ((prop = node.property ("phase-invert")) != 0) { + if ((prop = node.property (X_("phase-invert"))) != 0) { set_phase_invert(prop->value()=="yes"?true:false, this); } - if ((prop = node.property ("active")) != 0) { + if ((prop = node.property (X_("active"))) != 0) { set_active (prop->value() == "yes"); } - if ((prop = node.property ("muted")) != 0) { + if ((prop = node.property (X_("muted"))) != 0) { bool yn = prop->value()=="yes"?true:false; /* force reset of mute status */ @@ -1523,7 +1508,7 @@ Route::set_state (const XMLNode& node) mute_gain = desired_mute_gain; } - if ((prop = node.property ("soloed")) != 0) { + if ((prop = node.property (X_("soloed"))) != 0) { bool yn = prop->value()=="yes"?true:false; /* force reset of solo status */ @@ -1533,23 +1518,23 @@ Route::set_state (const XMLNode& node) solo_gain = desired_solo_gain; } - if ((prop = node.property ("mute-affects-pre-fader")) != 0) { + if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) { _mute_affects_pre_fader = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-post-fader")) != 0) { + if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) { _mute_affects_post_fader = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-control-outs")) != 0) { + if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { _mute_affects_control_outs = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-main-outs")) != 0) { + if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { _mute_affects_main_outs = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("edit-group")) != 0) { + if ((prop = node.property (X_("edit-group"))) != 0) { RouteGroup* edit_group = _session.edit_group_by_name(prop->value()); if(edit_group == 0) { error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; @@ -1558,7 +1543,7 @@ Route::set_state (const XMLNode& node) } } - if ((prop = node.property ("order-keys")) != 0) { + if ((prop = node.property (X_("order-keys"))) != 0) { long n; @@ -1595,7 +1580,7 @@ Route::set_state (const XMLNode& node) delete deferred_state; } - deferred_state = new XMLNode("deferred state"); + deferred_state = new XMLNode(X_("deferred state")); /* set parent class properties before anything else */ @@ -1603,7 +1588,7 @@ Route::set_state (const XMLNode& node) child = *niter; - if (child->name() == IO::state_node_name) { + if (child->name() == IO::state_node_name && call_base) { IO::set_state (*child); break; @@ -1614,7 +1599,7 @@ Route::set_state (const XMLNode& node) child = *niter; - if (child->name() == "Send") { + if (child->name() == X_("Send")) { if (!IO::ports_legal) { @@ -1625,7 +1610,7 @@ Route::set_state (const XMLNode& node) add_redirect_from_xml (*child); } - } else if (child->name() == "Insert") { + } else if (child->name() == X_("Insert")) { if (!IO::ports_legal) { @@ -1636,21 +1621,13 @@ Route::set_state (const XMLNode& node) add_redirect_from_xml (*child); } - } else if (child->name() == "Automation") { - - XMLPropertyList plist; - XMLPropertyConstIterator piter; - XMLProperty *prop; + } else if (child->name() == X_("Automation")) { - plist = child->properties(); - for (piter = plist.begin(); piter != plist.end(); ++piter) { - prop = *piter; - if (prop->name() == "path") { - load_automation (prop->value()); - } + if ((prop = child->property (X_("path"))) != 0) { + load_automation (prop->value()); } - } else if (child->name() == "ControlOuts") { + } else if (child->name() == X_("ControlOuts")) { string coutname = _name; coutname += _("[control]"); @@ -1658,25 +1635,25 @@ Route::set_state (const XMLNode& node) _control_outs = new IO (_session, coutname); _control_outs->set_state (**(child->children().begin())); - } else if (child->name() == "Comment") { + } else if (child->name() == X_("Comment")) { /* XXX this is a terrible API design in libxml++ */ XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == "extra") { + } else if (child->name() == X_("extra")) { _extra_xml = new XMLNode (*child); - } else if (child->name() == "solo") { + } else if (child->name() == X_("solo")) { _solo_control.set_state (*child); _session.add_controllable (&_solo_control); - } else if (child->name() == "mute") { + } else if (child->name() == X_("mute")) { _mute_control.set_state (*child); _session.add_controllable (&_mute_control); } } - if ((prop = node.property ("mix-group")) != 0) { + if ((prop = node.property (X_("mix-group"))) != 0) { RouteGroup* mix_group = _session.mix_group_by_name(prop->value()); if (mix_group == 0) { error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; @@ -1936,6 +1913,10 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f { Glib::RWLock::ReaderLock lm (redirect_lock); + if (!did_locate) { + automation_snapshot (now); + } + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { if (Config->get_plugins_stop_with_transport() && can_flush_redirects) { @@ -1952,19 +1933,6 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f _roll_delay = _initial_delay; } -UndoAction -Route::get_memento() const -{ - void (Route::*pmf)(state_id_t) = &Route::set_state; - return sigc::bind (mem_fun (*(const_cast<Route *>(this)), pmf), _current_state_id); -} - -void -Route::set_state (state_id_t id) -{ - return; -} - void Route::input_change_handler (IOChange change, void *ignored) { @@ -2048,6 +2016,15 @@ int Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, bool can_record, bool rec_monitors_input) { + { + Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + if (lm.locked()) { + // automation snapshot can also be called from the non-rt context + // and it uses the redirect list, so we take the lock out here + automation_snapshot (_session.transport_frame()); + } + } + if ((n_outputs().get_total() == 0 && _redirects.empty()) || n_inputs().get_total() == 0 || !_active) { silence (nframes, offset); return 0; @@ -2181,6 +2158,16 @@ Route::set_latency_delay (nframes_t longest_session_latency) } } +void +Route::automation_snapshot (nframes_t now) +{ + IO::automation_snapshot (now); + + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + (*i)->automation_snapshot (now); + } +} + Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp) : Controllable (name), route (s), type(tp) { diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index 2c2a152416..73dbf11ad5 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -37,7 +37,6 @@ Send::Send (Session& s, Placement p) : Redirect (s, s.next_send_name(), p) { _metering = false; - save_state (_("initial state")); RedirectCreated (this); /* EMIT SIGNAL */ } @@ -50,7 +49,6 @@ Send::Send (Session& s, const XMLNode& node) throw failed_constructor(); } - save_state (_("initial state")); RedirectCreated (this); /* EMIT SIGNAL */ } @@ -58,7 +56,6 @@ Send::Send (const Send& other) : Redirect (other._session, other._session.next_send_name(), other.placement()) { _metering = false; - save_state (_("initial state")); RedirectCreated (this); /* EMIT SIGNAL */ } @@ -77,7 +74,7 @@ XMLNode& Send::state(bool full) { XMLNode *node = new XMLNode("Send"); - node->add_child_nocopy (Redirect::state(full)); + node->add_child_nocopy (Redirect::state (full)); return *node; } @@ -86,11 +83,15 @@ Send::set_state(const XMLNode& node) { XMLNodeList nlist = node.children(); XMLNodeIterator niter; - + + /* Send has regular IO automation (gain, pan) */ + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((*niter)->name() == Redirect::state_node_name) { Redirect::set_state (**niter); break; + } else if ((*niter)->name() == X_("Automation")) { + IO::set_automation_state (*(*niter)); } } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 11cb658008..1b7c3be6dd 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -288,7 +288,7 @@ Session::Session (AudioEngine &eng, new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); if (new_session) { - if (create (new_session, mix_template, _engine.frame_rate() * 60 * 5)) { + if (create (new_session, mix_template, compute_initial_length())) { cerr << "create failed\n"; throw failed_constructor (); } @@ -342,12 +342,21 @@ Session::Session (AudioEngine &eng, cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; - n_physical_outputs = max (requested_physical_out, _engine.n_physical_outputs()); - n_physical_inputs = max (requested_physical_in, _engine.n_physical_inputs()); + n_physical_outputs = _engine.n_physical_outputs(); + n_physical_inputs = _engine.n_physical_inputs(); + + if (n_physical_inputs) { + n_physical_inputs = max (requested_physical_in, n_physical_inputs); + } + + if (n_physical_outputs) { + n_physical_outputs = max (requested_physical_out, n_physical_outputs); + } first_stage_init (fullpath, snapshot_name); new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + if (new_session) { if (create (new_session, 0, initial_length)) { throw failed_constructor (); @@ -410,7 +419,7 @@ Session::~Session () /* clear history so that no references to objects are held any more */ - history.clear (); + _history.clear (); /* clear state tree so that no references to objects are held any more */ @@ -1331,8 +1340,10 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); + Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25)); + // XXX we need some equivalent to this, somehow - // DestructiveFileSource::setup_standard_crossfades (frames_per_second); + // SndFileSource::setup_standard_crossfades (frames_per_second); set_dirty(); @@ -1824,12 +1835,12 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ << endmsg; } - for (uint32_t x = 0; x < bus->n_inputs().get(DataType::AUDIO); ++x) { + for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().get(DataType::AUDIO); ++x) { port = ""; - + if (Config->get_input_auto_connect() & AutoConnectPhysical) { - port = physinputs[((n+x)%n_physical_inputs)]; + port = physinputs[((n+x)%n_physical_inputs)]; } if (port.length() && bus->connect_input (bus->input (x), port, this)) { @@ -1837,7 +1848,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } } - for (uint32_t x = 0; x < bus->n_outputs().get(DataType::AUDIO); ++x) { + for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().get(DataType::AUDIO); ++x) { port = ""; @@ -1950,8 +1961,9 @@ Session::remove_route (shared_ptr<Route> route) { RCUWriter<RouteList> writer (routes); shared_ptr<RouteList> rs = writer.get_copy (); - rs->remove (route); + rs->remove (route); + /* deleting the master out seems like a dumb idea, but its more of a UI policy issue than our concern. @@ -2564,7 +2576,7 @@ Session::remove_region (boost::weak_ptr<Region> weak_region) } boost::shared_ptr<Region> -Session::find_whole_file_parent (Region& child) +Session::find_whole_file_parent (boost::shared_ptr<Region const> child) { RegionList::iterator i; boost::shared_ptr<Region> region; @@ -2577,13 +2589,13 @@ Session::find_whole_file_parent (Region& child) if (region->whole_file()) { - if (child.source_equivalent (region)) { + if (child->source_equivalent (region)) { return region; } } } - return boost::shared_ptr<AudioRegion> (); + return boost::shared_ptr<Region> (); } void @@ -2596,32 +2608,38 @@ Session::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vec int Session::destroy_region (boost::shared_ptr<Region> region) { - boost::shared_ptr<AudioRegion> aregion; - - if ((aregion = boost::dynamic_pointer_cast<AudioRegion> (region)) == 0) { - return 0; - } - - if (aregion->playlist()) { - aregion->playlist()->destroy_region (region); - } - vector<boost::shared_ptr<Source> > srcs; - - for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (aregion->source (n)); + + { + boost::shared_ptr<AudioRegion> aregion; + + if ((aregion = boost::dynamic_pointer_cast<AudioRegion> (region)) == 0) { + return 0; + } + + if (aregion->playlist()) { + aregion->playlist()->destroy_region (region); + } + + for (uint32_t n = 0; n < aregion->n_channels(); ++n) { + srcs.push_back (aregion->source (n)); + } } + region->drop_references (); + for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - - if ((*i).use_count() == 1) { - boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*i); + if (!(*i)->used()) { + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*i); + if (afs) { (afs)->mark_for_remove (); } (*i)->drop_references (); + + cerr << "source was not used by any playlist\n"; } } @@ -3198,6 +3216,20 @@ Session::add_playlist (Playlist* playlist) } void +Session::get_playlists (vector<Playlist*>& s) +{ + { + Glib::Mutex::Lock lm (playlist_lock); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + s.push_back (*i); + } + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + s.push_back (*i); + } + } +} + +void Session::track_playlist (Playlist* pl, bool inuse) { PlaylistList::iterator x; @@ -3970,13 +4002,14 @@ Session::nbusses () const } void -Session::add_curve(Curve *curve) +Session::add_automation_list(AutomationList *al) { - curves[curve->id()] = curve; + automation_lists[al->id()] = al; } -void -Session::add_automation_list(AutomationList *al) +nframes_t +Session::compute_initial_length () { - automation_lists[al->id()] = al; + return _engine.frame_rate() * 60 * 5; } + diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc index 5fd6d70983..f09c7232d7 100644 --- a/libs/ardour/session_click.cc +++ b/libs/ardour/session_click.cc @@ -84,7 +84,7 @@ Session::click (nframes_t start, nframes_t nframes, nframes_t offset) break; } } - + run_clicks: memset (buf, 0, sizeof (Sample) * nframes); diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index d71ba34fc7..5816f1c6b7 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -3,9 +3,13 @@ #include <pbd/memento_command.h> #include <ardour/diskstream.h> #include <ardour/playlist.h> +#include <ardour/audioplaylist.h> +#include <ardour/audio_track.h> #include <ardour/tempo.h> #include <ardour/audiosource.h> #include <ardour/audioregion.h> +#include <ardour/midi_source.h> +#include <ardour/midi_region.h> #include <pbd/error.h> using namespace PBD; #include "i18n.h" @@ -13,7 +17,7 @@ using namespace PBD; namespace ARDOUR { -void Session::register_with_memento_command_factory(PBD::ID id, StatefulDestructible *ptr) +void Session::register_with_memento_command_factory(PBD::ID id, PBD::StatefulThingWithGoingAway *ptr) { registry[id] = ptr; } @@ -49,41 +53,37 @@ Command *Session::memento_command_factory(XMLNode *n) { error << _("Tried to reconstitute a MementoCommand with no contents, failing. id=") << id.to_s() << endmsg; return 0; - } - - - /* create command */ - string obj_T = n->children().front()->name(); - if (obj_T == "AudioRegion" || obj_T == "MidiRegion" || obj_T == "Region") { + } + + /* create command */ + string obj_T = n->property ("type_name")->value(); + if (obj_T == typeid (AudioRegion).name() || obj_T == typeid (MidiRegion).name() || obj_T == typeid (Region).name()) { if (regions.count(id)) return new MementoCommand<Region>(*regions[id], before, after); - } else if (obj_T == "AudioSource" || obj_T == "MidiSource") { + } else if (obj_T == typeid (AudioSource).name() || obj_T == typeid (MidiSource).name()) { if (sources.count(id)) return new MementoCommand<Source>(*sources[id], before, after); - } else if (obj_T == "Location") { + } else if (obj_T == typeid (Location).name()) { return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after); - } else if (obj_T == "Locations") { + } else if (obj_T == typeid (Locations).name()) { return new MementoCommand<Locations>(_locations, before, after); - } else if (obj_T == "TempoMap") { + } else if (obj_T == typeid (TempoMap).name()) { return new MementoCommand<TempoMap>(*_tempo_map, before, after); - } else if (obj_T == "Playlist" || obj_T == "AudioPlaylist") { + } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) { if (Playlist *pl = playlist_by_name(child->property("name")->value())) return new MementoCommand<Playlist>(*pl, before, after); - } else if (obj_T == "Route") { // includes AudioTrack + } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) { return new MementoCommand<Route>(*route_by_id(id), before, after); - } else if (obj_T == "Curve") { - if (curves.count(id)) - return new MementoCommand<Curve>(*curves[id], before, after); - } else if (obj_T == "AutomationList") { + } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) { if (automation_lists.count(id)) return new MementoCommand<AutomationList>(*automation_lists[id], before, after); } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here - return new MementoCommand<StatefulDestructible>(*registry[id], before, after); + return new MementoCommand<PBD::StatefulThingWithGoingAway>(*registry[id], before, after); } /* we failed */ - error << _("could not reconstitute MementoCommand from XMLNode. id=") << id.to_s() << endmsg; - return 0; + error << string_compose (_("could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"), obj_T, id.to_s()) << endmsg; + return 0 ; } // solo diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index ff79a47e6b..bcc9b730ba 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -195,7 +195,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) /* default short fade = 15ms */ Crossfade::set_short_xfade_length ((nframes_t) floor (Config->get_short_xfade_seconds() * frame_rate())); - DestructiveFileSource::setup_standard_crossfades (frame_rate()); + SndFileSource::setup_standard_crossfades (frame_rate()); last_mmc_step.tv_sec = 0; last_mmc_step.tv_usec = 0; @@ -250,10 +250,9 @@ Session::first_stage_init (string fullpath, string snapshot_name) Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); - Curve::CurveCreated.connect (mem_fun (*this, &Session::add_curve)); AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); - Controllable::GoingAway.connect (mem_fun (*this, &Session::remove_controllable)); + Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable)); IO::MoreChannels.connect (mem_fun (*this, &Session::ensure_buffers)); @@ -533,7 +532,6 @@ Session::create (bool& new_session, string* mix_template, nframes_t initial_leng _state_of_the_state = Clean; if (save_state (_current_snapshot_name)) { - save_history (_current_snapshot_name); return -1; } @@ -858,13 +856,15 @@ Session::state(bool full_state) boost::shared_ptr<AudioFileSource> fs; if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) { - boost::shared_ptr<DestructiveFileSource> dfs = boost::dynamic_pointer_cast<DestructiveFileSource> (fs); /* Don't save sources that are empty, unless they're destructive (which are OK if they are empty, because we will re-use them every time.) */ - if ( ! dfs && siter->second->length() == 0) { - continue; + + if (!fs->destructive()) { + if (fs->length() == 0) { + continue; + } } } @@ -898,7 +898,20 @@ Session::state(bool full_state) } } - node->add_child_nocopy (_locations.get_state()); + if (full_state) { + node->add_child_nocopy (_locations.get_state()); + } else { + // for a template, just create a new Locations, populate it + // with the default start and end, and get the state for that. + Locations loc; + Location* start = new Location(0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart))); + Location* end = new Location(0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd))); + start->set_end(0); + loc.add (start); + end->set_end(compute_initial_length()); + loc.add (end); + node->add_child_nocopy (loc.get_state()); + } child = node->add_child ("Connections"); { @@ -1026,8 +1039,6 @@ Session::set_state (const XMLNode& node) return -1; } - StateManager::prohibit_save (); - if ((prop = node.property ("name")) != 0) { _name = prop->value (); } @@ -1058,11 +1069,11 @@ Session::set_state (const XMLNode& node) Path extra Options/Config + Locations Sources AudioRegions AudioDiskstreams Connections - Locations Routes EditGroups MixGroups @@ -1085,6 +1096,39 @@ Session::set_state (const XMLNode& node) error << _("Session: XML state has no options section") << endmsg; } + if ((child = find_named_node (node, "Locations")) == 0) { + error << _("Session: XML state has no locations section") << endmsg; + goto out; + } else if (_locations.set_state (*child)) { + goto out; + } + + Location* location; + + if ((location = _locations.auto_loop_location()) != 0) { + set_auto_loop_location (location); + } + + if ((location = _locations.auto_punch_location()) != 0) { + set_auto_punch_location (location); + } + + if ((location = _locations.end_location()) == 0) { + _locations.add (end_location); + } else { + delete end_location; + end_location = location; + } + + if ((location = _locations.start_location()) == 0) { + _locations.add (start_location); + } else { + delete start_location; + start_location = location; + } + + AudioFileSource::set_header_position_offset (start_location->start()); + if ((child = find_named_node (node, "Sources")) == 0) { error << _("Session: XML state has no sources section") << endmsg; goto out; @@ -1132,39 +1176,6 @@ Session::set_state (const XMLNode& node) goto out; } - if ((child = find_named_node (node, "Locations")) == 0) { - error << _("Session: XML state has no locations section") << endmsg; - goto out; - } else if (_locations.set_state (*child)) { - goto out; - } - - Location* location; - - if ((location = _locations.auto_loop_location()) != 0) { - set_auto_loop_location (location); - } - - if ((location = _locations.auto_punch_location()) != 0) { - set_auto_punch_location (location); - } - - if ((location = _locations.end_location()) == 0) { - _locations.add (end_location); - } else { - delete end_location; - end_location = location; - } - - if ((location = _locations.start_location()) == 0) { - _locations.add (start_location); - } else { - delete start_location; - start_location = location; - } - - _locations.save_state (_("initial state")); - if ((child = find_named_node (node, "EditGroups")) == 0) { error << _("Session: XML state has no edit groups section") << endmsg; goto out; @@ -1209,8 +1220,6 @@ Session::set_state (const XMLNode& node) _state_of_the_state = Clean; - StateManager::allow_save (_("initial state"), true); - if (state_was_pending) { save_state (_current_snapshot_name); remove_pending_capture_state (); @@ -1220,8 +1229,6 @@ Session::set_state (const XMLNode& node) return 0; out: - /* we failed, re-enable state saving but don't actually save internal state */ - StateManager::allow_save (X_("ignored"), false); return ret; } @@ -2210,7 +2217,7 @@ Session::commit_reversible_command (Command *cmd) gettimeofday (&now, 0); current_trans->set_timestamp (now); - history.add (current_trans); + _history.add (current_trans); } Session::GlobalRouteBooleanState @@ -2568,6 +2575,8 @@ Session::cleanup_sources (Session::cleanup_report& rep) capture files. */ + cerr << "checking out source " << i->second->name() << " use_count = " << i->second.use_count() << endl; + if (i->second.use_count() == 1 && i->second->length() > 0) { dead_sources.push_back (i->second); @@ -2757,7 +2766,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) /* dump the history list */ - history.clear (); + _history.clear (); /* save state so we don't end up a session file referring to non-existent sources. @@ -2908,7 +2917,7 @@ Session::save_history (string snapshot_name) string xml_path; string bak_path; - tree.set_root (&history.get_state()); + tree.set_root (&_history.get_state()); if (snapshot_name.empty()) { snapshot_name = _current_snapshot_name; @@ -2935,14 +2944,13 @@ Session::save_history (string snapshot_name) * possible to fix. */ - if (unlink (xml_path.c_str())) - { - error << string_compose (_("could not remove corrupt history file %1"), xml_path) << endmsg; + if (unlink (xml_path.c_str())) { + error << string_compose (_("could not remove corrupt history file %1"), xml_path) << endmsg; } else { - if (rename (bak_path.c_str(), xml_path.c_str())) - { - error << string_compose (_("could not restore history file from backup %1"), bak_path) << endmsg; - } + if (rename (bak_path.c_str(), xml_path.c_str())) + { + error << string_compose (_("could not restore history file from backup %1"), bak_path) << endmsg; + } } return -1; @@ -2972,7 +2980,7 @@ Session::restore_history (string snapshot_name) } /* replace history */ - history.clear(); + _history.clear(); for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) { @@ -3005,7 +3013,7 @@ Session::restore_history (string snapshot_name) } } - history.add (ut); + _history.add (ut); } return 0; @@ -3137,6 +3145,10 @@ Session::config_changed (const char* parameter_name) if (_mtc_port != 0) { session_send_mtc = Config->get_send_mtc(); + if (session_send_mtc) { + /* mark us ready to send */ + next_quarter_frame_to_send = 0; + } } } else if (PARAM_IS ("send-mmc")) { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 004261fe8e..e9c4e3785f 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -125,7 +125,7 @@ Session::request_play_loop (bool yn) if (!yn && Config->get_seamless_loop() && transport_rolling()) { // request an immediate locate to refresh the diskstreams // after disabling looping - request_locate (_transport_frame-1, true); + request_locate (_transport_frame-1, false); } } @@ -377,9 +377,6 @@ Session::non_realtime_stop (bool abort) } } } - - //FIXME - //deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame); #ifdef LEAVE_TRANSPORT_UNADJUSTED } diff --git a/libs/ardour/sndfile_helpers.cc b/libs/ardour/sndfile_helpers.cc index b308a74c36..4cf644e0e0 100644 --- a/libs/ardour/sndfile_helpers.cc +++ b/libs/ardour/sndfile_helpers.cc @@ -106,11 +106,11 @@ sndfile_endian_format_from_string (string str) string sndfile_file_ending_from_string (string str) -{ +{ static vector<string> file_endings; if (file_endings.empty()) { - file_endings = PBD::internationalize((const char **) sndfile_file_endings_strings); + file_endings = I18N((const char **) sndfile_file_endings_strings); } for (int n = 0; sndfile_header_formats_strings[n]; ++n) { diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index fcc6a33d81..a30bfcf49b 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -28,6 +28,8 @@ #include <glibmm/miscutils.h> #include <ardour/sndfilesource.h> +#include <ardour/sndfile_helpers.h> +#include <ardour/utils.h> #include "i18n.h" @@ -35,6 +37,14 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +gain_t* SndFileSource::out_coefficient = 0; +gain_t* SndFileSource::in_coefficient = 0; +nframes_t SndFileSource::xfade_frames = 64; +const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSource::Flag (AudioFileSource::Writable| + AudioFileSource::Removable| + AudioFileSource::RemovableIfEmpty| + AudioFileSource::CanRename); + SndFileSource::SndFileSource (Session& s, const XMLNode& node) : AudioFileSource (s, node) { @@ -164,22 +174,32 @@ SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, Heade } void -SndFileSource::init (const string& idstr) +SndFileSource::init (string idstr) { string::size_type pos; string file; + // lets try to keep the object initalizations here at the top + xfade_buf = 0; interleave_buf = 0; interleave_bufsize = 0; sf = 0; _broadcast_info = 0; + string tmp_name; + if ((pos = idstr.find_last_of (':')) == string::npos) { channel = 0; - _name = Glib::path_get_basename (idstr); + tmp_name = idstr; } else { channel = atoi (idstr.substr (pos+1).c_str()); - _name = Glib::path_get_basename (idstr.substr (0, pos)); + tmp_name = idstr.substr (0, pos); + } + + if (is_embedded()) { + _name = tmp_name; + } else { + _name = Glib::path_get_basename (tmp_name); } /* although libsndfile says we don't need to set this, @@ -187,6 +207,17 @@ SndFileSource::init (const string& idstr) */ memset (&_info, 0, sizeof(_info)); + + _capture_start = false; + _capture_end = false; + file_pos = 0; + + if (destructive()) { + xfade_buf = new Sample[xfade_frames]; + timeline_position = header_position_offset; + } + + AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change)); } int @@ -212,27 +243,14 @@ SndFileSource::open () _broadcast_info = new SF_BROADCAST_INFO; memset (_broadcast_info, 0, sizeof (*_broadcast_info)); - /* lookup broadcast info */ - - if (sf_command (sf, SFC_GET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) { - - /* if the file has data but no broadcast info, then clearly, there is no broadcast info */ - - if (_length) { - delete _broadcast_info; - _broadcast_info = 0; - _flags = Flag (_flags & ~Broadcast); - } - - set_timeline_position (header_position_offset); + bool timecode_info_exists; - } else { - - /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits - of the time reference. - */ + set_timeline_position (get_timecode_info (sf, _broadcast_info, timecode_info_exists)); - set_timeline_position ( _broadcast_info->time_reference_low ); + if (!timecode_info_exists) { + delete _broadcast_info; + _broadcast_info = 0; + _flags = Flag (_flags & ~Broadcast); } if (writable()) { @@ -266,6 +284,10 @@ SndFileSource::~SndFileSource () if (_broadcast_info) { delete _broadcast_info; } + + if (xfade_buf) { + delete [] xfade_buf; + } } float @@ -352,6 +374,16 @@ SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const nframes_t SndFileSource::write_unlocked (Sample *data, nframes_t cnt) { + if (destructive()) { + return destructive_write_unlocked (data, cnt); + } else { + return nondestructive_write_unlocked (data, cnt); + } +} + +nframes_t +SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) +{ if (!writable()) { return 0; } @@ -403,6 +435,117 @@ SndFileSource::write_unlocked (Sample *data, nframes_t cnt) return cnt; } +nframes_t +SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) +{ + nframes_t old_file_pos; + + if (!writable()) { + return 0; + } + + if (_capture_start && _capture_end) { + + /* start and end of capture both occur within the data we are writing, + so do both crossfades. + */ + + _capture_start = false; + _capture_end = false; + + /* move to the correct location place */ + file_pos = capture_start_frame - timeline_position; + + // split cnt in half + nframes_t subcnt = cnt / 2; + nframes_t ofilepos = file_pos; + + // fade in + if (crossfade (data, subcnt, 1) != subcnt) { + return 0; + } + + file_pos += subcnt; + Sample * tmpdata = data + subcnt; + + // fade out + subcnt = cnt - subcnt; + if (crossfade (tmpdata, subcnt, 0) != subcnt) { + return 0; + } + + file_pos = ofilepos; // adjusted below + + } else if (_capture_start) { + + /* start of capture both occur within the data we are writing, + so do the fade in + */ + + _capture_start = false; + _capture_end = false; + + /* move to the correct location place */ + file_pos = capture_start_frame - timeline_position; + + if (crossfade (data, cnt, 1) != cnt) { + return 0; + } + + } else if (_capture_end) { + + /* end of capture both occur within the data we are writing, + so do the fade out + */ + + _capture_start = false; + _capture_end = false; + + if (crossfade (data, cnt, 0) != cnt) { + return 0; + } + + } else { + + /* in the middle of recording */ + + if (write_float (data, file_pos, cnt) != cnt) { + return 0; + } + } + + old_file_pos = file_pos; + update_length (file_pos, cnt); + file_pos += cnt; + + if (_build_peakfiles) { + PeakBuildRecord *pbr = 0; + + if (pending_peak_builds.size()) { + pbr = pending_peak_builds.back(); + } + + if (pbr && pbr->frame + pbr->cnt == old_file_pos) { + + /* the last PBR extended to the start of the current write, + so just extend it again. + */ + + pbr->cnt += cnt; + } else { + pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt)); + } + + _peaks_built = false; + } + + if (_build_peakfiles) { + queue_for_peaks (shared_from_this ()); + } + + return cnt; +} + int SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow) { @@ -499,8 +642,10 @@ SndFileSource::set_header_timeline_position () nframes_t SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt) { - if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) != frame_pos) { - error << string_compose (_("%1: cannot seek to %2"), _path, frame_pos) << endmsg; + if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) { + char errbuf[256]; + sf_error_str (0, errbuf, sizeof (errbuf) - 1); + error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg; return 0; } @@ -516,3 +661,286 @@ SndFileSource::natural_position() const { return timeline_position; } + +bool +SndFileSource::set_destructive (bool yn) +{ + if (yn) { + _flags = Flag (_flags | Destructive); + if (!xfade_buf) { + xfade_buf = new Sample[xfade_frames]; + } + clear_capture_marks (); + timeline_position = header_position_offset; + } else { + _flags = Flag (_flags & ~Destructive); + timeline_position = 0; + /* leave xfade buf alone in case we need it again later */ + } + + return true; +} + +void +SndFileSource::clear_capture_marks () +{ + _capture_start = false; + _capture_end = false; +} + +void +SndFileSource::mark_capture_start (nframes_t pos) +{ + if (destructive()) { + if (pos < timeline_position) { + _capture_start = false; + } else { + _capture_start = true; + capture_start_frame = pos; + } + } +} + +void +SndFileSource::mark_capture_end() +{ + if (destructive()) { + _capture_end = true; + } +} + +nframes_t +SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in) +{ + nframes_t xfade = min (xfade_frames, cnt); + nframes_t nofade = cnt - xfade; + Sample* fade_data = 0; + nframes_t fade_position = 0; // in frames + ssize_t retval; + nframes_t file_cnt; + + if (fade_in) { + fade_position = file_pos; + fade_data = data; + } else { + fade_position = file_pos + nofade; + fade_data = data + nofade; + } + + if (fade_position > _length) { + + /* read starts beyond end of data, just memset to zero */ + + file_cnt = 0; + + } else if (fade_position + xfade > _length) { + + /* read ends beyond end of data, read some, memset the rest */ + + file_cnt = _length - fade_position; + + } else { + + /* read is entirely within data */ + + file_cnt = xfade; + } + + if (file_cnt) { + + if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) { + if (retval >= 0 && errno == EAGAIN) { + /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you. + * short or no data there */ + memset (xfade_buf, 0, xfade * sizeof(Sample)); + } else { + error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg; + return 0; + } + } + } + + if (file_cnt != xfade) { + nframes_t delta = xfade - file_cnt; + memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta); + } + + if (nofade && !fade_in) { + if (write_float (data, file_pos, nofade) != nofade) { + error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg; + return 0; + } + } + + if (xfade == xfade_frames) { + + nframes_t n; + + /* use the standard xfade curve */ + + if (fade_in) { + + /* fade new material in */ + + for (n = 0; n < xfade; ++n) { + xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]); + } + + } else { + + + /* fade new material out */ + + for (n = 0; n < xfade; ++n) { + xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]); + } + } + + } else if (xfade < xfade_frames) { + + gain_t in[xfade]; + gain_t out[xfade]; + + /* short xfade, compute custom curve */ + + compute_equal_power_fades (xfade, in, out); + + for (nframes_t n = 0; n < xfade; ++n) { + xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]); + } + + } else if (xfade) { + + /* long xfade length, has to be computed across several calls */ + + } + + if (xfade) { + if (write_float (xfade_buf, fade_position, xfade) != xfade) { + error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg; + return 0; + } + } + + if (fade_in && nofade) { + if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) { + error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg; + return 0; + } + } + + return cnt; +} + +nframes_t +SndFileSource::last_capture_start_frame () const +{ + if (destructive()) { + return capture_start_frame; + } else { + return 0; + } +} + +void +SndFileSource::handle_header_position_change () +{ + if (destructive()) { + if ( _length != 0 ) { + error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg; + //in the future, pop up a dialog here that allows user to regenerate file with new start offset + } else if (writable()) { + timeline_position = header_position_offset; + set_header_timeline_position (); //this will get flushed if/when the file is recorded to + } + } +} + +void +SndFileSource::setup_standard_crossfades (nframes_t rate) +{ + /* This static method is assumed to have been called by the Session + before any DFS's are created. + */ + + xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate); + + if (out_coefficient) { + delete [] out_coefficient; + } + + if (in_coefficient) { + delete [] in_coefficient; + } + + out_coefficient = new gain_t[xfade_frames]; + in_coefficient = new gain_t[xfade_frames]; + + compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient); +} + +void +SndFileSource::set_timeline_position (int64_t pos) +{ + // destructive track timeline postion does not change + // except at instantion or when header_position_offset + // (session start) changes + + if (!destructive()) { + AudioFileSource::set_timeline_position (pos); + } +} + +int +SndFileSource::get_soundfile_info (string path, SoundFileInfo& info, string& error_msg) +{ + SNDFILE *sf; + SF_INFO sf_info; + SF_BROADCAST_INFO binfo; + bool timecode_exists; + + sf_info.format = 0; // libsndfile says to clear this before sf_open(). + + if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) { + char errbuf[256]; + error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1); + return false; + } + + info.samplerate = sf_info.samplerate; + info.channels = sf_info.channels; + info.length = sf_info.frames; + info.format_name = string_compose("Format: %1, %2", + sndfile_major_format(sf_info.format), + sndfile_minor_format(sf_info.format)); + + memset (&binfo, 0, sizeof (binfo)); + info.timecode = get_timecode_info (sf, &binfo, timecode_exists); + + if (!timecode_exists) { + info.timecode = 0; + } + + sf_close (sf); + + return true; +} + +int64_t +SndFileSource::get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists) +{ + if (sf_command (sf, SFC_GET_BROADCAST_INFO, binfo, sizeof (*binfo)) != SF_TRUE) { + exists = false; + return (header_position_offset); + } + + /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits + of the time reference. + */ + + exists = true; + int64_t ret = (uint32_t) binfo->time_reference_high; + ret <<= 32; + ret |= (uint32_t) binfo->time_reference_low; + return ret; +} diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 86ca0c55f2..8f0afd3507 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -34,6 +34,7 @@ #include <pbd/pthread_utils.h> #include <ardour/source.h> +#include <ardour/playlist.h> #include "i18n.h" @@ -51,6 +52,7 @@ Source::Source (Session& s, string name, DataType type) _name = name; _timestamp = 0; _length = 0; + _in_use = 0; } Source::Source (Session& s, const XMLNode& node) @@ -59,6 +61,7 @@ Source::Source (Session& s, const XMLNode& node) { _timestamp = 0; _length = 0; + _in_use = 0; if (set_state (node) || _type == DataType::NIL) { throw failed_constructor(); @@ -127,3 +130,24 @@ Source::update_length (jack_nframes_t pos, jack_nframes_t cnt) } } +void +Source::add_playlist (Playlist* pl) +{ + _playlists.insert (pl); +} + +void +Source::remove_playlist (Playlist* pl) +{ + std::set<Playlist*>::iterator x; + + if ((x = _playlists.find (pl)) != _playlists.end()) { + _playlists.erase (x); + } +} + +uint32_t +Source::used () const +{ + return _playlists.size(); +} diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index e9564a6193..001af609dc 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -110,7 +110,7 @@ SourceFactory::create (Session& s, const XMLNode& node) return ret; } - + return boost::shared_ptr<Source>(); } @@ -163,7 +163,6 @@ SourceFactory::create (Session& s, const XMLNode& node) boost::shared_ptr<Source> SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { -<<<<<<< .working if (type == DataType::AUDIO) { if (flags & Destructive) { boost::shared_ptr<Source> ret (new DestructiveFileSource (s, idstr, flags)); @@ -197,7 +196,7 @@ SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFil return ret; } - + return boost::shared_ptr<Source>(); } @@ -220,9 +219,6 @@ SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFil } else if (type == DataType::MIDI) { boost::shared_ptr<Source> ret (new SMFSource (s, idstr, SMFSource::Flag(0))); // FIXME: flags? - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } if (announce) { SourceCreated (ret); } @@ -239,7 +235,6 @@ boost::shared_ptr<Source> SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce) { /* this might throw failed_constructor(), which is OK */ - if (type == DataType::AUDIO) { if (destructive) { diff --git a/libs/ardour/sse_functions_64bit.s b/libs/ardour/sse_functions_64bit.s index 997852eb5b..0242db3e77 100644 --- a/libs/ardour/sse_functions_64bit.s +++ b/libs/ardour/sse_functions_64bit.s @@ -602,3 +602,8 @@ x86_sse_compute_peak: .size x86_sse_compute_peak, .-x86_sse_compute_peak #; end proc + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif + diff --git a/libs/ardour/state_manager.cc b/libs/ardour/state_manager.cc deleted file mode 100644 index 153773ed30..0000000000 --- a/libs/ardour/state_manager.cc +++ /dev/null @@ -1,91 +0,0 @@ -#include <pbd/error.h> -#include <ardour/state_manager.h> - -#include "i18n.h" - -using namespace ARDOUR; -using namespace std; -using namespace PBD; - -bool StateManager::_allow_save = true; -sigc::signal<void,const char*> StateManager::SaveAllowed; - -StateManager::StateManager () -{ - _current_state_id = 0; -} - -StateManager::~StateManager() -{ -} - -void -StateManager::prohibit_save () -{ - _allow_save = false; -} - -void -StateManager::allow_save (const char* why, bool do_save) -{ - _allow_save = true; - if (do_save) { - SaveAllowed (why); - SaveAllowed.slots().erase (SaveAllowed.slots().begin(), SaveAllowed.slots().end()); - } -} - -void -StateManager::drop_all_states () -{ - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - delete *i; - } - - states.clear (); - - save_state (_("cleared history")); -} - -void -StateManager::use_state (state_id_t id) -{ - Change what_changed; - state_id_t n; - StateMap::iterator i; - - for (n = 0, i = states.begin(); n < id && i != states.end(); ++n, ++i); - - if (n != id || i == states.end()) { - fatal << string_compose (_("programming error: illegal state ID (%1) passed to " - "StateManager::set_state() (range = 0-%2)"), id, states.size()-1) - << endmsg; - /*NOTREACHED*/ - return; - } - - what_changed = restore_state (**i); - _current_state_id = id; - send_state_changed (what_changed); -} - -void -StateManager::save_state (std::string why) -{ - if (!should_save_state()) - return; - - if (!_allow_save) { - SaveAllowed.connect (mem_fun (*this, &StateManager::save_state)); - return; - } - - states.push_back (state_factory (why)); - _current_state_id = states.size() - 1; -} - -void -StateManager::send_state_changed (Change what_changed) -{ - StateChanged (what_changed); -} diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 3cc5420c67..0ff94324bb 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -206,7 +206,6 @@ TempoMap::TempoMap (nframes_t fr) _frame_rate = fr; last_bbt_valid = false; BBT_Time start; - in_set_state = false; start.bars = 1; start.beats = 1; @@ -222,8 +221,6 @@ TempoMap::TempoMap (nframes_t fr) metrics->push_back (t); metrics->push_back (m); - - save_state (_("initial")); } TempoMap::~TempoMap () @@ -256,7 +253,6 @@ TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when) section.set_start (corrected); metrics->sort (cmp); timestamp_metrics (); - save_state (_("move metric")); return 0; } @@ -265,7 +261,7 @@ void TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when) { if (move_metric_section (tempo, when) == 0) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -273,7 +269,7 @@ void TempoMap::move_meter (MeterSection& meter, const BBT_Time& when) { if (move_metric_section (meter, when) == 0) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -301,7 +297,7 @@ TempoMap::remove_tempo (const TempoSection& tempo) } if (removed) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -325,14 +321,10 @@ TempoMap::remove_meter (const MeterSection& tempo) } } } - - if (removed) { - save_state (_("metric removed")); - } } if (removed) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -369,11 +361,9 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) where.ticks = 0; do_insert (new TempoSection (where, tempo.beats_per_minute())); - - save_state (_("add tempo")); } - send_state_changed (Change (0)); + StateChanged (Change (0)); } void @@ -397,14 +387,10 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement) break; } } - - if (replaced) { - save_state (_("replace tempo")); - } } if (replaced) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -431,11 +417,9 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) where.ticks = 0; do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor())); - - save_state (_("add meter")); } - send_state_changed (Change (0)); + StateChanged (Change (0)); } void @@ -458,14 +442,10 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement) break; } } - - if (replaced) { - save_state (_("replaced meter")); - } } if (replaced) { - send_state_changed (Change (0)); + StateChanged (Change (0)); } } @@ -1071,6 +1051,9 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const double beat_frame; double beat_frames; double frames_per_bar; + double delta_bars; + double delta_beats; + double dummy; nframes_t limit; meter = &first_meter (); @@ -1100,6 +1083,10 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const Now start generating points. */ + beats_per_bar = meter->beats_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate); + if (meter->frame() > tempo->frame()) { bar = meter->start().bars; beat = meter->start().beats; @@ -1110,12 +1097,21 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const current = tempo->frame(); } + /* initialize current to point to the bar/beat just prior to the + lower frame bound passed in. assumes that current is initialized + above to be on a beat. + */ + + delta_bars = (lower-current) / frames_per_bar; + delta_beats = modf(delta_bars, &dummy) * beats_per_bar; + current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames); + + // adjust bars and beats too + bar += (uint32_t) (floor(delta_bars)); + beat += (uint32_t) (floor(delta_beats)); + points = new BBTPointList; - beats_per_bar = meter->beats_per_bar (); - frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = tempo->frames_per_beat (_frame_rate); - do { if (i == metrics->end()) { @@ -1197,6 +1193,10 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beat = 1; } + beats_per_bar = meter->beats_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate); + ++i; } @@ -1246,8 +1246,6 @@ TempoMap::set_state (const XMLNode& node) XMLNodeConstIterator niter; Metrics old_metrics (*metrics); - in_set_state = true; - metrics->clear(); nlist = node.children(); @@ -1287,20 +1285,9 @@ TempoMap::set_state (const XMLNode& node) metrics->sort (cmp); timestamp_metrics (); } - - in_set_state = false; } - /* This state needs to be saved. This string will never be a part of the - object's history though, because the allow_save flag is false during - session load. This state will eventually be tagged "initial state", - by a call to StateManager::allow_save from Session::set_state. - - If this state is not saved, there is no way to reach it through undo actions. - */ - save_state(_("load XML data")); - - send_state_changed (Change (0)); + StateChanged (Change (0)); return 0; } @@ -1323,65 +1310,3 @@ TempoMap::dump (std::ostream& o) const } } -UndoAction -TempoMap::get_memento () const -{ - return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id); -} - -Change -TempoMap::restore_state (StateManager::State& state) -{ - Glib::RWLock::ReaderLock lm (lock); - - TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state); - - /* We can't just set the metrics pointer to the address of the metrics list - stored in the state, cause this would ruin this state for restoring in - the future. If they have the same address, they are the same list. - Thus we need to copy all the elements from the state metrics list to the - current metrics list. - */ - metrics->clear(); - for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) { - TempoSection *ts; - MeterSection *ms; - - if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) { - metrics->push_back (new TempoSection (*ts)); - } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) { - metrics->push_back (new MeterSection (*ms)); - } - } - - last_bbt_valid = false; - - return Change (0); -} - -StateManager::State* -TempoMap::state_factory (std::string why) const -{ - TempoMapState* state = new TempoMapState (why); - - for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { - TempoSection *ts; - MeterSection *ms; - - if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) { - state->metrics->push_back (new TempoSection (*ts)); - } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) { - state->metrics->push_back (new MeterSection (*ms)); - } - } - - return state; -} - -void -TempoMap::save_state (std::string why) -{ - if (!in_set_state) { - StateManager::save_state (why); - } -} diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 053a866256..a5484813f9 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -50,8 +50,8 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data } Track::Track (Session& sess, const XMLNode& node, DataType default_type) - : Route (sess, "to be renamed", 0, 0, -1, -1, Route::Flag(0), default_type) - , _rec_enable_control (*this) + : Route (sess, node), + _rec_enable_control (*this) { _freeze_record.state = NoFreeze; _declickable = true; @@ -183,18 +183,6 @@ Track::set_record_enable (bool yn, void *src) _rec_enable_control.Changed (); } -void -Track::set_mode (TrackMode m) -{ - if (_diskstream) { - if (_mode != m) { - _mode = m; - _diskstream->set_destructive (m == Destructive); - ModeChanged(); - } - } -} - int Track::set_name (string str, void *src) { diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 9a841e81b4..9c94d32241 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -38,6 +38,7 @@ #endif #include <pbd/error.h> +#include <pbd/stacktrace.h> #include <pbd/xml++.h> #include <ardour/utils.h> @@ -221,7 +222,7 @@ region_name_from_path (string path) /* remove any "?R", "?L" or "?[a-z]" channel identifier */ string::size_type len = path.length(); - + if (len > 3 && (path[len-2] == '%' || path[len-2] == '?') && (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) { @@ -299,7 +300,7 @@ compute_equal_power_fades (nframes_t nframes, float* in, float* out) const float pan_law_attenuation = -3.0f; const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); - for (unsigned long n = 0; n < nframes; ++n) { + for (nframes_t n = 0; n < nframes; ++n) { float inVal = in[n]; float outVal = 1 - inVal; out[n] = outVal * (scale * outVal + 1.0f - scale); @@ -407,3 +408,82 @@ meter_hold_to_float (MeterHold hold) return 200.0f; } } + +AutoState +ARDOUR::string_to_auto_state (std::string str) +{ + if (str == X_("Off")) { + return Off; + } else if (str == X_("Play")) { + return Play; + } else if (str == X_("Write")) { + return Write; + } else if (str == X_("Touch")) { + return Touch; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg; + /*NOTREACHED*/ + return Touch; +} + +string +ARDOUR::auto_state_to_string (AutoState as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Off: + return X_("Off"); + break; + case Play: + return X_("Play"); + break; + case Write: + return X_("Write"); + break; + case Touch: + return X_("Touch"); + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg; + /*NOTREACHED*/ + return ""; +} + +AutoStyle +ARDOUR::string_to_auto_style (std::string str) +{ + if (str == X_("Absolute")) { + return Absolute; + } else if (str == X_("Trim")) { + return Trim; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg; + /*NOTREACHED*/ + return Trim; +} + +string +ARDOUR::auto_style_to_string (AutoStyle as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Absolute: + return X_("Absolute"); + break; + case Trim: + return X_("Trim"); + break; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg; + /*NOTREACHED*/ + return ""; +} + +extern "C" { + void c_stacktrace() { stacktrace (cerr); } +} diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index 800c5a9856..5d7a303fc6 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -110,16 +110,6 @@ VSTPlugin::set_block_size (nframes_t nframes) activate (); } -void -VSTPlugin::store_state (PluginState& state) -{ -} - -void -VSTPlugin::restore_state (PluginState& state) -{ -} - float VSTPlugin::default_value (uint32_t port) { diff --git a/libs/clearlooks/SConscript b/libs/clearlooks/SConscript new file mode 100644 index 0000000000..0df20efe56 --- /dev/null +++ b/libs/clearlooks/SConscript @@ -0,0 +1,23 @@ +import os.path + +Import ('env install_prefix') + +clearlooks = env.Copy() + +clearlooks.Replace(CCFLAGS = ' `pkg-config --cflags gtk+-2.0` ', + LINKFLAGS = ' `pkg-config --libs gtk+-2.0` ') + +libclearlooks = clearlooks.SharedLibrary('clearlooks', [ + 'clearlooks_draw.c', + 'clearlooks_rc_style.c', + 'clearlooks_style.c', + 'clearlooks_theme_main.c', + 'support.c' +]) + +usable_libclearlooks = clearlooks.Install ('engines', libclearlooks) +Default (usable_libclearlooks) + +env.Alias('install', + env.Install(os.path.join(install_prefix,'lib/ardour2/engines'), + libclearlooks)) diff --git a/libs/clearlooks/bits.c b/libs/clearlooks/bits.c new file mode 100644 index 0000000000..1e871bc5d3 --- /dev/null +++ b/libs/clearlooks/bits.c @@ -0,0 +1,121 @@ +static unsigned char dot_intensity[] = { +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x98,0xb9,0xc6,0xb9,0x91,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0xb9,0xbd,0xac,0x9e,0x65,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0xc6,0xac,0x9e,0x96,0x5c,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0xb9,0x9e,0x96,0x62,0x55,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x91,0x65,0x5c,0x55,0x68,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e,0x6e, +}; +static unsigned char dot_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x66,0xc4,0xff,0xc4,0x66,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x66,0xdf,0xff,0xff,0xff,0xdf,0x66,0x00,0x00,0x00, +0x00,0x00,0x00,0xc4,0xff,0xff,0xff,0xff,0xff,0xc4,0x00,0x00,0x00, +0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, +0x00,0x00,0x00,0xc4,0xff,0xff,0xff,0xff,0xff,0xc4,0x00,0x00,0x00, +0x00,0x00,0x00,0x66,0xdf,0xff,0xff,0xff,0xdf,0x66,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x66,0xc4,0xff,0xc4,0x66,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static unsigned char circle_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x23,0x62,0x92,0xb3,0xb2,0x95,0x2b,0x00,0x00,0x00, +0x00,0x00,0x3e,0xab,0xc9,0xeb,0xf9,0xf5,0xfd,0xff,0x57,0x00,0x00, +0x00,0x1f,0xb5,0xd8,0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0x2b,0x00, +0x00,0x67,0xb9,0xf2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9c,0x00, +0x00,0x9a,0xe2,0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0x00, +0x00,0xba,0xeb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xc0,0xfa,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0x00, +0x00,0x9b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9c,0x00, +0x00,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2b,0x00, +0x00,0x00,0x57,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x57,0x00,0x00, +0x00,0x00,0x00,0x2b,0x9c,0xe5,0xff,0xe5,0x9c,0x2b,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static unsigned char outline_alpha[] = { +0x00,0x00,0x00,0x4a,0xac,0xe9,0xff,0xe9,0xac,0x4a,0x00,0x00,0x00, +0x00,0x00,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x98,0x00,0x00, +0x00,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x98,0x00, +0x4a,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x4a, +0xac,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xac, +0xe9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe9, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xe9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe9, +0xac,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xac, +0x4a,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x4a, +0x00,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x98,0x00, +0x00,0x00,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x98,0x00,0x00, +0x00,0x00,0x00,0x4a,0xac,0xe9,0xff,0xe9,0xac,0x4a,0x00,0x00,0x00, +}; +static unsigned char inconsistent_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, +0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, +0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static unsigned char check_base_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static unsigned char check_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 11, 137, 151,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00, 9, 183, 172, 7,0x00,0x00, +0x00,0x00, 12, 18,0x00,0x00, 3, 161, 233, 27,0x00,0x00,0x00, +0x00,0x00, 199, 239, 101,0x00, 85, 253, 108,0x00,0x00,0x00,0x00, +0x00,0x00, 83, 245, 250, 75, 206, 230, 8,0x00,0x00,0x00,0x00, +0x00,0x00,0x00, 104, 252, 243, 253, 124,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00, 2, 162, 255, 241, 28,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, 18, 228, 163,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00, 78, 62,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static unsigned char check_inconsistent_alpha[] = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, +0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, +0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/libs/clearlooks/clearlooks_draw.c b/libs/clearlooks/clearlooks_draw.c new file mode 100644 index 0000000000..144be35152 --- /dev/null +++ b/libs/clearlooks/clearlooks_draw.c @@ -0,0 +1,1293 @@ +#include "clearlooks_draw.h" +#include "clearlooks_style.h" + +#include "support.h" + +/** WANTED: + FASTER GRADIENT FILL FUNCTION, POSSIBLY USING XRENDER. **/ + +static void cl_draw_borders (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r); + +static void cl_draw_line (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x1, int y1, int x2, int y2, CLBorderType border, + CLRectangle *r); + +static void cl_draw_corner (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, + CLRectangle *r, CLCornerSide corner); + +static void cl_draw_fill (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r); + +void cl_draw_rectangle (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r) +{ + if (r->fillgc) + { + cl_draw_fill(window, widget, style, x, y, width, height, r); + } + + if (r->bordergc) + { + cl_draw_borders(window, widget, style, x, y, width, height, r); + } +} + + +static void cl_get_coords ( CLBorderType border, + int x, int y, int width, int height, + CLRectangle *r, int *x1, int *y1, int *x2, int *y2) +{ + switch (border) + { + case CL_BORDER_TOP: + *x1 = x + r->corners[CL_CORNER_TOPLEFT]; + *x2 = *x1 + width - r->corners[CL_CORNER_TOPLEFT] - r->corners[CL_CORNER_TOPRIGHT] - 1; + *y1 = *y2 = y; + break; + case CL_BORDER_BOTTOM: + *x1 = x + r->corners[CL_CORNER_BOTTOMLEFT]; + *x2 = *x1 + width - r->corners[CL_CORNER_BOTTOMLEFT] - r->corners[CL_CORNER_BOTTOMRIGHT] - 1; + *y1 = *y2 = y + height - 1; + break; + case CL_BORDER_LEFT: + *x1 = *x2 = x; + *y1 = y + r->corners[CL_CORNER_TOPLEFT]; + *y2 = *y1 + height - r->corners[CL_CORNER_TOPLEFT] - r->corners[CL_CORNER_BOTTOMLEFT] - 1; + break; + case CL_BORDER_RIGHT: + *x1 = *x2 = x + width - 1; + *y1 = y + r->corners[CL_CORNER_TOPRIGHT]; + *y2 = *y1 + height - r->corners[CL_CORNER_TOPRIGHT] - r->corners[CL_CORNER_BOTTOMRIGHT] - 1; + break; + } +} + +void cl_draw_borders (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r) +{ + int x1, y1, x2, y2, i; + + if (r->bordergc == NULL) + return; + + for ( i=0; i<4; i++) /* draw all four borders + corners */ + { + cl_get_coords (i, x, y, width, height, r, &x1, &y1, &x2, &y2); + cl_draw_line (window, widget, style, x1, y1, x2, y2, i, r); + cl_draw_corner (window, widget, style, x, y, width, height, r, i ); + } +} + + +static GdkColor cl_gc_get_foreground(GdkGC *gc) +{ + GdkGCValues values; + gdk_gc_get_values (gc, &values); + return values.foreground; +} + +static void cl_draw_line (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x1, int y1, int x2, int y2, CLBorderType border, + CLRectangle *r) +{ + if (r->gradient_type == CL_GRADIENT_NONE || + r->border_gradient.from == NULL || r->border_gradient.to == NULL ) + { + gdk_draw_line (window, r->bordergc, x1, y1, x2, y2); + } + else if (r->gradient_type == CL_GRADIENT_HORIZONTAL && (border == CL_BORDER_TOP || border == CL_BORDER_BOTTOM)) + { + draw_vgradient (window, r->bordergc, style, + x1, y1, x2-x1+1, 1, + r->border_gradient.from, r->border_gradient.to); + } + else if (r->gradient_type == CL_GRADIENT_VERTICAL && (border == CL_BORDER_LEFT || border == CL_BORDER_RIGHT)) + { + draw_hgradient (window, r->bordergc, style, + x1, y1, 1, y2-y1+1, + r->border_gradient.from, r->border_gradient.to); + } + else + { + GdkColor tmp_color = cl_gc_get_foreground (r->bordergc); + + if (r->gradient_type == CL_GRADIENT_HORIZONTAL && border == CL_BORDER_LEFT || + r->gradient_type == CL_GRADIENT_VERTICAL && border == CL_BORDER_TOP) + gdk_gc_set_foreground (r->bordergc, r->border_gradient.from); + else + gdk_gc_set_foreground (r->bordergc, r->border_gradient.to); + + gdk_draw_line (window, r->bordergc, x1, y1, x2, y2); + + gdk_gc_set_foreground (r->bordergc, &tmp_color); + } +} + +static GdkColor *cl_get_gradient_corner_color (CLRectangle *r, CLCornerSide corner) +{ + GdkColor *color; + + if (r->border_gradient.from == NULL || r->border_gradient.to == NULL) + { + color = NULL; + } + else if ((r->gradient_type == CL_GRADIENT_HORIZONTAL && (corner == CL_CORNER_TOPLEFT || corner == CL_CORNER_BOTTOMLEFT)) || + (r->gradient_type == CL_GRADIENT_VERTICAL && (corner == CL_CORNER_TOPLEFT || corner == CL_CORNER_TOPRIGHT))) + { + color = r->border_gradient.from; + } + else /* no gradient or other corner */ + { + color = r->border_gradient.to; + } + + return color; +} + +static void cl_draw_corner (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, + CLRectangle *r, CLCornerSide corner) +{ + GdkColor *color; + GdkColor aacolor; /* anti-aliasing color */ + GdkGCValues values; + GdkColor tmp; + GdkColor *bgcolor; + + int x1; + int y1; + + if (r->corners[corner] == CL_CORNER_NONE) + return; + + color = cl_get_gradient_corner_color (r, corner); + gdk_gc_get_values (r->bordergc, &values); + + if (color == NULL) + { + tmp = values.foreground; + gdk_colormap_query_color (gtk_widget_get_colormap(widget), values.foreground.pixel, &tmp); + color = &tmp; + } + + bgcolor = get_parent_bgcolor(widget); + + if (bgcolor == NULL) + { + bgcolor = color; + } + + blend (style->colormap, bgcolor, color, &aacolor, 70); + + if (r->corners[corner] == CL_CORNER_ROUND) + { + x1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_BOTTOMLEFT) ? x+1 : x+width - 2; + + y1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_TOPRIGHT) ? y+1 : y+height - 2; + + gdk_gc_set_foreground (r->bordergc, color); + gdk_draw_point (window, r->bordergc, x1, y1); + + gdk_gc_set_foreground (r->bordergc, &aacolor); + + x1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_BOTTOMLEFT) ? x+1 : x+width-2; + + y1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_TOPRIGHT) ? y : y+height-1; + + gdk_draw_point (window, r->bordergc, x1, y1); + + x1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_BOTTOMLEFT) ? x : x+width-1; + + y1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_TOPRIGHT) ? y+1 : y+height-2; + + gdk_draw_point (window, r->bordergc, x1, y1); + + } + else if (r->corners[corner] == CL_CORNER_NARROW) + { + x1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_BOTTOMLEFT) ? x : x+width-1; + + y1 = (corner == CL_CORNER_TOPLEFT || + corner == CL_CORNER_TOPRIGHT) ? y : y+height-1; + + gdk_gc_set_foreground (r->bordergc, &aacolor); + gdk_draw_point (window, r->bordergc, x1, y1); + } + + gdk_gc_set_foreground (r->bordergc, &values.foreground); +} + +static void cl_draw_fill (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r) +{ + if (r->gradient_type == CL_GRADIENT_NONE || + r->fill_gradient.from == NULL || r->fill_gradient.to == NULL) + { + gdk_draw_rectangle (window, r->fillgc, TRUE, + x+1, y+1, width-2, height-2); + } + else if (r->gradient_type == CL_GRADIENT_HORIZONTAL) + { + draw_vgradient (window, r->fillgc, gtk_widget_get_style(widget), + x+1, y+1, width-2, height-2, + r->fill_gradient.from, r->fill_gradient.to); + } + else if (r->gradient_type == CL_GRADIENT_VERTICAL) + { + draw_hgradient (window, r->fillgc, gtk_widget_get_style(widget), + x+1, y+1, width-2, height-2, + r->fill_gradient.from, r->fill_gradient.to); + } +} + +void cl_rectangle_set_button(CLRectangle *r, GtkStyle *style, + GtkStateType state_type, gboolean has_default, + gboolean has_focus, + CLBorderType tl, CLBorderType tr, + CLBorderType bl, CLBorderType br) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int my_state_type = (state_type == GTK_STATE_ACTIVE) ? 2 : 0; + GdkGC *border_gc = clearlooks_style->border_gc[CL_BORDER_UPPER+my_state_type]; + + + cl_rectangle_init (r, style->bg_gc[state_type], + clearlooks_style->border_gc[CL_BORDER_UPPER+my_state_type], + tl, tr, bl, br); + + if (state_type != GTK_STATE_INSENSITIVE && !has_default) + { + cl_rectangle_set_gradient (&r->border_gradient, + &clearlooks_style->border[CL_BORDER_UPPER+my_state_type], + &clearlooks_style->border[CL_BORDER_LOWER+my_state_type]); + } + else if (has_default) + r->bordergc = style->black_gc; + else + r->bordergc = clearlooks_style->shade_gc[4]; + + r->gradient_type = CL_GRADIENT_VERTICAL; + + r->topleft = (state_type != GTK_STATE_ACTIVE) ? style->light_gc[state_type] : clearlooks_style->shade_gc[4]; + r->bottomright = (state_type != GTK_STATE_ACTIVE) ? clearlooks_style->shade_gc[1] : NULL; + + shade (&style->bg[state_type], &r->tmp_color, 0.93); + + + cl_rectangle_set_gradient (&r->fill_gradient, + &style->bg[state_type], + &r->tmp_color); +} + +void cl_rectangle_set_entry (CLRectangle *r, GtkStyle *style, + GtkStateType state_type, + CLBorderType tl, CLBorderType tr, + CLBorderType bl, CLBorderType br, + gboolean has_focus) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkGC *bordergc; + + if (has_focus) + bordergc = clearlooks_style->spot3_gc; + else if (state_type != GTK_STATE_INSENSITIVE) + bordergc = clearlooks_style->border_gc[CL_BORDER_LOWER]; + else + bordergc = clearlooks_style->shade_gc[3]; + + cl_rectangle_init (r, style->base_gc[state_type], bordergc, + tl, tr, bl, br); + + if (state_type != GTK_STATE_INSENSITIVE ) + r->topleft = (has_focus) ? clearlooks_style->spot1_gc + : style->bg_gc[GTK_STATE_NORMAL]; + + if (has_focus) + r->bottomright = clearlooks_style->spot1_gc; + else if (state_type == GTK_STATE_INSENSITIVE) + r->bottomright = style->base_gc[state_type]; +} + +void cl_draw_shadow(GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int x1, y1, x2, y2; + + if (r->bottomright != NULL) + { + x1 = x+1+(r->corners[CL_CORNER_BOTTOMLEFT]/2); + y1 = y2 = y+height-2; + x2 = x+width - 1 - (1+r->corners[CL_CORNER_BOTTOMRIGHT]/2); + + gdk_draw_line (window, r->bottomright, x1, y1, x2, y2); + + x1 = x2 = x+width-2; + y1 = y+1+(r->corners[CL_CORNER_TOPRIGHT]/2); + y2 = y+height - 1 - (1+r->corners[CL_CORNER_BOTTOMRIGHT]/2); + + gdk_draw_line (window, r->bottomright, x1, y1, x2, y2); + } + + if (r->topleft != NULL) + { + x1 = x+1+(r->corners[CL_CORNER_TOPLEFT]/2); + y1 = y2 = y+1; + x2 = x+width-1-(1+r->corners[CL_CORNER_TOPRIGHT]/2); + + gdk_draw_line (window, r->topleft, x1, y1, x2, y2); + + x1 = x2 = x+1; + y1 = y+1+(r->corners[CL_CORNER_TOPLEFT]/2); + y2 = y+height-1-(1+r->corners[CL_CORNER_BOTTOMLEFT]/2); + + gdk_draw_line (window, r->topleft, x1, y1, x2, y2); + } +} + +void cl_rectangle_set_color (CLGradient *g, GdkColor *color) +{ + g->from = color; + g->to = color; +} + +void cl_rectangle_set_gradient (CLGradient *g, GdkColor *from, GdkColor *to) +{ + g->from = from; + g->to = to; +} + +void cl_rectangle_init (CLRectangle *r, + GdkGC *fillgc, GdkGC *bordergc, + int tl, int tr, int bl, int br) +{ + r->gradient_type = CL_GRADIENT_NONE; + + r->border_gradient.from = r->border_gradient.to = NULL; + r->fill_gradient.from = r->fill_gradient.to = NULL; + + r->fillgc = fillgc; + r->bordergc = bordergc; + + r->topleft = NULL; + r->bottomright = NULL; + + r->corners[CL_CORNER_TOPLEFT] = tl; + r->corners[CL_CORNER_TOPRIGHT] = tr; + r->corners[CL_CORNER_BOTTOMLEFT] = bl; + r->corners[CL_CORNER_BOTTOMRIGHT] = br; +} + +void cl_rectangle_set_corners (CLRectangle *r, int tl, int tr, int bl, int br) +{ + r->corners[CL_CORNER_TOPLEFT] = tl; + r->corners[CL_CORNER_TOPRIGHT] = tr; + r->corners[CL_CORNER_BOTTOMLEFT] = bl; + r->corners[CL_CORNER_BOTTOMRIGHT] = br; +} + +void cl_set_corner_sharpness (const gchar *detail, GtkWidget *widget, CLRectangle *r) +{ + if (widget->parent && GTK_IS_COMBO_BOX_ENTRY (widget->parent) || GTK_IS_COMBO (widget->parent)) + { + gboolean rtl = get_direction (widget->parent) == GTK_TEXT_DIR_RTL; + int cl = rtl ? CL_CORNER_ROUND : CL_CORNER_NONE; + int cr = rtl ? CL_CORNER_NONE : CL_CORNER_ROUND; + + cl_rectangle_set_corners (r, cl, cr, cl, cr); + } + else if (detail && !strcmp (detail, "spinbutton_up")) + { + gboolean rtl = get_direction (widget->parent) == GTK_TEXT_DIR_RTL; + int tl = rtl ? CL_CORNER_ROUND : CL_CORNER_NONE; + int tr = rtl ? CL_CORNER_NONE : CL_CORNER_ROUND; + + cl_rectangle_set_corners (r, tl, tr, + CL_CORNER_NONE, CL_CORNER_NONE); + } + else if (detail && !strcmp (detail, "spinbutton_down")) + { + gboolean rtl = get_direction (widget->parent) == GTK_TEXT_DIR_RTL; + int bl = rtl ? CL_CORNER_ROUND : CL_CORNER_NONE; + int br = rtl ? CL_CORNER_NONE : CL_CORNER_ROUND; + + cl_rectangle_set_corners (r, CL_CORNER_NONE, CL_CORNER_NONE, + bl, br); + } + else + { + cl_rectangle_set_corners (r, CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + }; +} + +void cl_rectangle_set_clip_rectangle (CLRectangle *r, GdkRectangle *area) +{ + if (area == NULL) + return; + + if (r->fillgc) + gdk_gc_set_clip_rectangle (r->fillgc, area); + + if (r->bordergc) + gdk_gc_set_clip_rectangle (r->bordergc, area); + + if (r->topleft) + gdk_gc_set_clip_rectangle (r->topleft, area); + + if (r->bottomright) + gdk_gc_set_clip_rectangle (r->bottomright, area); +} + +void cl_rectangle_reset_clip_rectangle (CLRectangle *r) +{ + if (r->fillgc) + gdk_gc_set_clip_rectangle (r->fillgc, NULL); + + if (r->bordergc) + gdk_gc_set_clip_rectangle (r->bordergc, NULL); + + if (r->topleft) + gdk_gc_set_clip_rectangle (r->topleft, NULL); + + if (r->bottomright) + gdk_gc_set_clip_rectangle (r->bottomright, NULL); +} + +void cl_rectangle_reset (CLRectangle *r, GtkStyle *style) +{ + cl_rectangle_init (r, + NULL, NULL, + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); +} + +static void cl_progressbar_points_transform (GdkPoint *points, int npoints, + int offset, gboolean is_horizontal) +{ + int i; + for ( i=0; i<npoints; i++) { + if ( is_horizontal ) + points[i].x += offset; + else + points[i].y += offset; + } +} + +GdkPixmap* cl_progressbar_tile_new (GdkDrawable *drawable, GtkWidget *widget, + GtkStyle *style, gint height, gint offset) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int width = height; + int line = 0; + int center = width/2; + int xdir = 1; + int trans; + + int stripe_width = height/2; + int topright = height + stripe_width; + int topright_div_2 = topright/2; + + double shift; + GdkPoint points[4]; + + GtkProgressBarOrientation orientation = gtk_progress_bar_get_orientation (GTK_PROGRESS_BAR (widget)); + gboolean is_horizontal = (orientation == GTK_PROGRESS_LEFT_TO_RIGHT || orientation == GTK_PROGRESS_RIGHT_TO_LEFT) ? 1 : 0; + + GdkPixmap *tmp = gdk_pixmap_new (widget->window, width, height, -1); + + GdkColor tmp_color; + shade (&clearlooks_style->spot2, &tmp_color, 0.90); + + if (is_horizontal) + draw_hgradient (tmp, style->black_gc, style, 0, 0, width, height, + &clearlooks_style->spot2, &tmp_color ); + else + draw_vgradient (tmp, style->black_gc, style, 0, 0, width, height, + &tmp_color, &clearlooks_style->spot2); /* TODO: swap for RTL */ + + if (orientation == GTK_PROGRESS_RIGHT_TO_LEFT || + orientation == GTK_PROGRESS_BOTTOM_TO_TOP) + { + offset = -offset; + xdir = -1; + } + + if (get_direction (widget) == GTK_TEXT_DIR_RTL) + offset = -offset; + + if (is_horizontal) + { + points[0] = (GdkPoint){xdir*(topright - stripe_width - topright_div_2), 0}; /* topleft */ + points[1] = (GdkPoint){xdir*(topright - topright_div_2), 0}; /* topright */ + points[2] = (GdkPoint){xdir*(stripe_width - topright_div_2), height}; /* bottomright */ + points[3] = (GdkPoint){xdir*(-topright_div_2), height}; /* bottomleft */ + } + else + { + points[0] = (GdkPoint){height, xdir*(topright - stripe_width - topright_div_2)}; /* topleft */ + points[1] = (GdkPoint){height, xdir*(topright - topright_div_2)}; /* topright */ + points[2] = (GdkPoint){0, xdir*(stripe_width - topright_div_2)}; /* bottomright */ + points[3] = (GdkPoint){0, xdir*(-topright_div_2)}; /* bottomleft */ + } + + + shift = (stripe_width*2)/(double)10; + cl_progressbar_points_transform (points, 4, (offset*shift), is_horizontal); + + trans = (width/2)-1-(stripe_width*2); + cl_progressbar_points_transform (points, 4, trans, is_horizontal); + gdk_draw_polygon (tmp, clearlooks_style->spot2_gc, TRUE, points, 4); + cl_progressbar_points_transform (points, 4, -trans, is_horizontal); + + trans = width/2-1; + cl_progressbar_points_transform (points, 4, trans, is_horizontal); + gdk_draw_polygon (tmp, clearlooks_style->spot2_gc, TRUE, points, 4); + cl_progressbar_points_transform (points, 4, -trans, is_horizontal); + + trans = (width/2)-1+(stripe_width*2); + cl_progressbar_points_transform (points, 4, trans, is_horizontal); + gdk_draw_polygon (tmp, clearlooks_style->spot2_gc, TRUE, points, 4); + + return tmp; +} + +/* could be improved, I think. */ +void cl_progressbar_fill (GdkDrawable *drawable, GtkWidget *widget, + GtkStyle *style, GdkGC *gc, + gint x, gint y, + gint width, gint height, + guint8 offset, GdkRectangle *area) +{ + GtkProgressBarOrientation orientation = gtk_progress_bar_get_orientation (GTK_PROGRESS_BAR (widget)); + gint size = (orientation == GTK_PROGRESS_LEFT_TO_RIGHT || orientation == GTK_PROGRESS_RIGHT_TO_LEFT) ? height : width; + GdkPixmap *tile = cl_progressbar_tile_new (widget->window, widget, style, size, offset); + + gint nx = x, + ny = y, + nwidth = height, + nheight = width; + + gdk_gc_set_clip_rectangle (gc, area); + + switch (orientation) + { + case GTK_PROGRESS_LEFT_TO_RIGHT: + { + while (nx <= x + width ) + { + if (nx + nwidth > x+width ) nwidth = (x+width) - nx; + gdk_draw_drawable (drawable, gc, tile, 0, 0, nx, y, nwidth, height); + if (height <= 1) + nx += 1; + else + nx += (height-1 + !(height % 2)); + } + break; + } + case GTK_PROGRESS_RIGHT_TO_LEFT: + { + gint src_x = 0, dst_x; + nx += width; + while (nx >= x ) + { + dst_x = nx - height; + if (dst_x < x ) + { + src_x = x - dst_x; + dst_x = x; + } + gdk_draw_drawable (drawable, gc, tile, src_x, 0, dst_x, y, nwidth, height); + if (height <= 1) + nx -= 1; + else + nx -= (height-1 + !(height % 2)); + } + break; + } + case GTK_PROGRESS_TOP_TO_BOTTOM: + { + while (ny <= y + height ) + { + if (ny + nheight > y+height ) nheight = (y+height) - ny; + gdk_draw_drawable (drawable, gc, tile, 0, 0, x, ny, width, nheight); + if (width <= 1) + ny += 1; + else + ny += (width-1 + !(width % 2)); + } + break; + } + case GTK_PROGRESS_BOTTOM_TO_TOP: + { + gint src_y = 0, dst_y; + ny += height; + while (ny >= y ) + { + dst_y = ny - width; + if (dst_y < y ) + { + src_y = y - dst_y; + dst_y = y; + } + gdk_draw_drawable (drawable, gc, tile, 0, src_y, x, dst_y, width, width); + if (width <= 1) + ny -= 1; + else + ny -= (width-1 + !(width % 2)); + } + break; + } + } + + gdk_gc_set_clip_rectangle (gc, NULL); + + g_object_unref (tile); +} + +GdkColor cl_gc_set_fg_color_shade (GdkGC *gc, GdkColormap *colormap, + GdkColor *from, gfloat s) +{ + GdkColor tmp_color; + GdkGCValues values; + + shade (from, &tmp_color, s); + gdk_gc_get_values (gc, &values); + gdk_rgb_find_color (colormap, &tmp_color); + gdk_gc_set_foreground (gc, &tmp_color); + + return values.foreground; +} + +/* #warning MOVE THIS TO SUPPORT.C/H SO THE DRAW_CORNER FUNCTION CAN USE IT. OR, MAKE DRAW_CORNER USE IT SOME OTHER WAY. */ + +static void cl_get_window_style_state (GtkWidget *widget, GtkStyle **style, GtkStateType *state_type) +{ + GtkStyle *windowstyle = NULL; + GtkWidget *tmpwidget = widget; + GtkStateType windowstate; + + if (widget && GTK_IS_ENTRY (widget)) + tmpwidget = tmpwidget->parent; + + while (tmpwidget && GTK_WIDGET_NO_WINDOW (tmpwidget) && !GTK_IS_NOTEBOOK(tmpwidget)) + { + tmpwidget = tmpwidget->parent; + } + + *style = tmpwidget->style; + *state_type = GTK_WIDGET_STATE(tmpwidget); +} + +static GdkGC *cl_get_window_bg_gc (GtkWidget *widget) +{ + GtkStyle *style; + GtkStateType state_type; + + cl_get_window_style_state (widget, &style, &state_type); + + return style->bg_gc[state_type]; +} + +/****************************************************************************** + * DRAW THE MIGHTY WIDGETS! * + ******************************************************************************/ + +void cl_draw_inset (GtkStyle *style, GdkWindow *window, GtkWidget *widget, + GdkRectangle *area, + gint x, gint y, gint width, gint height, + int tl, int tr, int bl, int br ) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE(style); + ClearlooksStyle *clwindowstyle; /* style of the window this widget is on */ + GtkStateType windowstate; + CLRectangle r; + + cl_rectangle_init (&r, NULL, style->black_gc, + tl, tr, bl, br); + + r.gradient_type = CL_GRADIENT_VERTICAL; + + cl_get_window_style_state(widget, (GtkStyle**)&clwindowstyle, &windowstate); + + g_assert (clwindowstyle != NULL); + + if (GTK_WIDGET_HAS_DEFAULT (widget)) + { + r.bordergc = style->mid_gc[GTK_STATE_NORMAL]; + } + else + { + cl_rectangle_set_gradient (&r.border_gradient, + &clwindowstyle->inset_dark[windowstate], + &clwindowstyle->inset_light[windowstate]); + } + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); +} + +/* Draw a normal (toggle)button. Not spinbuttons.*/ +void cl_draw_button(GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE(style); + int my_state_type = (state_type == GTK_STATE_ACTIVE) ? 2 : 0; + GdkGC *bg_gc = NULL; + gboolean is_active = FALSE; + CLRectangle r; + + /* Get the background color of the window we're on */ + bg_gc = cl_get_window_bg_gc(widget); + + cl_rectangle_set_button (&r, style, state_type, + GTK_WIDGET_HAS_DEFAULT (widget), + GTK_WIDGET_HAS_FOCUS (widget), + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + + if (state_type == GTK_STATE_ACTIVE) + is_active = TRUE; + + if (GTK_IS_TOGGLE_BUTTON(widget) && + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) && + state_type == GTK_STATE_PRELIGHT) + { + cl_rectangle_set_gradient (&r.fill_gradient, &clearlooks_style->shade[1], &clearlooks_style->shade[1]); + r.topleft = clearlooks_style->shade_gc[3]; + r.bottomright = clearlooks_style->shade_gc[1]; + + is_active = TRUE; + } + + if (!is_active) + r.fillgc = NULL; + + if (!GTK_IS_NOTEBOOK (widget->parent)) + { + gdk_draw_rectangle (window, bg_gc, FALSE, x, y, width-1, height-1); + + /* Draw "sunken" look when border thickness is more than 2 pixels. */ + if (style->xthickness > 2 && style->ythickness > 2) + cl_draw_inset (style, window, widget, area, x, y, width, height, + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + } + + /* Draw "sunken" look when border thickness is more than 2 pixels.*/ + if (style->xthickness > 2 && style->ythickness > 2) + { + x++; + y++; + height-=2; + width-=2; + } + + /* Don't draw the normal gradient for normal buttons. */ + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + + + if (!is_active) + { + int tmp_height = (float)height*0.25; + + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], area); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2,y+2,width-4,tmp_height, + &clearlooks_style->button_g1[state_type], + &clearlooks_style->button_g2[state_type]); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2, y+2+tmp_height, width-4, height-3-tmp_height*2, + &clearlooks_style->button_g2[state_type], + &clearlooks_style->button_g3[state_type]); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2,y+height-tmp_height-1,width-4,tmp_height, + &clearlooks_style->button_g3[state_type], + &clearlooks_style->button_g4[state_type]); + + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], NULL); + } + + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); +} + +/* Draw spinbuttons. */ +void cl_draw_spinbutton(GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + CLRectangle r; + GdkRectangle new_area; + + int tl = CL_CORNER_NONE, tr = CL_CORNER_NONE, + bl = CL_CORNER_NONE, br = CL_CORNER_NONE; + + if (area == NULL) + { + new_area.x = x; + new_area.y = y; + new_area.width = width; + new_area.height = height; + area = &new_area; + } + + if (!strcmp (detail, "spinbutton")) /* draws the 'back' of the spinbutton */ + { + GdkGC *bg_gc = cl_get_window_bg_gc(widget); + + gdk_gc_set_clip_rectangle (bg_gc, area); + gdk_draw_rectangle (window, bg_gc, FALSE, x, y, width-1, height-1); + gdk_gc_set_clip_rectangle (bg_gc, NULL); + + if (style->xthickness > 2 && style->ythickness > 2) + cl_draw_inset (style, window, widget, area, x, y, width, height, + CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + + return; + } + + if (!strcmp (detail, "spinbutton_up")) + { + tr = CL_CORNER_ROUND; + + (style->xthickness > 2 && style->ythickness > 2) ? y++ : height++; + } + + if (!strcmp (detail, "spinbutton_down")) + { + br = CL_CORNER_ROUND; + + if (style->xthickness > 2 && style->ythickness > 2) + height--; + } + + cl_rectangle_set_button (&r, style, state_type, + GTK_WIDGET_HAS_DEFAULT (widget), + GTK_WIDGET_HAS_FOCUS (widget), + tl, tr, + bl, br); + width--; + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); +} + +void cl_draw_combobox_entry (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + CLRectangle r; + + gboolean rtl = get_direction (widget->parent) == GTK_TEXT_DIR_RTL; + gboolean has_focus = GTK_WIDGET_HAS_FOCUS (widget); + + int cl = rtl ? CL_CORNER_NONE : CL_CORNER_ROUND, + cr = rtl ? CL_CORNER_ROUND : CL_CORNER_NONE; + + GdkGC *bg_gc = cl_get_window_bg_gc(widget); + + if (rtl) + { + if (!has_focus) + { + x -= 1; + width +=1; + } + } + else + { + width += 2; + if (has_focus) width--; /* this gives us a 2px focus line at the right side. */ + } + + cl_rectangle_set_entry (&r, style, state_type, + cl, cr, cl, cr, + has_focus); + + gdk_gc_set_clip_rectangle (bg_gc, area); + gdk_draw_rectangle (window, bg_gc, FALSE, x, y, width-1, height-1); + gdk_gc_set_clip_rectangle (bg_gc, NULL); + + /* Draw "sunken" look when border thickness is more than 2 pixels. */ + if (style->xthickness > 2 && style->ythickness > 2) + { + cl_draw_inset (style, window, widget, area, x, y, width, height, + cl, cr, cl, cr); + + y++; + x++; + width-=2; + height-=2; + } + + cl_rectangle_set_clip_rectangle (&r, area); + + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + + cl_rectangle_reset_clip_rectangle (&r); +} + +void cl_draw_combobox_button (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE(style); + gboolean is_active = FALSE; + gboolean draw_inset = FALSE; + CLRectangle r; + + cl_rectangle_set_button (&r, style, state_type, + GTK_WIDGET_HAS_DEFAULT (widget), + GTK_WIDGET_HAS_FOCUS (widget), + CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + + if (state_type == GTK_STATE_ACTIVE) + is_active = TRUE; + else + r.fillgc = NULL; + + /* Seriously, why can't non-gtk-apps at least try to be decent citizens? + Take this fscking OpenOffice.org 1.9 for example. The morons responsible + for this utter piece of crap give the clip size wrong values! :'( */ + + if (area) + { + area->x = x; + area->y = y; + area->width = width; + area->height = height; + } + + x--; + width++; + + /* Draw "sunken" look when border thickness is more than 2 pixels. */ + if (GTK_IS_COMBO(widget->parent)) + draw_inset = (widget->parent->style->xthickness > 2 && + widget->parent->style->ythickness > 2); + else + draw_inset = (style->xthickness > 2 && style->ythickness > 2); + + if (draw_inset) + { + cl_draw_inset (style, window, widget, area, x, y, width, height, + CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + + x++; + y++; + height-=2; + width-=2; + } + else + { + x++; + width--; + } + + if (area) + cl_rectangle_set_clip_rectangle (&r, area); + + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + + if (!is_active) + { + int tmp_height = (float)height*0.25; + + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], area); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2,y+2,width-4,tmp_height, + &clearlooks_style->button_g1[state_type], + &clearlooks_style->button_g2[state_type]); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2, y+2+tmp_height, width-4, height-3-tmp_height*2, + &clearlooks_style->button_g2[state_type], + &clearlooks_style->button_g3[state_type]); + + draw_hgradient (window, style->bg_gc[state_type], style, + x+2,y+height-tmp_height-1,width-4,tmp_height, + &clearlooks_style->button_g3[state_type], + &clearlooks_style->button_g4[state_type]); + + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], NULL); + } + + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + + if (area) + cl_rectangle_reset_clip_rectangle (&r); +} + +/* Draw text Entry */ +void cl_draw_entry (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + CLRectangle r; + gboolean has_focus = GTK_WIDGET_HAS_FOCUS(widget); + GdkGC *bg_gc = cl_get_window_bg_gc(widget); + + gdk_draw_rectangle (window, bg_gc, FALSE, x, y, width-1, height-1); + + gtk_style_apply_default_background (style, window, TRUE, state_type, + area, x+1, y+1, width-2, height-2); + + + cl_rectangle_set_entry (&r, style, state_type, + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND, + has_focus); + + /* Draw "sunken" look when border thickness is more than 2 pixels. */ + if (style->xthickness > 2 && style->ythickness > 2) + { + cl_draw_inset (style, window, widget, area, x, y, width, height, + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + + x++; + y++; + width-=2; + height-=2; + } + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); +} + +void cl_draw_optionmenu(GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, GtkWidget *widget, + const gchar *detail, + gint x, gint y, gint width, gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE(style); + GtkRequisition indicator_size; + GtkBorder indicator_spacing; + int line_pos; + + option_menu_get_props (widget, &indicator_size, &indicator_spacing); + + if (get_direction (widget) == GTK_TEXT_DIR_RTL) + line_pos = x + (indicator_size.width + indicator_spacing.left + indicator_spacing.right) + style->xthickness; + else + line_pos = x + width - (indicator_size.width + indicator_spacing.left + indicator_spacing.right) - style->xthickness; + + cl_draw_button (style, window, state_type, shadow_type, area, widget, detail, x, y, width, height); + + gdk_draw_line (window, clearlooks_style->shade_gc[3], + line_pos, y + style->ythickness - 1, line_pos, + y + height - style->ythickness); + + gdk_draw_line (window, style->light_gc[state_type], + line_pos+1, y + style->ythickness - 1, line_pos+1, + y + height - style->ythickness); +} + + +void cl_draw_menuitem_button (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int width, int height, CLRectangle *r) +{ + ClearlooksStyle *clearlooks_style = (ClearlooksStyle*)style; + gboolean menubar = (widget->parent && GTK_IS_MENU_BAR(widget->parent)) ? TRUE : FALSE; + int corner = CL_CORNER_NARROW; + GdkColor lower_color; + + shade (&style->base[GTK_STATE_SELECTED], &lower_color, 0.85); + + if (menubar) + { + height++; + corner = CL_CORNER_NONE; + r->bordergc = clearlooks_style->border_gc[CL_BORDER_UPPER]; + } + else + { + r->bordergc = clearlooks_style->spot3_gc; + } + + cl_rectangle_set_corners (r, corner, corner, corner, corner); + + cl_rectangle_set_gradient (&r->fill_gradient, + &style->base[GTK_STATE_SELECTED], &lower_color); + + r->gradient_type = CL_GRADIENT_VERTICAL; + + r->fillgc = clearlooks_style->spot2_gc; + r->topleft = clearlooks_style->spot1_gc; + + cl_rectangle_set_clip_rectangle (r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, r); + cl_draw_shadow (window, widget, style, x, y, width, height, r); + cl_rectangle_reset_clip_rectangle (r); +} + +void cl_draw_menuitem_flat (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int width, int height, CLRectangle *r) +{ + ClearlooksStyle *clearlooks_style = (ClearlooksStyle*)style; + gboolean menubar = (widget->parent && GTK_IS_MENU_BAR(widget->parent)) ? TRUE : FALSE; + GdkColor tmp; + + cl_rectangle_set_corners (r, CL_CORNER_NARROW, CL_CORNER_NARROW, + CL_CORNER_NARROW, CL_CORNER_NARROW); + + tmp = cl_gc_set_fg_color_shade (style->black_gc, style->colormap, + &style->base[GTK_STATE_PRELIGHT], 0.8); + + r->bordergc = style->black_gc; + r->fillgc = style->base_gc[GTK_STATE_PRELIGHT]; + + if (menubar) height++; + + cl_rectangle_set_clip_rectangle (r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, r); + cl_rectangle_reset_clip_rectangle (r); + + gdk_gc_set_foreground (style->black_gc, &tmp); +} + +void cl_draw_menuitem_gradient (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int width, int height, CLRectangle *r) +{ + ClearlooksStyle *clearlooks_style = (ClearlooksStyle*)style; + gboolean menubar = (widget->parent && GTK_IS_MENU_BAR(widget->parent)) ? TRUE : FALSE; + GdkColor tmp; + GdkColor lower_color; + + shade (&style->base[GTK_STATE_SELECTED], &lower_color, 0.8); + + cl_rectangle_set_corners (r, CL_CORNER_NARROW, CL_CORNER_NARROW, + CL_CORNER_NARROW, CL_CORNER_NARROW); + + cl_rectangle_set_gradient (&r->fill_gradient, + &style->base[GTK_STATE_SELECTED], &lower_color); + + r->gradient_type = CL_GRADIENT_VERTICAL; + + tmp = cl_gc_set_fg_color_shade (style->black_gc, style->colormap, + &style->base[GTK_STATE_PRELIGHT], 0.8); + + r->bordergc = style->black_gc; + r->fillgc = style->base_gc[GTK_STATE_PRELIGHT]; + + if (menubar) height++; + + cl_rectangle_set_clip_rectangle (r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, r); + cl_rectangle_reset_clip_rectangle (r); + + gdk_gc_set_foreground (style->black_gc, &tmp); +} + +void cl_draw_treeview_header (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + gint columns = 0, column_index = -1, fill_width = width; + gboolean is_etree = strcmp("ETree", G_OBJECT_TYPE_NAME(widget->parent)) == 0; + gboolean resizable = TRUE; + + GdkGC *bottom = clearlooks_style->shade_gc[5]; + + if ( width < 2 || height < 2 ) + return; + + if (GTK_IS_TREE_VIEW (widget->parent)) + { + gtk_treeview_get_header_index (GTK_TREE_VIEW(widget->parent), + widget, &column_index, &columns, + &resizable); + } + else if (GTK_IS_CLIST (widget->parent)) + { + gtk_clist_get_header_index (GTK_CLIST(widget->parent), + widget, &column_index, &columns); + } + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], area); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[4], area); + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], area); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[5], area); + } + + if (state_type != GTK_STATE_NORMAL) + fill_width-=2; + + gdk_draw_rectangle (window, style->bg_gc[state_type], TRUE, x, y, fill_width, height-(height/3)+1); + + draw_hgradient (window, style->bg_gc[state_type], style, + x, 1+y+height-(height/3), fill_width, height/3, + &style->bg[state_type], &clearlooks_style->inset_dark[state_type]); + + if (resizable || (column_index != columns-1)) + { + gdk_draw_line (window, clearlooks_style->shade_gc[4], x+width-2, y+4, x+width-2, y+height-5); + gdk_draw_line (window, clearlooks_style->shade_gc[0], x+width-1, y+4, x+width-1, y+height-5); + } + + /* left light line */ + if (column_index == 0) + gdk_draw_line (window, clearlooks_style->shade_gc[0], x, y+1, x, y+height-2); + + /* top light line */ + gdk_draw_line (window, clearlooks_style->shade_gc[0], x, y, x+width-1, y); + + /* bottom dark line */ + if (state_type == GTK_STATE_INSENSITIVE) + bottom = clearlooks_style->shade_gc[3]; + + + gdk_draw_line (window, bottom, x, y+height-1, x+width-1, y+height-1); + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], NULL); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[4], NULL); + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], NULL); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[5], NULL); + } +} diff --git a/libs/clearlooks/clearlooks_draw.h b/libs/clearlooks/clearlooks_draw.h new file mode 100644 index 0000000000..a8cbb3732f --- /dev/null +++ b/libs/clearlooks/clearlooks_draw.h @@ -0,0 +1,159 @@ +#ifndef CLEARLOOKS_DRAW_H +#define CLEARLOOKS_DRAW_H + +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +typedef struct +{ + GdkColor *from; + GdkColor *to; +} CLGradient; + +typedef enum +{ + CL_GRADIENT_NONE, + CL_GRADIENT_HORIZONTAL, + CL_GRADIENT_VERTICAL +} CLGradientType; + +typedef struct +{ + CLGradient fill_gradient; + CLGradient border_gradient; + + CLGradientType gradient_type; + + GdkGC *bordergc; + GdkGC *fillgc; + + guint8 corners[4]; + + GdkGC *topleft; /* top + left shadow */ + GdkGC *bottomright; /* bottom + right shadow */ + + GdkColor tmp_color; /* used for gradient */ +} CLRectangle; + +typedef enum /* DON'T CHANGE THE ORDER! */ +{ + CL_CORNER_TOPRIGHT, + CL_CORNER_BOTTOMRIGHT, + CL_CORNER_BOTTOMLEFT, + CL_CORNER_TOPLEFT +} CLCornerSide; + +typedef enum /* DON'T CHANGE THE ORDER! */ +{ + CL_BORDER_TOP, + CL_BORDER_RIGHT, + CL_BORDER_BOTTOM, + CL_BORDER_LEFT +} CLBorderType; + +typedef enum +{ + CL_CORNER_NONE = 0, + CL_CORNER_NARROW = 1, + CL_CORNER_ROUND = 2 +} CLCornerSharpness; + + + +CLRectangle *cl_rectangle_new(GdkGC *fillgc, GdkGC *bordergc, + int tl, int tr, int bl, int br); + +void cl_draw_rectangle (GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r); + +void cl_rectangle_set_color (CLGradient *g, GdkColor *color); +void cl_rectangle_set_gradient (CLGradient *g, GdkColor *from, GdkColor *to); + +void cl_rectangle_set_button (CLRectangle *r, GtkStyle *style, + GtkStateType state_type, gboolean hasdefault, gboolean has_focus, + CLBorderType tl, CLBorderType tr, + CLBorderType bl, CLBorderType br); + +void cl_rectangle_set_entry (CLRectangle *r, GtkStyle *style, + GtkStateType state_type, + CLBorderType tl, CLBorderType tr, + CLBorderType bl, CLBorderType br, + gboolean has_focus); + +void cl_draw_shadow(GdkWindow *window, GtkWidget *widget, GtkStyle *style, + int x, int y, int width, int height, CLRectangle *r); + +void cl_rectangle_set_clip_rectangle (CLRectangle *r, GdkRectangle *area); +void cl_rectangle_reset_clip_rectangle (CLRectangle *r); + +void cl_set_corner_sharpness (const gchar *detail, GtkWidget *widget, CLRectangle *r); + + +void cl_rectangle_set_corners (CLRectangle *r, int tl, int tr, int bl, int br); + +void cl_rectangle_init (CLRectangle *r, GdkGC *fillgc, GdkGC *bordergc, + int tl, int tr, int bl, int br); + +void cl_rectangle_reset (CLRectangle *r, GtkStyle *style); + + +GdkPixmap* cl_progressbar_tile_new (GdkDrawable *drawable, GtkWidget *widget, + GtkStyle *style, gint height, gint offset); + +void cl_progressbar_fill (GdkDrawable *drawable, GtkWidget *widget, + GtkStyle *style, GdkGC *gc, + gint x, gint y, gint width, gint height, + guint8 offset, GdkRectangle *area); + +GdkColor cl_gc_set_fg_color_shade (GdkGC *gc, GdkColormap *colormap, + GdkColor *from, gfloat s); + +void cl_draw_spinbutton(GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +void cl_draw_button(GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +void cl_draw_entry (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +void cl_draw_combobox_entry (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +void cl_draw_combobox_button (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +void cl_draw_menuitem_button (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int wiidth, int height, CLRectangle *r); + +void cl_draw_menuitem_flat (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int wiidth, int height, CLRectangle *r); + +void cl_draw_menuitem_gradient (GdkDrawable *window, GtkWidget *widget, GtkStyle *style, + GdkRectangle *area, GtkStateType state_type, + int x, int y, int wiidth, int height, CLRectangle *r); + +void cl_draw_treeview_header (GtkStyle *style, GdkWindow *window, + GtkStateType state_type, GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, const gchar *detail, + gint x, gint y, gint width, gint height); + +#endif /* CLEARLOOKS_DRAW_H */ diff --git a/libs/clearlooks/clearlooks_rc_style.c b/libs/clearlooks/clearlooks_rc_style.c new file mode 100644 index 0000000000..1c5f2c495e --- /dev/null +++ b/libs/clearlooks/clearlooks_rc_style.c @@ -0,0 +1,392 @@ +/* Clearlooks theme engine + * Copyright (C) 2005 Richard Stellingwerff. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by Owen Taylor <otaylor@redhat.com> + * and by Alexander Larsson <alexl@redhat.com> + * Modified by Richard Stellingwerff <remenic@gmail.com> + */ + +#include "clearlooks_style.h" +#include "clearlooks_rc_style.h" + +static void clearlooks_rc_style_init (ClearlooksRcStyle *style); +static void clearlooks_rc_style_class_init (ClearlooksRcStyleClass *klass); +static GtkStyle *clearlooks_rc_style_create_style (GtkRcStyle *rc_style); +static guint clearlooks_rc_style_parse (GtkRcStyle *rc_style, + GtkSettings *settings, + GScanner *scanner); +static void clearlooks_rc_style_merge (GtkRcStyle *dest, + GtkRcStyle *src); + + +static GtkRcStyleClass *parent_class; + +GType clearlooks_type_rc_style = 0; + +enum +{ + TOKEN_SPOTCOLOR = G_TOKEN_LAST + 1, + TOKEN_CONTRAST, + TOKEN_SUNKENMENU, + TOKEN_PROGRESSBARSTYLE, + TOKEN_MENUBARSTYLE, + TOKEN_MENUITEMSTYLE, + TOKEN_LISTVIEWITEMSTYLE +}; + +static struct + { + const gchar *name; + guint token; + } +theme_symbols[] = +{ + { "spotcolor", TOKEN_SPOTCOLOR }, + { "contrast", TOKEN_CONTRAST }, + { "sunkenmenubar", TOKEN_SUNKENMENU }, + { "progressbarstyle", TOKEN_PROGRESSBARSTYLE }, + { "menubarstyle", TOKEN_MENUBARSTYLE }, + { "menuitemstyle", TOKEN_MENUITEMSTYLE }, + { "listviewitemstyle", TOKEN_LISTVIEWITEMSTYLE } +}; + + +void +clearlooks_rc_style_register_type (GTypeModule *module) +{ + static const GTypeInfo object_info = + { + sizeof (ClearlooksRcStyleClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) clearlooks_rc_style_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ClearlooksRcStyle), + 0, /* n_preallocs */ + (GInstanceInitFunc) clearlooks_rc_style_init, + NULL + }; + + clearlooks_type_rc_style = g_type_module_register_type (module, + GTK_TYPE_RC_STYLE, + "ClearlooksRcStyle", + &object_info, 0); +} + +static void +clearlooks_rc_style_init (ClearlooksRcStyle *clearlooks_rc) +{ + clearlooks_rc->has_spot_color = FALSE; + clearlooks_rc->contrast = 1.0; + clearlooks_rc->sunkenmenubar = 1; + clearlooks_rc->progressbarstyle = 0; + clearlooks_rc->menubarstyle = 0; + clearlooks_rc->menuitemstyle = 1; + clearlooks_rc->listviewitemstyle = 1; +} + +static void +clearlooks_rc_style_class_init (ClearlooksRcStyleClass *klass) +{ + GtkRcStyleClass *rc_style_class = GTK_RC_STYLE_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + rc_style_class->parse = clearlooks_rc_style_parse; + rc_style_class->create_style = clearlooks_rc_style_create_style; + rc_style_class->merge = clearlooks_rc_style_merge; +} + +static guint +theme_parse_color(GtkSettings *settings, + GScanner *scanner, + GdkColor *color) +{ + guint token; + + /* Skip 'blah_color' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + return gtk_rc_parse_color (scanner, color); +} + +static guint +theme_parse_contrast(GtkSettings *settings, + GScanner *scanner, + double *contrast) +{ + guint token; + + /* Skip 'contrast' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_FLOAT) + return G_TOKEN_FLOAT; + + *contrast = scanner->value.v_float; + + return G_TOKEN_NONE; +} + +static guint +theme_parse_sunkenmenubar(GtkSettings *settings, + GScanner *scanner, + guint8 *sunken) +{ + guint token; + + /* Skip 'sunkenmenubar' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *sunken = scanner->value.v_int; + + return G_TOKEN_NONE; +} + +static guint +theme_parse_progressbarstyle(GtkSettings *settings, + GScanner *scanner, + guint8 *progressbarstyle) +{ + guint token; + + /* Skip 'sunkenmenubar' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *progressbarstyle = scanner->value.v_int; + + return G_TOKEN_NONE; +} + +static guint +theme_parse_menubarstyle(GtkSettings *settings, + GScanner *scanner, + guint8 *menubarstyle) +{ + guint token; + + /* Skip 'menubarstyle' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *menubarstyle = scanner->value.v_int; + + return G_TOKEN_NONE; +} + +static guint +theme_parse_menuitemstyle(GtkSettings *settings, + GScanner *scanner, + guint8 *menuitemstyle) +{ + guint token; + + /* Skip 'sunkenmenubar' */ + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *menuitemstyle = scanner->value.v_int; + + return G_TOKEN_NONE; +} + +static guint +theme_parse_listviewitemstyle(GtkSettings *settings, + GScanner *scanner, + guint8 *listviewitemstyle) +{ + guint token; + + token = g_scanner_get_next_token(scanner); + + token = g_scanner_get_next_token(scanner); + + if (token != G_TOKEN_EQUAL_SIGN) + return G_TOKEN_EQUAL_SIGN; + + token = g_scanner_get_next_token(scanner); + if (token != G_TOKEN_INT) + return G_TOKEN_INT; + + *listviewitemstyle = scanner->value.v_int; + + return G_TOKEN_NONE; +} + +static guint +clearlooks_rc_style_parse (GtkRcStyle *rc_style, + GtkSettings *settings, + GScanner *scanner) + +{ + static GQuark scope_id = 0; + ClearlooksRcStyle *clearlooks_style = CLEARLOOKS_RC_STYLE (rc_style); + + guint old_scope; + guint token; + guint i; + + /* Set up a new scope in this scanner. */ + + if (!scope_id) + scope_id = g_quark_from_string("clearlooks_theme_engine"); + + /* If we bail out due to errors, we *don't* reset the scope, so the + * error messaging code can make sense of our tokens. + */ + old_scope = g_scanner_set_scope(scanner, scope_id); + + /* Now check if we already added our symbols to this scope + * (in some previous call to clearlooks_rc_style_parse for the + * same scanner. + */ + + if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name)) + { + g_scanner_freeze_symbol_table(scanner); + for (i = 0; i < G_N_ELEMENTS (theme_symbols); i++) + g_scanner_scope_add_symbol(scanner, scope_id, + theme_symbols[i].name, + GINT_TO_POINTER(theme_symbols[i].token)); + g_scanner_thaw_symbol_table(scanner); + } + + /* We're ready to go, now parse the top level */ + + token = g_scanner_peek_next_token(scanner); + while (token != G_TOKEN_RIGHT_CURLY) + { + switch (token) + { + case TOKEN_SPOTCOLOR: + token = theme_parse_color(settings, scanner, &clearlooks_style->spot_color); + clearlooks_style->has_spot_color = TRUE; + break; + case TOKEN_CONTRAST: + token = theme_parse_contrast(settings, scanner, &clearlooks_style->contrast); + break; + case TOKEN_SUNKENMENU: + token = theme_parse_sunkenmenubar(settings, scanner, &clearlooks_style->sunkenmenubar); + break; + case TOKEN_PROGRESSBARSTYLE: + token = theme_parse_progressbarstyle(settings, scanner, &clearlooks_style->progressbarstyle); + break; + case TOKEN_MENUBARSTYLE: + token = theme_parse_menubarstyle(settings, scanner, &clearlooks_style->menubarstyle); + break; + case TOKEN_MENUITEMSTYLE: + token = theme_parse_menuitemstyle(settings, scanner, &clearlooks_style->menuitemstyle); + break; + case TOKEN_LISTVIEWITEMSTYLE: + token = theme_parse_listviewitemstyle(settings, scanner, &clearlooks_style->listviewitemstyle); + break; + default: + g_scanner_get_next_token(scanner); + token = G_TOKEN_RIGHT_CURLY; + break; + } + + if (token != G_TOKEN_NONE) + return token; + + token = g_scanner_peek_next_token(scanner); + } + + g_scanner_get_next_token(scanner); + + g_scanner_set_scope(scanner, old_scope); + + return G_TOKEN_NONE; +} + +static void +clearlooks_rc_style_merge (GtkRcStyle *dest, + GtkRcStyle *src) +{ + ClearlooksRcStyle *dest_w, *src_w; + + parent_class->merge (dest, src); + + if (!CLEARLOOKS_IS_RC_STYLE (src)) + return; + + src_w = CLEARLOOKS_RC_STYLE (src); + dest_w = CLEARLOOKS_RC_STYLE (dest); + + dest_w->contrast = src_w->contrast; + dest_w->sunkenmenubar = src_w->sunkenmenubar; + dest_w->progressbarstyle = src_w->progressbarstyle; + dest_w->menubarstyle = src_w->menubarstyle; + dest_w->menuitemstyle = src_w->menuitemstyle; + dest_w->listviewitemstyle = src_w->listviewitemstyle; + + if (src_w->has_spot_color) + { + dest_w->has_spot_color = TRUE; + dest_w->spot_color = src_w->spot_color; + } +} + + +/* Create an empty style suitable to this RC style + */ +static GtkStyle * +clearlooks_rc_style_create_style (GtkRcStyle *rc_style) +{ + return GTK_STYLE (g_object_new (CLEARLOOKS_TYPE_STYLE, NULL)); +} diff --git a/libs/clearlooks/clearlooks_rc_style.h b/libs/clearlooks/clearlooks_rc_style.h new file mode 100644 index 0000000000..bd8e0ca05d --- /dev/null +++ b/libs/clearlooks/clearlooks_rc_style.h @@ -0,0 +1,57 @@ +/* Clearlooks Theme Engine + * Copyright (C) 2005 Richard Stellingwerff. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by Owen Taylor <otaylor@redhat.com> + * and by Alexander Larsson <alexl@redhat.com> + * Modified by Richard Stellingwerff <remenic@gmail.com> + */ + +#include <gtk/gtkrc.h> + +typedef struct _ClearlooksRcStyle ClearlooksRcStyle; +typedef struct _ClearlooksRcStyleClass ClearlooksRcStyleClass; + +extern GType clearlooks_type_rc_style; + +#define CLEARLOOKS_TYPE_RC_STYLE clearlooks_type_rc_style +#define CLEARLOOKS_RC_STYLE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CLEARLOOKS_TYPE_RC_STYLE, ClearlooksRcStyle)) +#define CLEARLOOKS_RC_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLEARLOOKS_TYPE_RC_STYLE, ClearlooksRcStyleClass)) +#define CLEARLOOKS_IS_RC_STYLE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CLEARLOOKS_TYPE_RC_STYLE)) +#define CLEARLOOKS_IS_RC_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLEARLOOKS_TYPE_RC_STYLE)) +#define CLEARLOOKS_RC_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLEARLOOKS_TYPE_RC_STYLE, ClearlooksRcStyleClass)) + +struct _ClearlooksRcStyle +{ + GtkRcStyle parent_instance; + + GdkColor spot_color; + gboolean has_spot_color; + double contrast; + guint8 sunkenmenubar; + guint8 progressbarstyle; + guint8 menubarstyle; + guint8 menuitemstyle; + guint8 listviewitemstyle; +}; + +struct _ClearlooksRcStyleClass +{ + GtkRcStyleClass parent_class; +}; + +void clearlooks_rc_style_register_type (GTypeModule *module); diff --git a/libs/clearlooks/clearlooks_style.c b/libs/clearlooks/clearlooks_style.c new file mode 100644 index 0000000000..241f14c6e4 --- /dev/null +++ b/libs/clearlooks/clearlooks_style.c @@ -0,0 +1,2657 @@ +#include <gtk/gtk.h> + +#include "clearlooks_style.h" +#include "clearlooks_rc_style.h" +#include "clearlooks_draw.h" + +#include <math.h> +#include <string.h> + +#include "bits.c" +#include "support.h" +//#include "config.h" + +/* #define DEBUG 1 */ + +#define SCALE_SIZE 5 + +#define DETAIL(xx) ((detail) && (!strcmp(xx, detail))) +#define COMPARE_COLORS(a,b) (a.red == b.red && a.green == b.green && a.blue == b.blue) + +#define DRAW_ARGS GtkStyle *style, \ + GdkWindow *window, \ + GtkStateType state_type, \ + GtkShadowType shadow_type, \ + GdkRectangle *area, \ + GtkWidget *widget, \ + const gchar *detail, \ + gint x, \ + gint y, \ + gint width, \ + gint height + +static GdkGC *realize_color (GtkStyle * style, GdkColor * color); +static GtkStyleClass *parent_class; +static GList *progressbars = NULL; +static gint8 pboffset = 10; +static int timer_id = 0; + +static void cl_progressbar_remove (gpointer data) +{ + if (g_list_find (progressbars, data) == NULL) + return; + + progressbars = g_list_remove (progressbars, data); + g_object_unref (data); + + if (g_list_first(progressbars) == NULL) { + g_source_remove(timer_id); + timer_id = 0; + } +} + +static void update_progressbar (gpointer data, gpointer user_data) +{ + gfloat fraction; + + if (data == NULL) + return; + + fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (data)); + + /* update only if not filled */ + if (fraction < 1.0) + gtk_widget_queue_resize ((GtkWidget*)data); + + if (fraction >= 1.0 || GTK_PROGRESS (data)->activity_mode) + cl_progressbar_remove (data); +} + +static gboolean timer_func (gpointer data) +{ + g_list_foreach (progressbars, update_progressbar, NULL); + if (--pboffset < 0) pboffset = 9; + return (g_list_first(progressbars) != NULL); +} + +static gboolean cl_progressbar_known(gconstpointer data) +{ + return (g_list_find (progressbars, data) != NULL); +} + + +static void cl_progressbar_add (gpointer data) +{ + if (!GTK_IS_PROGRESS_BAR (data)) + return; + + progressbars = g_list_append (progressbars, data); + + g_object_ref (data); + g_signal_connect ((GObject*)data, "unrealize", G_CALLBACK (cl_progressbar_remove), data); + + if (timer_id == 0) + timer_id = g_timeout_add (100, timer_func, NULL); +} + +static GdkColor * +clearlooks_get_spot_color (ClearlooksRcStyle *clearlooks_rc) +{ + GtkRcStyle *rc = GTK_RC_STYLE (clearlooks_rc); + + if (clearlooks_rc->has_spot_color) + return &clearlooks_rc->spot_color; + else + return &rc->base[GTK_STATE_SELECTED]; +} + +/**************************************************************************/ + +/* used for optionmenus... */ +static void +draw_tab (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + GtkShadowType shadow_type, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint x, + gint y, + gint width, + gint height) +{ +#define ARROW_SPACE 2 +#define ARROW_LINE_HEIGHT 2 +#define ARROW_LINE_WIDTH 5 + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GtkRequisition indicator_size; + GtkBorder indicator_spacing; + gint arrow_height; + + option_menu_get_props (widget, &indicator_size, &indicator_spacing); + + indicator_size.width += (indicator_size.width % 2) - 1; + arrow_height = indicator_size.width / 2 + 2; + + x += (width - indicator_size.width) / 2; + y += height/2; + + if (state_type == GTK_STATE_INSENSITIVE) + { + draw_arrow (window, style->light_gc[state_type], area, + GTK_ARROW_UP, 1+x, 1+y-arrow_height, + indicator_size.width, arrow_height); + + draw_arrow (window, style->light_gc[state_type], area, + GTK_ARROW_DOWN, 1+x, 1+y+1, + indicator_size.width, arrow_height); + } + + draw_arrow (window, style->fg_gc[state_type], area, + GTK_ARROW_UP, x, y-arrow_height, + indicator_size.width, arrow_height); + + draw_arrow (window, style->fg_gc[state_type], area, + GTK_ARROW_DOWN, x, y+1, + indicator_size.width, arrow_height); +} + +static void +clearlooks_draw_arrow (GtkStyle *style, + GdkWindow *window, + GtkStateType state, + GtkShadowType shadow, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + GtkArrowType arrow_type, + gboolean fill, + gint x, + gint y, + gint width, + gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + gint original_width, original_x; + GdkGC *gc; + + sanitize_size (window, &width, &height); + + if (is_combo_box (widget)) + { + width = 7; + height = 5; + x+=2; + y+=4; + if (state == GTK_STATE_INSENSITIVE) + { + draw_arrow (window, style->light_gc[state], area, + GTK_ARROW_UP, 1+x, 1+y-height, + width, height); + + draw_arrow (window, style->light_gc[state], area, + GTK_ARROW_DOWN, 1+x, 1+y+1, + width, height); + } + + draw_arrow (window, style->fg_gc[state], area, + GTK_ARROW_UP, x, y-height, + width, height); + + draw_arrow (window, style->fg_gc[state], area, + GTK_ARROW_DOWN, x, y+1, + width, height); + + return; + } + + original_width = width; + original_x = x; + + /* Make spinbutton arrows and arrows in menus + * slightly larger to get the right pixels drawn */ + if (DETAIL ("spinbutton")) + height += 1; + + if (DETAIL("menuitem")) + { + width = 6; + height = 7; + } + + /* Compensate arrow position for "sunken" look */ + if (DETAIL ("spinbutton") && arrow_type == GTK_ARROW_DOWN && + style->xthickness > 2 && style->ythickness > 2) + y -= 1; + + if (widget && widget->parent && GTK_IS_COMBO (widget->parent->parent)) + { + width -= 2; + height -=2; + x++; + } + + calculate_arrow_geometry (arrow_type, &x, &y, &width, &height); + + if (DETAIL ("menuitem")) + x = original_x + original_width - width; + + if (DETAIL ("spinbutton") && (arrow_type == GTK_ARROW_DOWN)) + y += 1; + + if (state == GTK_STATE_INSENSITIVE) + draw_arrow (window, style->light_gc[state], area, arrow_type, x + 1, y + 1, width, height); + + gc = style->fg_gc[state]; + + draw_arrow (window, gc, area, arrow_type, x, y, width, height); +} + + +static void +draw_flat_box (DRAW_ARGS) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + if (detail && + clearlooks_style->listviewitemstyle == 1 && + state_type == GTK_STATE_SELECTED && ( + !strncmp ("cell_even", detail, strlen ("cell_even")) || + !strncmp ("cell_odd", detail, strlen ("cell_odd")))) + { + GdkGC *gc; + GdkColor lower_color; + GdkColor *upper_color; + + if (GTK_WIDGET_HAS_FOCUS (widget)) + { + gc = style->base_gc[state_type]; + upper_color = &style->base[state_type]; + } + else + { + gc = style->base_gc[GTK_STATE_ACTIVE]; + upper_color = &style->base[GTK_STATE_ACTIVE]; + } + + if (GTK_IS_TREE_VIEW (widget) && 0) + { + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + + if (gtk_tree_selection_count_selected_rows (sel) > 1) + { + parent_class->draw_flat_box (style, window, state_type, shadow_type, + area, widget, detail, + x, y, width, height); + return; + } + } + + shade (upper_color, &lower_color, 0.8); + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + draw_hgradient (window, gc, style, + x, y, width, height, upper_color, &lower_color); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + } + else + { + parent_class->draw_flat_box (style, window, state_type, + shadow_type, + area, widget, detail, + x, y, width, height); + } +} +/**************************************************************************/ + +static void +draw_shadow (DRAW_ARGS) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + CLRectangle r; + + GdkGC *outer_gc = clearlooks_style->shade_gc[4]; + GdkGC *gc1 = NULL; + GdkGC *gc2 = NULL; + gint thickness_light; + gint thickness_dark; + gboolean interior_focus = FALSE; + +#if DEBUG + printf("draw_shadow: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + if (widget == NULL) + { + gdk_draw_rectangle (window, outer_gc, FALSE, + x, y, width - 1, height - 1); + return; + } + + if ((width == -1) && (height == -1)) + gdk_window_get_size (window, &width, &height); + else if (width == -1) + gdk_window_get_size (window, &width, NULL); + else if (height == -1) + gdk_window_get_size (window, NULL, &height); + + cl_rectangle_reset (&r, style); + + if (DETAIL ("frame") && widget->parent && + GTK_IS_STATUSBAR (widget->parent)) + { + gtk_style_apply_default_background (style, window,widget && !GTK_WIDGET_NO_WINDOW (widget), + state_type, area, x, y, width, height); + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], area); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], area); + } + + gdk_draw_line (window, clearlooks_style->shade_gc[3], + x, y, x + width, y); + gdk_draw_line (window, clearlooks_style->shade_gc[0], + x, y + 1, x + width, y + 1); + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], NULL); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], NULL); + } + } + else if (detail && !strcmp (detail, "entry")) + { + if ( widget->parent && (GTK_IS_COMBO_BOX_ENTRY (widget->parent) || + GTK_IS_SPIN_BUTTON(widget) || + GTK_IS_COMBO (widget->parent))) + { + cl_draw_combobox_entry (style, window, GTK_WIDGET_STATE(widget), shadow_type, area, widget, detail, x, y, width, height); + } + else + { + cl_draw_entry (style, window, GTK_WIDGET_STATE(widget), shadow_type, area, widget, detail, x, y, width, height); + } + } + else if (DETAIL ("viewport") || DETAIL ("scrolled_window")) + { + gdk_draw_rectangle (window, clearlooks_style->shade_gc[4], FALSE, + x, y, width - 1, height - 1); + } + else + { + if (DETAIL ("menuitem")) + outer_gc = clearlooks_style->spot3_gc; + else + outer_gc = clearlooks_style->shade_gc[4]; + + if (shadow_type == GTK_SHADOW_IN) + gdk_draw_rectangle (window, outer_gc, FALSE, + x, y, width - 1, height - 1); + else if (shadow_type == GTK_SHADOW_OUT) + { + gdk_draw_rectangle (window, outer_gc, FALSE, + x, y, width - 1, height - 1); + gdk_draw_line (window, style->light_gc[state_type], + x+1, y+1, x+width-2, y+1); + gdk_draw_line (window, style->light_gc[state_type], + x+1, y+1, x+1, y+height-2); + } + else if (shadow_type == GTK_SHADOW_ETCHED_IN) + { + GdkGC *a = clearlooks_style->shade_gc[(shadow_type == GTK_SHADOW_ETCHED_IN) ? 0 : 3]; + GdkGC *b = clearlooks_style->shade_gc[(shadow_type == GTK_SHADOW_ETCHED_IN) ? 3 : 0]; + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.bordergc = a; + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x+1, y+1, width-1, height-1, &r); + cl_rectangle_reset_clip_rectangle (&r); + + r.bordergc = b; + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width-1, height-1, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (shadow_type == GTK_SHADOW_ETCHED_IN) + { + GdkGC *a = clearlooks_style->shade_gc[(shadow_type == GTK_SHADOW_ETCHED_IN) ? 3 : 0]; + GdkGC *b = clearlooks_style->shade_gc[(shadow_type == GTK_SHADOW_ETCHED_IN) ? 0 : 3]; + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.bordergc = a; + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x+1, y+1, width-1, height-1, &r); + cl_rectangle_reset_clip_rectangle (&r); + + r.bordergc = b; + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width-1, height-1, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else + parent_class->draw_shadow (style, window, state_type, shadow_type, + area, widget, detail, + x, y, width, height); + } +} + +#define GDK_RECTANGLE_SET(rect,a,b,c,d) rect.x = a; \ + rect.y = b; \ + rect.width = c; \ + rect.height = d; + + +static void +draw_box_gap (DRAW_ARGS, + GtkPositionType gap_side, + gint gap_x, + gint gap_width) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + CLRectangle r; + + GdkRegion *area_region = NULL, + *gap_region = NULL; + GdkRectangle light_rect; + GdkRectangle dark_rect; + +#if DEBUG + printf("draw_box_gap: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + cl_rectangle_reset (&r, style); + + r.bordergc = clearlooks_style->shade_gc[5]; + + r.topleft = style->light_gc[state_type]; + r.bottomright = clearlooks_style->shade_gc[1]; + + if (area) + area_region = gdk_region_rectangle (area); + else + { + GdkRectangle tmp = { x, y, width, height }; + area_region = gdk_region_rectangle (&tmp); + } + + switch (gap_side) + { + case GTK_POS_TOP: + { + GdkRectangle rect = { x+gap_x+1, y, gap_width-2, 2 }; + gap_region = gdk_region_rectangle (&rect); + + GDK_RECTANGLE_SET (light_rect, x+gap_x+1, y, x+gap_x+1, y+1); + GDK_RECTANGLE_SET (dark_rect, x+gap_x+gap_width-2, y, x+gap_x+gap_width-2, y); + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_ROUND); + + break; + } + case GTK_POS_BOTTOM: + { + GdkRectangle rect = { x+gap_x+1, y+height-2, gap_width-2, 2 }; + gap_region = gdk_region_rectangle (&rect); + + GDK_RECTANGLE_SET (light_rect, x+gap_x+1, y+height-2, x+gap_x+1, y+height-1); + GDK_RECTANGLE_SET (dark_rect, x+gap_x+gap_width-2, y+height-2, x+gap_x+gap_width-2, y+height-1); + + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_NONE); + + break; + } + case GTK_POS_LEFT: + { + GdkRectangle rect = { x, y+gap_x+1, 2, gap_width-2 }; + gap_region = gdk_region_rectangle (&rect); + + GDK_RECTANGLE_SET (light_rect, x, y+gap_x+1, x+1, y+gap_x+1); + GDK_RECTANGLE_SET (dark_rect, x, y+gap_x+gap_width-2, x, y+gap_x+gap_width-2); + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + break; + } + case GTK_POS_RIGHT: + { + GdkRectangle rect = { x+width-2, y+gap_x+1, 2, gap_width-2 }; + gap_region = gdk_region_rectangle (&rect); + + GDK_RECTANGLE_SET (light_rect, x+width-2, y+gap_x+1, x+width-1, y+gap_x+1); + GDK_RECTANGLE_SET (dark_rect, x+width-2, y+gap_x+gap_width-2, x+width-1, y+gap_x+gap_width-2); + + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_NONE); + break; + } + } + + gdk_region_subtract (area_region, gap_region); + + gdk_gc_set_clip_region (r.bordergc, area_region); + gdk_gc_set_clip_region (r.topleft, area_region); + gdk_gc_set_clip_region (r.bottomright, area_region); + + gdk_region_destroy (area_region); + gdk_region_destroy (gap_region); + + gdk_draw_rectangle (window, style->bg_gc[state_type], TRUE, x, y, width, height); + + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + + gdk_gc_set_clip_region (r.bordergc, NULL); + gdk_gc_set_clip_region (r.topleft, NULL); + gdk_gc_set_clip_region (r.bottomright, NULL); + + /* it's a semi hack */ + gdk_draw_line (window, style->light_gc[state_type], + light_rect.x, light_rect.y, + light_rect.width, light_rect.height); + + gdk_draw_line (window, clearlooks_style->shade_gc[1], + dark_rect.x, dark_rect.y, + dark_rect.width, dark_rect.height); +} + +/**************************************************************************/ + +static void +draw_extension (DRAW_ARGS, GtkPositionType gap_side) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int my_state_type = (state_type == GTK_STATE_ACTIVE) ? 2 : 0; + CLRectangle r; + +#if DEBUG + printf("draw_extension: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + if (DETAIL ("tab")) + { + GdkRectangle new_area; + GdkColor tmp_color; + + cl_rectangle_set_button (&r, style, state_type, FALSE, FALSE, + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + + if (state_type == GTK_STATE_ACTIVE) + shade (&style->bg[state_type], &tmp_color, 1.08); + else + shade (&style->bg[state_type], &tmp_color, 1.05); + + if (area) + { + new_area = *area; + } + else + { + new_area.x = x; + new_area.y = y; + new_area.width = width; + new_area.height = height; + } + + switch (gap_side) + { + case GTK_POS_BOTTOM: + height+=2; + new_area.y = y; + new_area.height = height-2; + r.gradient_type = CL_GRADIENT_VERTICAL; + cl_rectangle_set_gradient (&r.fill_gradient, &tmp_color, &style->bg[state_type]); + cl_rectangle_set_gradient (&r.border_gradient, + &clearlooks_style->border[CL_BORDER_UPPER+my_state_type], + &clearlooks_style->border[CL_BORDER_LOWER+my_state_type]); + break; + case GTK_POS_TOP: + y-=2; + height+=2; + new_area.y = y+2; + new_area.height = height; + r.gradient_type = CL_GRADIENT_VERTICAL; + cl_rectangle_set_gradient (&r.fill_gradient, &style->bg[state_type], &tmp_color); + cl_rectangle_set_gradient (&r.border_gradient, + &clearlooks_style->border[CL_BORDER_LOWER+my_state_type], + &clearlooks_style->border[CL_BORDER_UPPER+my_state_type]); + break; + case GTK_POS_LEFT: + x-=2; + width+=2; + new_area.x = x+2; + new_area.width = width; + r.gradient_type = CL_GRADIENT_HORIZONTAL; + cl_rectangle_set_gradient (&r.fill_gradient, &style->bg[state_type], &tmp_color); + cl_rectangle_set_gradient (&r.border_gradient, + &clearlooks_style->border[CL_BORDER_LOWER+my_state_type], + &clearlooks_style->border[CL_BORDER_UPPER+my_state_type]); + break; + case GTK_POS_RIGHT: + width+=2; + new_area.x = x; + new_area.width = width-2; + r.gradient_type = CL_GRADIENT_HORIZONTAL; + cl_rectangle_set_gradient (&r.fill_gradient, &tmp_color, &style->bg[state_type]); + cl_rectangle_set_gradient (&r.border_gradient, + &clearlooks_style->border[CL_BORDER_UPPER+my_state_type], + &clearlooks_style->border[CL_BORDER_LOWER+my_state_type]); + break; + } + + r.topleft = style->light_gc[state_type]; + r.bottomright = (state_type == GTK_STATE_NORMAL) ? clearlooks_style->shade_gc[1] : NULL; + + cl_rectangle_set_clip_rectangle (&r, &new_area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + + /* draw the selection stripe */ + if (state_type != GTK_STATE_ACTIVE) { + cl_rectangle_set_gradient (&r.fill_gradient, NULL, NULL); + r.fillgc = clearlooks_style->spot2_gc; + + switch (gap_side) + { + case GTK_POS_BOTTOM: + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_NONE); + cl_rectangle_set_gradient (&r.border_gradient, &clearlooks_style->spot3, &clearlooks_style->spot2); + r.gradient_type = CL_GRADIENT_VERTICAL; + + cl_rectangle_set_clip_rectangle (&r, &new_area); + cl_draw_rectangle (window, widget, style, x, y, width, 3, &r); + cl_rectangle_reset_clip_rectangle (&r); + break; + case GTK_POS_TOP: + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_ROUND); + cl_rectangle_set_gradient (&r.border_gradient, &clearlooks_style->spot2, &clearlooks_style->spot3); + r.gradient_type = CL_GRADIENT_VERTICAL; + + cl_rectangle_set_clip_rectangle (&r, &new_area); + cl_draw_rectangle (window, widget, style, x, y + height - 3, width, 3, &r); + cl_rectangle_reset_clip_rectangle (&r); + break; + case GTK_POS_LEFT: + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + cl_rectangle_set_gradient (&r.border_gradient, &clearlooks_style->spot2, &clearlooks_style->spot3); + r.gradient_type = CL_GRADIENT_HORIZONTAL; + + cl_rectangle_set_clip_rectangle (&r, &new_area); + cl_draw_rectangle (window, widget, style, x + width - 3, y, 3, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + break; + case GTK_POS_RIGHT: + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_NONE); + cl_rectangle_set_gradient (&r.border_gradient, &clearlooks_style->spot3, &clearlooks_style->spot2); + r.gradient_type = CL_GRADIENT_HORIZONTAL; + + cl_rectangle_set_clip_rectangle (&r, &new_area); + cl_draw_rectangle (window, widget, style, x, y, 3, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + break; + } + } + + + } + else + { + parent_class->draw_extension (style, window, state_type, shadow_type, area, + widget, detail, x, y, width, height, + gap_side); + } +} + + +/**************************************************************************/ + +static void +draw_handle (DRAW_ARGS, GtkOrientation orientation) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + gint xx, yy; + gint xthick, ythick; + GdkGC *light_gc, *dark_gc; + GdkRectangle rect; + GdkRectangle dest; + gint intersect; + gint h; + int i; + int n_lines; + int offset; + +#if DEBUG + printf("draw_handle: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + if (state_type == GTK_STATE_PRELIGHT) + gtk_style_apply_default_background (style, window, + widget && !GTK_WIDGET_NO_WINDOW (widget), + state_type, area, x, y, width, height); + + /* orientation is totally bugged, but this actually works... */ + orientation = (width > height) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; + + if (!strcmp (detail, "paned")) + { + /* we want to ignore the shadow border in paned widgets */ + xthick = 0; + ythick = 0; + } + else + { + xthick = style->xthickness; + ythick = style->ythickness; + } + + if ( ((DETAIL ("handlebox") && widget && GTK_IS_HANDLE_BOX (widget)) || DETAIL ("dockitem")) && + orientation == GTK_ORIENTATION_VERTICAL ) + { + /* The line in the toolbar */ + + light_gc = style->light_gc[state_type]; + dark_gc = clearlooks_style->shade_gc[3]; + + if (area) + { + gdk_gc_set_clip_rectangle (light_gc, area); + gdk_gc_set_clip_rectangle (dark_gc, area); + } + + if (area) + { + gdk_gc_set_clip_rectangle (light_gc, NULL); + gdk_gc_set_clip_rectangle (dark_gc, NULL); + } + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], area); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], area); + } + + gdk_draw_line (window, clearlooks_style->shade_gc[0], x, y, x + width, y); + gdk_draw_line (window, clearlooks_style->shade_gc[3], x, y + height - 1, x + width, y + height - 1); + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], NULL); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], NULL); + } + } + + light_gc = clearlooks_style->shade_gc[0]; + dark_gc = clearlooks_style->shade_gc[4]; + + rect.x = x + xthick; + rect.y = y + ythick; + rect.width = width - (xthick * 2); + rect.height = height - (ythick * 2); + + if (area) + intersect = gdk_rectangle_intersect (area, &rect, &dest); + else + { + intersect = TRUE; + dest = rect; + } + + if (!intersect) + return; + + gdk_gc_set_clip_rectangle (light_gc, &dest); + gdk_gc_set_clip_rectangle (dark_gc, &dest); + + n_lines = (!strcmp (detail, "paned")) ? 21 : 11; + + if (orientation == GTK_ORIENTATION_VERTICAL) + { + h = width - 2 * xthick; + h = MAX (3, h - 6); + + xx = x + (width - h) / 2; + offset = (height - 2*ythick - 2*n_lines)/2 + 1; + if (offset < 0) + offset = 0; + + for (i = 0, yy = y + ythick + offset; yy <= (y + height - ythick - 1) && i < n_lines; yy += 2, i++) + { + gdk_draw_line (window, dark_gc, xx, yy, xx + h, yy); + gdk_draw_line (window, light_gc, xx, yy + 1, xx + h, yy + 1); + } + } + else + { + h = height - 2 * ythick; + h = MAX (3, h - 6); + + yy = y + (height - h) / 2; + offset = (width - 2*xthick - 2*n_lines)/2 + 1; + if (offset < 0) + offset = 0; + + for (i = 0, xx = x + xthick + offset; i < n_lines; xx += 2, i++) + { + gdk_draw_line (window, dark_gc, xx, yy, xx, yy + h); + gdk_draw_line (window, light_gc, xx + 1, yy, xx + 1, yy + h); + } + } + + gdk_gc_set_clip_rectangle (light_gc, NULL); + gdk_gc_set_clip_rectangle (dark_gc, NULL); +} + +/**************************************************************************/ + +static void +draw_box (DRAW_ARGS) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + CLRectangle r; + gboolean false_size = FALSE; + +#ifdef DEBUG + printf("draw_box: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (style != NULL); + g_return_if_fail (window != NULL); + + if (width == -1 || height == -1) + false_size = TRUE; + + if ((width == -1) && (height == -1)) + gdk_window_get_size (window, &width, &height); + else if (width == -1) + gdk_window_get_size (window, &width, NULL); + else if (height == -1) + gdk_window_get_size (window, NULL, &height); + + cl_rectangle_reset (&r, style); + + if (widget == NULL) + return; + + /* listview headers */ + if (widget && DETAIL ("button") && widget->parent && + (GTK_IS_TREE_VIEW(widget->parent) || + GTK_IS_CLIST (widget->parent) || + strcmp(G_OBJECT_TYPE_NAME (widget->parent), "ETree") == 0)) + { + cl_draw_treeview_header (style, window, state_type, shadow_type, + area, widget, detail, x, y, width, height); + } + else if (detail && (!strcmp (detail, "button") || + !strcmp (detail, "buttondefault"))) + { + if (GTK_IS_COMBO_BOX_ENTRY(widget->parent) || GTK_IS_COMBO(widget->parent)) + { + cl_draw_combobox_button (style, window, state_type, shadow_type, + area, widget, + detail, x, y, width, height); + } + else + { + cl_draw_button (style, window, state_type, shadow_type, area, widget, + detail, x, y, width, height); + } + } + else if (detail && ( + !strcmp (detail, "spinbutton_up") || + !strcmp (detail, "spinbutton_down") || + !strcmp (detail, "spinbutton"))) + { + cl_draw_spinbutton (style, window, state_type, shadow_type, area, + widget, detail, x, y, width, height); + } + else if (detail && ( + !strcmp (detail, "hscale") || !strcmp (detail, "vscale"))) + { + cl_rectangle_set_button (&r, style, state_type, + GTK_WIDGET_HAS_DEFAULT (widget), GTK_WIDGET_HAS_FOCUS (widget), + CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_ROUND, CL_CORNER_ROUND); + + if (!strcmp (detail, "hscale") || !strcmp (detail, "vscale")) + { + r.fill_gradient.to = &clearlooks_style->shade[2]; + r.bottomright = clearlooks_style->shade_gc[2]; + } + + cl_set_corner_sharpness (detail, widget, &r); + + if (!strcmp (detail, "spinbutton_up")) + { + r.border_gradient.to = r.border_gradient.from; + height++; + gtk_style_apply_default_background (style, window, FALSE, state_type, + area, x, y, width, height); + } + else if (!strcmp (detail, "spinbutton_down")) + { + r.border_gradient.to = r.border_gradient.from; + gtk_style_apply_default_background (style, window, FALSE, state_type, + area, x, y, width, height); + } + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x+1, y+1, width-2, height-2, &r); + cl_draw_shadow (window, widget, style, x+1, y+1, width-2, height-2, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (DETAIL ("trough") && GTK_IS_PROGRESS_BAR (widget)) + { + GdkPoint points[4] = { {x,y}, {x+width-1,y}, {x,y+height-1}, {x+width-1,y+height-1} }; + + gdk_draw_points (window, style->bg_gc[state_type], points, 4); + + r.bordergc = clearlooks_style->shade_gc[5]; + r.fillgc = clearlooks_style->shade_gc[2]; + + cl_rectangle_set_corners (&r, CL_CORNER_NARROW, CL_CORNER_NARROW, + CL_CORNER_NARROW, CL_CORNER_NARROW); + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (DETAIL ("trough") && + (GTK_IS_VSCALE (widget) || GTK_IS_HSCALE (widget))) + { + GdkGC *inner = clearlooks_style->shade_gc[3], + *outer = clearlooks_style->shade_gc[5], + *shadow = clearlooks_style->shade_gc[4]; + GdkColor upper_color = *clearlooks_get_spot_color (CLEARLOOKS_RC_STYLE (style->rc_style)), + lower_color; + + GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (widget)); + + GtkOrientation orientation = GTK_RANGE (widget)->orientation; + + gint fill_size = (orientation ? height : width) * + (1 / ((adjustment->upper - adjustment->lower) / + (adjustment->value - adjustment->lower))); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + y += (height - SCALE_SIZE) / 2; + height = SCALE_SIZE; + } + else + { + x += (width - SCALE_SIZE) / 2; + width = SCALE_SIZE; + } + + if (state_type == GTK_STATE_INSENSITIVE) + { + outer = clearlooks_style->shade_gc[4]; + inner = clearlooks_style->shade_gc[2]; + shadow = clearlooks_style->shade_gc[3]; + } + + cl_rectangle_init (&r, inner, outer, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE ); + + r.topleft = shadow; + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + + /* DRAW FILL */ + shade (&upper_color, &lower_color, 1.3); + + r.bordergc = clearlooks_style->spot3_gc; + r.fillgc = style->bg_gc[state_type]; + + r.gradient_type = (orientation == GTK_ORIENTATION_HORIZONTAL ) ? CL_GRADIENT_VERTICAL + : CL_GRADIENT_HORIZONTAL; + + cl_rectangle_set_gradient (&r.fill_gradient, &upper_color, &lower_color); + + cl_rectangle_set_clip_rectangle (&r, area); + if (orientation == GTK_ORIENTATION_HORIZONTAL && fill_size > 1) + { + if (gtk_range_get_inverted(GTK_RANGE(widget)) != (get_direction(widget) == GTK_TEXT_DIR_RTL)) + cl_draw_rectangle (window, widget, style, x+width-fill_size, y, fill_size, height, &r); + else + cl_draw_rectangle (window, widget, style, x, y, fill_size, height, &r); + } + else if (fill_size > 1) + { + if (gtk_range_get_inverted (GTK_RANGE (widget))) + cl_draw_rectangle (window, widget, style, x, y+height-fill_size, width, fill_size, &r); + else + cl_draw_rectangle (window, widget, style, x, y, width, fill_size, &r); + } + cl_rectangle_reset_clip_rectangle (&r); + } + else if (DETAIL ("trough")) + { + GdkGC *inner = clearlooks_style->shade_gc[3], + *outer = clearlooks_style->shade_gc[5]; + + cl_rectangle_init (&r, inner, outer, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE ); + + if (GTK_RANGE (widget)->orientation == GTK_ORIENTATION_VERTICAL) + { + y+=1; + height-=2; + } + else + { + x+=1; + width-=2; + } + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (detail && (!strcmp (detail, "vscrollbar") || + !strcmp (detail, "hscrollbar") || + !strcmp (detail, "stepper"))) + { + ClScrollButtonType button_type = CL_SCROLLBUTTON_OTHER; + gboolean horizontal = TRUE; + + if (GTK_IS_VSCROLLBAR(widget)) + { + if (y == widget->allocation.y) + button_type = CL_SCROLLBUTTON_BEGIN; + else if (y+height == widget->allocation.y+widget->allocation.height) + button_type = CL_SCROLLBUTTON_END; + + horizontal = FALSE; + } + else if (GTK_IS_HSCROLLBAR(widget)) + { + if (x == widget->allocation.x) + button_type = CL_SCROLLBUTTON_BEGIN; + else if (x+width == widget->allocation.x+widget->allocation.width) + button_type = CL_SCROLLBUTTON_END; + } + + cl_rectangle_set_button (&r, style, state_type, FALSE, FALSE, 0,0,0,0); + + cl_rectangle_set_gradient (&r.fill_gradient, NULL, NULL); + cl_rectangle_set_gradient (&r.fill_gradient, &clearlooks_style->inset_light[state_type], + &clearlooks_style->inset_dark[state_type]); + + + r.gradient_type = horizontal ? CL_GRADIENT_VERTICAL + : CL_GRADIENT_HORIZONTAL; + + r.bottomright = clearlooks_style->shade_gc[1]; + r.border_gradient.to = r.border_gradient.from; + + if (button_type == CL_SCROLLBUTTON_OTHER) + { + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + } + else if (button_type == CL_SCROLLBUTTON_BEGIN) + { + if (horizontal) + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_NONE); + else + cl_rectangle_set_corners (&r, CL_CORNER_ROUND, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_NONE); + } + else + { + if (horizontal) + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_ROUND, + CL_CORNER_NONE, CL_CORNER_ROUND); + else + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_ROUND, CL_CORNER_ROUND); + } + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + + } + else if (DETAIL ("slider")) + { + if (DETAIL("slider") && widget && GTK_IS_RANGE (widget)) + { + GtkAdjustment *adj = GTK_RANGE (widget)->adjustment; + + if (adj->value <= adj->lower && + (GTK_RANGE (widget)->has_stepper_a || GTK_RANGE (widget)->has_stepper_b)) + { + if (GTK_IS_VSCROLLBAR (widget)) + { + y-=1; + height+=1; + } + else if (GTK_IS_HSCROLLBAR (widget)) + { + x-=1; + width+=1; + } + } + if (adj->value >= adj->upper - adj->page_size && + (GTK_RANGE (widget)->has_stepper_c || GTK_RANGE (widget)->has_stepper_d)) + { + if (GTK_IS_VSCROLLBAR (widget)) + height+=1; + else if (GTK_IS_HSCROLLBAR (widget)) + width+=1; + } + } + + cl_rectangle_set_button (&r, style, state_type, FALSE, GTK_WIDGET_HAS_FOCUS (widget), + CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.gradient_type = GTK_IS_HSCROLLBAR (widget) ? CL_GRADIENT_VERTICAL + : CL_GRADIENT_HORIZONTAL; + + cl_rectangle_set_gradient (&r.fill_gradient, &clearlooks_style->inset_light[state_type], + &clearlooks_style->inset_dark[state_type]); + + r.bottomright = clearlooks_style->shade_gc[1]; + r.border_gradient.to = r.border_gradient.from; + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (detail && !strcmp (detail, "optionmenu")) /* supporting deprecated */ + { + cl_draw_optionmenu(style, window, state_type, shadow_type, area, widget, detail, x, y, width, height); + } + else if (DETAIL ("menuitem")) + { + if (clearlooks_style->menuitemstyle == 0) + { + cl_draw_menuitem_flat (window, widget, style, area, state_type, + x, y, width, height, &r); + } + else if (clearlooks_style->menuitemstyle == 1) + { + cl_draw_menuitem_gradient (window, widget, style, area, state_type, + x, y, width, height, &r); + } + else + { + cl_draw_menuitem_button (window, widget, style, area, state_type, + x, y, width, height, &r); + } + } + else if (DETAIL ("menubar") && (clearlooks_style->sunkenmenubar || clearlooks_style->menubarstyle > 0)) + { + GdkGC *dark = clearlooks_style->shade_gc[2]; + GdkColor upper_color, lower_color; + + /* don't draw sunken menubar on gnome panel + IT'S A HACK! HORRIBLE HACK! HIDEOUS HACK! + BUT IT WORKS FOR ME(tm)! */ + if (widget->parent && + strcmp(G_OBJECT_TYPE_NAME (widget->parent), "PanelWidget") == 0) + return; + + shade(&style->bg[state_type], &upper_color, 1.0); + shade(&style->bg[state_type], &lower_color, 0.95); + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.fillgc = style->bg_gc[state_type]; + r.bordergc = clearlooks_style->shade_gc[2]; + r.gradient_type = CL_GRADIENT_VERTICAL; + + cl_rectangle_set_gradient (&r.border_gradient, &clearlooks_style->shade[2], + &clearlooks_style->shade[3]); + cl_rectangle_set_gradient (&r.fill_gradient, &upper_color, &lower_color); + + /* make vertical and top borders invisible for style 2 */ + if (clearlooks_style->menubarstyle == 2) { + x--; width+=2; + y--; height+=1; + } + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + } + else if (DETAIL ("menu") && widget->parent && + GDK_IS_WINDOW (widget->parent->window)) + { + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.bordergc = clearlooks_style->border_gc[CL_BORDER_UPPER]; + r.topleft = style->light_gc[state_type]; + r.bottomright = clearlooks_style->shade_gc[1]; + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + + return; + } + else if (DETAIL ("bar") && widget && GTK_IS_PROGRESS_BAR (widget)) + { + GdkColor upper_color = *clearlooks_get_spot_color (CLEARLOOKS_RC_STYLE (style->rc_style)), + lower_color, + prev_foreground; + gboolean activity_mode = GTK_PROGRESS (widget)->activity_mode; + +#ifdef HAVE_ANIMATION + if (!activity_mode && gtk_progress_bar_get_fraction (widget) != 1.0 && + !cl_progressbar_known((gconstpointer)widget)) + { + cl_progressbar_add ((gpointer)widget); + } +#endif + cl_progressbar_fill (window, widget, style, style->black_gc, + x, y, width, height, +#ifdef HAVE_ANIMATION + activity_mode ? 0 : pboffset, +#else + 0, +#endif + area); + + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + r.bordergc = clearlooks_style->spot3_gc; + r.topleft = clearlooks_style->spot2_gc; + + prev_foreground = cl_gc_set_fg_color_shade (clearlooks_style->spot2_gc, + style->colormap, + &clearlooks_style->spot2, + 1.2); + + cl_rectangle_set_clip_rectangle (&r, area); + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + cl_rectangle_reset_clip_rectangle (&r); + + gdk_gc_set_foreground (clearlooks_style->spot2_gc, &prev_foreground); + } + + else if ( widget && (DETAIL ("menubar") || DETAIL ("toolbar") || DETAIL ("dockitem_bin") || DETAIL ("handlebox_bin")) && shadow_type != GTK_SHADOW_NONE) /* Toolbars and menus */ + { + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], area); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], area); + } + + gtk_style_apply_default_background (style, window, + widget && !GTK_WIDGET_NO_WINDOW (widget), + state_type, area, x, y, width, height); + + /* we only want the borders on horizontal toolbars */ + if ( DETAIL ("menubar") || height < 2*width ) { + if (!DETAIL ("menubar")) + gdk_draw_line (window, clearlooks_style->shade_gc[0], + x, y, x + width, y); /* top */ + + gdk_draw_line (window, clearlooks_style->shade_gc[3], + x, y + height - 1, x + width, y + height - 1); /* bottom */ + } + + if (area) + { + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[0], NULL); + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[3], NULL); + } + } + else + { + parent_class->draw_box (style, window, state_type, shadow_type, area, + widget, detail, x, y, width, height); + } +} + +/**************************************************************************/ + +static void +ensure_check_pixmaps (GtkStyle *style, + GtkStateType state, + GdkScreen *screen, + gboolean treeview) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + ClearlooksRcStyle *clearlooks_rc = CLEARLOOKS_RC_STYLE (style->rc_style); + GdkPixbuf *check, *base, *inconsistent, *composite; + GdkColor *spot_color = clearlooks_get_spot_color (clearlooks_rc); + + if (clearlooks_style->check_pixmap_nonactive[state] != NULL) + return; + + if (state == GTK_STATE_ACTIVE || state == GTK_STATE_SELECTED) { + check = generate_bit (check_alpha, &style->text[GTK_STATE_NORMAL], 1.0); + inconsistent = generate_bit (check_inconsistent_alpha, &style->text[GTK_STATE_NORMAL], 1.0); + } else { + check = generate_bit (check_alpha, &style->text[state], 1.0); + inconsistent = generate_bit (check_inconsistent_alpha, &style->text[state], 1.0); + } + + if (state == GTK_STATE_ACTIVE && !treeview) + base = generate_bit (check_base_alpha, &style->bg[state], 1.0); + else + base = generate_bit (check_base_alpha, &style->base[GTK_STATE_NORMAL], 1.0); + + if (treeview) + composite = generate_bit (NULL, &clearlooks_style->shade[6], 1.0); + else + composite = generate_bit (NULL, &clearlooks_style->shade[5], 1.0); + + gdk_pixbuf_composite (base, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->check_pixmap_nonactive[state] = + pixbuf_to_pixmap (style, composite, screen); + + gdk_pixbuf_composite (check, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->check_pixmap_active[state] = + pixbuf_to_pixmap (style, composite, screen); + + g_object_unref (composite); + + composite = generate_bit (NULL, &clearlooks_style->shade[6], 1.0); + + gdk_pixbuf_composite (base, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + gdk_pixbuf_composite (inconsistent, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->check_pixmap_inconsistent[state] = + pixbuf_to_pixmap (style, composite, screen); + + g_object_unref (composite); + g_object_unref (base); + g_object_unref (check); + g_object_unref (inconsistent); +} + +static void +draw_check (DRAW_ARGS) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkGC *gc = style->base_gc[state_type]; + GdkPixmap *pixmap; + gboolean treeview; + + if (DETAIL ("check")) /* Menu item */ + { + parent_class->draw_check (style, window, state_type, shadow_type, area, + widget, detail, x, y, width, height); + return; + } + + treeview = widget && GTK_IS_TREE_VIEW(widget); + ensure_check_pixmaps (style, state_type, gtk_widget_get_screen (widget), treeview); + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + if (shadow_type == GTK_SHADOW_IN) + pixmap = clearlooks_style->check_pixmap_active[state_type]; + else if (shadow_type == GTK_SHADOW_ETCHED_IN) /* inconsistent */ + pixmap = clearlooks_style->check_pixmap_inconsistent[state_type]; + else + pixmap = clearlooks_style->check_pixmap_nonactive[state_type]; + + x += (width - CHECK_SIZE)/2; + y += (height - CHECK_SIZE)/2; + + gdk_draw_drawable (window, gc, pixmap, 0, 0, x, y, CHECK_SIZE, CHECK_SIZE); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); +} + +/**************************************************************************/ +static void +draw_slider (DRAW_ARGS, GtkOrientation orientation) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkGC *shade_gc = clearlooks_style->shade_gc[4]; + GdkGC *white_gc = clearlooks_style->shade_gc[0]; + int x1, y1; + +#if DEBUG + printf("draw_slider: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + gtk_paint_box (style, window, state_type, shadow_type, + area, widget, detail, x, y, width, height); + + if ((orientation == GTK_ORIENTATION_VERTICAL && height < 20) || + (orientation == GTK_ORIENTATION_HORIZONTAL && width < 20)) + return; + + if (detail && strcmp ("slider", detail) == 0) + { + if (area) + { + gdk_gc_set_clip_rectangle (shade_gc, area); + gdk_gc_set_clip_rectangle (white_gc, area); + } + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + x1 = x + width / 2 - 4; + y1 = y + (height - 6) / 2; + gdk_draw_line (window, shade_gc, x1, y1, x1, y1 + 6); + gdk_draw_line (window, white_gc, x1 + 1, y1, x1 + 1, y1 + 6); + gdk_draw_line (window, shade_gc, x1 + 3, y1, x1 + 3, y1 + 6); + gdk_draw_line (window, white_gc, x1 + 3 + 1, y1, x1 + 3 + 1, y1 + 6); + gdk_draw_line (window, shade_gc, x1 + 3*2, y1, x1 + 3*2, y1 + 6); + gdk_draw_line (window, white_gc, x1 + 3*2 + 1, y1, x1 + 3*2 + 1, y1 + 6); + } + else + { + x1 = x + (width - 6) / 2; + y1 = y + height / 2 - 4; + gdk_draw_line (window, shade_gc, x1 + 6, y1, x1, y1); + gdk_draw_line (window, white_gc, x1 + 6, y1 + 1, x1, y1 + 1); + gdk_draw_line (window, shade_gc, x1 + 6, y1 + 3, x1, y1 + 3); + gdk_draw_line (window, white_gc, x1 + 6, y1 + 3 + 1, x1, y1 + 3 + 1); + gdk_draw_line (window, shade_gc, x1 + 6, y1 + 3*2, x1, y1 + 3*2); + gdk_draw_line (window, white_gc, x1 + 6, y1 + 3*2 + 1, x1, y1 + 3*2 + 1); + } + if (area) + { + gdk_gc_set_clip_rectangle (shade_gc, NULL); + gdk_gc_set_clip_rectangle (white_gc, NULL); + } + } + else if (detail && (strcmp ("hscale", detail) == 0 || strcmp ("vscale", detail) == 0)) + { + if (area) + { + gdk_gc_set_clip_rectangle (shade_gc, area); + gdk_gc_set_clip_rectangle (white_gc, area); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + x1 = x + width / 2 - 3; + y1 = y + (height - 7) / 2; + gdk_draw_line (window, shade_gc, x1 + 0, y1 + 5, x1 + 0, y1 + 1); + gdk_draw_line (window, white_gc, x1 + 1, y1 + 5, x1 + 1, y1 + 1); + gdk_draw_line (window, shade_gc, x1 + 3, y1 + 5, x1 + 3, y1 + 1); + gdk_draw_line (window, white_gc, x1 + 4, y1 + 5, x1 + 4, y1 + 1); + gdk_draw_line (window, shade_gc, x1 + 6, y1 + 5, x1 + 6, y1 + 1); + gdk_draw_line (window, white_gc, x1 + 7, y1 + 5, x1 + 7, y1 + 1); + } + else + { + x1 = x + (width - 7) / 2; + y1 = y + height / 2 - 3; + gdk_draw_line (window, shade_gc, x1 + 5, y1 + 0, x1 + 1, y1 + 0); + gdk_draw_line (window, white_gc, x1 + 5, y1 + 1, x1 + 1, y1 + 1); + gdk_draw_line (window, shade_gc, x1 + 5, y1 + 3, x1 + 1, y1 + 3); + gdk_draw_line (window, white_gc, x1 + 5, y1 + 4, x1 + 1, y1 + 4); + gdk_draw_line (window, shade_gc, x1 + 5, y1 + 6, x1 + 1, y1 + 6); + gdk_draw_line (window, white_gc, x1 + 5, y1 + 7, x1 + 1, y1 + 7); + } + if (area) + { + gdk_gc_set_clip_rectangle (shade_gc, NULL); + gdk_gc_set_clip_rectangle (white_gc, NULL); + } + } +} + +/**************************************************************************/ +static void +ensure_radio_pixmaps (GtkStyle *style, + GtkStateType state, + GdkScreen *screen) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + ClearlooksRcStyle *clearlooks_rc = CLEARLOOKS_RC_STYLE (style->rc_style); + GdkPixbuf *dot, *circle, *outline, *inconsistent, *composite; + GdkColor *spot_color = clearlooks_get_spot_color (clearlooks_rc); + GdkColor *composite_color; + + if (clearlooks_style->radio_pixmap_nonactive[state] != NULL) + return; + + if (state == GTK_STATE_ACTIVE || state == GTK_STATE_SELECTED) { + dot = colorize_bit (dot_intensity, dot_alpha, &style->text[GTK_STATE_NORMAL]); + inconsistent = generate_bit (inconsistent_alpha, &style->text[GTK_STATE_NORMAL], 1.0); + } else { + dot = colorize_bit (dot_intensity, dot_alpha, &style->text[state]); + inconsistent = generate_bit (inconsistent_alpha, &style->text[state], 1.0); + } + + outline = generate_bit (outline_alpha, &clearlooks_style->shade[5], 1.0); + + if (clearlooks_style->radio_pixmap_mask == NULL) + { + gdk_pixbuf_render_pixmap_and_mask (outline, + NULL, + &clearlooks_style->radio_pixmap_mask, + 1); + } + + if (state == GTK_STATE_ACTIVE) + { + composite_color = &style->bg[GTK_STATE_PRELIGHT]; + circle = generate_bit (circle_alpha, &style->bg[state], 1.0); + } + else + { + composite_color = &style->bg[state]; + circle = generate_bit (circle_alpha, &style->base[GTK_STATE_NORMAL], 1.0); + } + + composite = generate_bit (NULL, composite_color, 1.0); + + gdk_pixbuf_composite (outline, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + gdk_pixbuf_composite (circle, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->radio_pixmap_nonactive[state] = + pixbuf_to_pixmap (style, composite, screen); + + gdk_pixbuf_composite (dot, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->radio_pixmap_active[state] = + pixbuf_to_pixmap (style, composite, screen); + + g_object_unref (composite); + + composite = generate_bit (NULL, composite_color,1.0); + + gdk_pixbuf_composite (outline, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (circle, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (inconsistent, composite, + 0, 0, RADIO_SIZE, RADIO_SIZE, 0, 0, + 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + clearlooks_style->radio_pixmap_inconsistent[state] = + pixbuf_to_pixmap (style, composite, screen); + + g_object_unref (composite); + g_object_unref (circle); + g_object_unref (dot); + g_object_unref (inconsistent); + g_object_unref (outline); +} + +static void +draw_option (DRAW_ARGS) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkGC *gc = style->base_gc[state_type]; + GdkPixmap *pixmap; + + if (DETAIL ("option")) /* Menu item */ + { + parent_class->draw_option (style, window, state_type, shadow_type, + area, widget, detail, x, y, width, height); + return; + } + + ensure_radio_pixmaps (style, state_type, gtk_widget_get_screen (widget)); + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + if (shadow_type == GTK_SHADOW_IN) + pixmap = clearlooks_style->radio_pixmap_active[state_type]; + else if (shadow_type == GTK_SHADOW_ETCHED_IN) /* inconsistent */ + pixmap = clearlooks_style->radio_pixmap_inconsistent[state_type]; + else + pixmap = clearlooks_style->radio_pixmap_nonactive[state_type]; + + x += (width - RADIO_SIZE)/2; + y += (height - RADIO_SIZE)/2; + + gdk_gc_set_clip_mask (gc, clearlooks_style->radio_pixmap_mask); + gdk_gc_set_clip_origin (gc, x, y); + + gdk_draw_drawable (window, gc, pixmap, 0, 0, x, y, + RADIO_SIZE, RADIO_SIZE); + + gdk_gc_set_clip_origin (gc, 0, 0); + gdk_gc_set_clip_mask (gc, NULL); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); +} + +/**************************************************************************/ + +static void +draw_shadow_gap (DRAW_ARGS, + GtkPositionType gap_side, + gint gap_x, + gint gap_width) +{ + /* I need to improve this function. */ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + CLRectangle r; + GdkRegion *area_region = NULL, + *gap_region = NULL; + +#if DEBUG + printf("draw_shadow_gap: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + sanitize_size (window, &width, &height); + + cl_rectangle_reset (&r, style); + cl_rectangle_set_corners (&r, CL_CORNER_NONE, CL_CORNER_NONE, + CL_CORNER_NONE, CL_CORNER_NONE); + + if (area) + { + area_region = gdk_region_rectangle (area); + + switch (gap_side) + { + case GTK_POS_TOP: + { + GdkRectangle rect = { x+gap_x, y, gap_width, 2 }; + gap_region = gdk_region_rectangle (&rect); + break; + } + case GTK_POS_BOTTOM: + { + GdkRectangle rect = { x+gap_x, y+height-2, gap_width, 2 }; + gap_region = gdk_region_rectangle (&rect); + break; + } + case GTK_POS_LEFT: + { + GdkRectangle rect = { x, y+gap_x, 2, gap_width }; + gap_region = gdk_region_rectangle (&rect); + break; + } + case GTK_POS_RIGHT: + { + GdkRectangle rect = { x+width-2, y+gap_x, 2, gap_width }; + gap_region = gdk_region_rectangle (&rect); + break; + } + } + + gdk_region_subtract (area_region, gap_region); + } + + if (shadow_type == GTK_SHADOW_ETCHED_IN || + shadow_type == GTK_SHADOW_ETCHED_OUT) + { + GdkGC *a; + GdkGC *b; + + if (shadow_type == GTK_SHADOW_ETCHED_IN) + { + a = style->light_gc[state_type]; + b = clearlooks_style->shade_gc[3]; + } + else + { + a = clearlooks_style->shade_gc[3]; + b = style->light_gc[state_type]; + } + + gdk_gc_set_clip_region (a, area_region); + gdk_gc_set_clip_region (b, area_region); + + r.bordergc = a; + cl_draw_rectangle (window, widget, style, x+1, y+1, width-1, height-1, &r); + + r.bordergc = b; + cl_draw_rectangle (window, widget, style, x, y, width-1, height-1, &r); + + gdk_gc_set_clip_region (a, NULL); + gdk_gc_set_clip_region (b, NULL); + } + else if (shadow_type == GTK_SHADOW_IN || shadow_type == GTK_SHADOW_OUT) + { + r.topleft = (shadow_type == GTK_SHADOW_OUT) ? style->light_gc[state_type] : clearlooks_style->shade_gc[1]; + r.bottomright = (shadow_type == GTK_SHADOW_OUT) ? clearlooks_style->shade_gc[1] : style->light_gc[state_type]; + r.bordergc = clearlooks_style->shade_gc[5]; + + gdk_gc_set_clip_region (r.bordergc, area_region); + gdk_gc_set_clip_region (r.topleft, area_region); + gdk_gc_set_clip_region (r.bottomright, area_region); + + cl_draw_rectangle (window, widget, style, x, y, width, height, &r); + + cl_draw_shadow (window, widget, style, x, y, width, height, &r); + + gdk_gc_set_clip_region (r.bordergc, NULL); + gdk_gc_set_clip_region (r.topleft, NULL); + gdk_gc_set_clip_region (r.bottomright, NULL); + } + + if (area_region) + gdk_region_destroy (area_region); +} + +/**************************************************************************/ +static void +draw_hline (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint x1, + gint x2, + gint y) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + +#if DEBUG + printf("draw_hline\n"); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + if (area) + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[2], area); + + if (detail && !strcmp (detail, "label")) + { + if (state_type == GTK_STATE_INSENSITIVE) + gdk_draw_line (window, style->light_gc[state_type], x1 + 1, y + 1, x2 + 1, y + 1); + + gdk_draw_line (window, style->fg_gc[state_type], x1, y, x2, y); + } + else + { + gdk_draw_line (window, clearlooks_style->shade_gc[2], x1, y, x2, y); + + /* if (DETAIL ("menuitem")) */ + gdk_draw_line (window, clearlooks_style->shade_gc[0], x1, y+1, x2, y+1); + } + + if (area) + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[2], NULL); +} + +/**************************************************************************/ +static void +draw_vline (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint y1, + gint y2, + gint x) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + gint thickness_light; + gint thickness_dark; + +#if DEBUG + printf("draw_vline\n"); +#endif + + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + thickness_light = style->xthickness / 2; + thickness_dark = style->xthickness - thickness_light; + + if (area) + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[2], area); + + gdk_draw_line (window, clearlooks_style->shade_gc[2], x, y1, x, y2 - 1); + gdk_draw_line (window, clearlooks_style->shade_gc[0], x+1, y1, x+1, y2 - 1); + + if (area) + gdk_gc_set_clip_rectangle (clearlooks_style->shade_gc[2], NULL); +} + +/**************************************************************************/ +static void +draw_focus (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint x, + gint y, + gint width, + gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkPoint points[5]; + GdkGC *gc; + gboolean free_dash_list = FALSE; + gint line_width = 1; + gchar *dash_list = "\1\1"; + gint dash_len; + +#if DEBUG + printf("draw_focus: %s %d %d %d %d\n", detail, x, y, width, height); +#endif + + gc = clearlooks_style->shade_gc[6]; + + if (widget) + { + gtk_widget_style_get (widget, + "focus-line-width", &line_width, + "focus-line-pattern", (gchar *)&dash_list, + NULL); + + free_dash_list = TRUE; + } + + sanitize_size (window, &width, &height); + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + gdk_gc_set_line_attributes (gc, line_width, + dash_list[0] ? GDK_LINE_ON_OFF_DASH : GDK_LINE_SOLID, + GDK_CAP_BUTT, GDK_JOIN_MITER); + + + if (detail && !strcmp (detail, "add-mode")) + { + if (free_dash_list) + g_free (dash_list); + + dash_list = "\4\4"; + free_dash_list = FALSE; + } + + points[0].x = x + line_width / 2; + points[0].y = y + line_width / 2; + points[1].x = x + width - line_width + line_width / 2; + points[1].y = y + line_width / 2; + points[2].x = x + width - line_width + line_width / 2; + points[2].y = y + height - line_width + line_width / 2; + points[3].x = x + line_width / 2; + points[3].y = y + height - line_width + line_width / 2; + points[4] = points[0]; + + if (!dash_list[0]) + { + gdk_draw_lines (window, gc, points, 5); + } + else + { + dash_len = strlen (dash_list); + + if (dash_list[0]) + gdk_gc_set_dashes (gc, 0, dash_list, dash_len); + + gdk_draw_lines (window, gc, points, 3); + + points[2].x += 1; + + if (dash_list[0]) + { + gint dash_pixels = 0; + gint i; + + /* Adjust the dash offset for the bottom and left so we + * match up at the upper left. + */ + for (i = 0; i < dash_len; i++) + dash_pixels += dash_list[i]; + + if (dash_len % 2 == 1) + dash_pixels *= 2; + + gdk_gc_set_dashes (gc, + dash_pixels - (width + height - 2 * line_width) % dash_pixels, + dash_list, dash_len); + } + + gdk_draw_lines (window, gc, points + 2, 3); + } + + gdk_gc_set_line_attributes (gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + + if (free_dash_list) + g_free (dash_list); +} + +static void +draw_layout(GtkStyle * style, + GdkWindow * window, + GtkStateType state_type, + gboolean use_text, + GdkRectangle * area, + GtkWidget * widget, + const gchar * detail, gint x, gint y, PangoLayout * layout) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + + g_return_if_fail(GTK_IS_STYLE (style)); + g_return_if_fail(window != NULL); + + parent_class->draw_layout(style, window, state_type, use_text, + area, widget, detail, x, y, layout); + + +} + +/**************************************************************************/ +static void +draw_resize_grip (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + GdkWindowEdge edge, + gint x, + gint y, + gint width, + gint height) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (window != NULL); + + if (area) + { + gdk_gc_set_clip_rectangle (style->light_gc[state_type], area); + gdk_gc_set_clip_rectangle (style->dark_gc[state_type], area); + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], area); + } + + switch (edge) + { + case GDK_WINDOW_EDGE_NORTH_WEST: + /* make it square */ + if (width < height) + { + height = width; + } + else if (height < width) + { + width = height; + } + break; + case GDK_WINDOW_EDGE_NORTH: + if (width < height) + { + height = width; + } + break; + case GDK_WINDOW_EDGE_NORTH_EAST: + /* make it square, aligning to top right */ + if (width < height) + { + height = width; + } + else if (height < width) + { + x += (width - height); + width = height; + } + break; + case GDK_WINDOW_EDGE_WEST: + if (height < width) + { + width = height; + } + break; + case GDK_WINDOW_EDGE_EAST: + /* aligning to right */ + if (height < width) + { + x += (width - height); + width = height; + } + break; + case GDK_WINDOW_EDGE_SOUTH_WEST: + /* make it square, aligning to bottom left */ + if (width < height) + { + y += (height - width); + height = width; + } + else if (height < width) + { + width = height; + } + break; + case GDK_WINDOW_EDGE_SOUTH: + /* align to bottom */ + if (width < height) + { + y += (height - width); + height = width; + } + break; + case GDK_WINDOW_EDGE_SOUTH_EAST: + /* make it square, aligning to bottom right */ + if (width < height) + { + y += (height - width); + height = width; + } + else if (height < width) + { + x += (width - height); + width = height; + } + break; + default: + g_assert_not_reached (); + } + + /* Clear background */ + gtk_style_apply_default_background (style, window, FALSE, + state_type, area, + x, y, width, height); + + switch (edge) + { + case GDK_WINDOW_EDGE_WEST: + case GDK_WINDOW_EDGE_EAST: + { + gint xi; + + xi = x; + + while (xi < x + width) + { + gdk_draw_line (window, + style->light_gc[state_type], + xi, y, + xi, y + height); + + xi++; + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + xi, y, + xi, y + height); + + xi += 2; + } + } + break; + case GDK_WINDOW_EDGE_NORTH: + case GDK_WINDOW_EDGE_SOUTH: + { + gint yi; + + yi = y; + + while (yi < y + height) + { + gdk_draw_line (window, + style->light_gc[state_type], + x, yi, + x + width, yi); + + yi++; + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + x, yi, + x + width, yi); + + yi+= 2; + } + } + break; + case GDK_WINDOW_EDGE_NORTH_WEST: + { + gint xi, yi; + + xi = x + width; + yi = y + height; + + while (xi > x + 3) + { + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + xi, y, + x, yi); + + --xi; + --yi; + + gdk_draw_line (window, + style->light_gc[state_type], + xi, y, + x, yi); + + xi -= 3; + yi -= 3; + + } + } + break; + case GDK_WINDOW_EDGE_NORTH_EAST: + { + gint xi, yi; + + xi = x; + yi = y + height; + + while (xi < (x + width - 3)) + { + gdk_draw_line (window, + style->light_gc[state_type], + xi, y, + x + width, yi); + + ++xi; + --yi; + + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + xi, y, + x + width, yi); + + xi += 3; + yi -= 3; + } + } + break; + case GDK_WINDOW_EDGE_SOUTH_WEST: + { + gint xi, yi; + + xi = x + width; + yi = y; + + while (xi > x + 3) + { + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + x, yi, + xi, y + height); + + --xi; + ++yi; + + gdk_draw_line (window, + style->light_gc[state_type], + x, yi, + xi, y + height); + + xi -= 3; + yi += 3; + + } + } + break; + + case GDK_WINDOW_EDGE_SOUTH_EAST: + { + gint xi, yi; + + xi = x; + yi = y; + + while (xi < (x + width - 3)) + { + gdk_draw_line (window, + style->light_gc[state_type], + xi, y + height, + x + width, yi); + + ++xi; + ++yi; + + gdk_draw_line (window, + clearlooks_style->shade_gc[4], + xi, y + height, + x + width, yi); + + xi += 3; + yi += 3; + } + } + break; + default: + g_assert_not_reached (); + break; + } + + if (area) + { + gdk_gc_set_clip_rectangle (style->light_gc[state_type], NULL); + gdk_gc_set_clip_rectangle (style->dark_gc[state_type], NULL); + gdk_gc_set_clip_rectangle (style->bg_gc[state_type], NULL); + } +} + +/**************************************************************************/ + +static void +clearlooks_style_init_from_rc (GtkStyle * style, + GtkRcStyle * rc_style) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + GdkColor *spot_color; + double shades[] = {1.065, 0.93, 0.896, 0.85, 0.768, 0.665, 0.4, 0.205}; + int i; + double contrast; + + parent_class->init_from_rc (style, rc_style); + + contrast = CLEARLOOKS_RC_STYLE (rc_style)->contrast; + + clearlooks_style->sunkenmenubar = CLEARLOOKS_RC_STYLE (rc_style)->sunkenmenubar; + clearlooks_style->progressbarstyle = CLEARLOOKS_RC_STYLE (rc_style)->progressbarstyle; + clearlooks_style->menubarstyle = CLEARLOOKS_RC_STYLE (rc_style)->menubarstyle; + clearlooks_style->menuitemstyle = CLEARLOOKS_RC_STYLE (rc_style)->menuitemstyle; + clearlooks_style->listviewitemstyle = CLEARLOOKS_RC_STYLE (rc_style)->listviewitemstyle; + + /* Lighter to darker */ + for (i = 0; i < 8; i++) + { + shade (&style->bg[GTK_STATE_NORMAL], &clearlooks_style->shade[i], + (shades[i]-0.7) * contrast + 0.7); + } + + spot_color = clearlooks_get_spot_color (CLEARLOOKS_RC_STYLE (rc_style)); + + clearlooks_style->spot_color = *spot_color; + shade (&clearlooks_style->spot_color, &clearlooks_style->spot1, 1.42); + shade (&clearlooks_style->spot_color, &clearlooks_style->spot2, 1.05); + shade (&clearlooks_style->spot_color, &clearlooks_style->spot3, 0.65); + + shade (&style->bg[GTK_STATE_NORMAL], &clearlooks_style->border[CL_BORDER_UPPER], 0.5); + shade (&style->bg[GTK_STATE_NORMAL], &clearlooks_style->border[CL_BORDER_LOWER], 0.62); + shade (&style->bg[GTK_STATE_ACTIVE], &clearlooks_style->border[CL_BORDER_UPPER_ACTIVE], 0.5); + shade (&style->bg[GTK_STATE_ACTIVE], &clearlooks_style->border[CL_BORDER_LOWER_ACTIVE], 0.55); +} + +static GdkGC * +realize_color (GtkStyle * style, + GdkColor * color) +{ + GdkGCValues gc_values; + + gdk_colormap_alloc_color (style->colormap, color, FALSE, TRUE); + + gc_values.foreground = *color; + + return gtk_gc_get (style->depth, style->colormap, &gc_values, GDK_GC_FOREGROUND); +} + +static void +clearlooks_style_realize (GtkStyle * style) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int i; + + parent_class->realize (style); + + for (i = 0; i < 8; i++) + clearlooks_style->shade_gc[i] = realize_color (style, &clearlooks_style->shade[i]); + + for (i=0; i < CL_BORDER_COUNT; i++) + clearlooks_style->border_gc[i] = realize_color (style, &clearlooks_style->border[i]); + + clearlooks_style->spot1_gc = realize_color (style, &clearlooks_style->spot1); + clearlooks_style->spot2_gc = realize_color (style, &clearlooks_style->spot2); + clearlooks_style->spot3_gc = realize_color (style, &clearlooks_style->spot3); + + /* set light inset color */ + for (i=0; i<5; i++) + { + shade (&style->bg[i], &clearlooks_style->inset_dark[i], 0.93); + gdk_rgb_find_color (style->colormap, &clearlooks_style->inset_dark[i]); + + shade (&style->bg[i], &clearlooks_style->inset_light[i], 1.055); + gdk_rgb_find_color (style->colormap, &clearlooks_style->inset_light[i]); + + shade (&style->bg[i], &clearlooks_style->listview_bg[i], 1.015); + gdk_rgb_find_color (style->colormap, &clearlooks_style->listview_bg[i]); + + /* CREATE GRADIENT FOR BUTTONS */ + shade (&style->bg[i], &clearlooks_style->button_g1[i], 1.055); + gdk_rgb_find_color (style->colormap, &clearlooks_style->button_g1[i]); + + shade (&style->bg[i], &clearlooks_style->button_g2[i], 1.005); + gdk_rgb_find_color (style->colormap, &clearlooks_style->button_g2[i]); + + shade (&style->bg[i], &clearlooks_style->button_g3[i], 0.98); + gdk_rgb_find_color (style->colormap, &clearlooks_style->button_g3[i]); + + shade (&style->bg[i], &clearlooks_style->button_g4[i], 0.91); + gdk_rgb_find_color (style->colormap, &clearlooks_style->button_g4[i]); + } + +} + +static void +clearlooks_style_unrealize (GtkStyle * style) +{ + ClearlooksStyle *clearlooks_style = CLEARLOOKS_STYLE (style); + int i; + + /* We don't free the colors, because we don't know if + * gtk_gc_release() actually freed the GC. FIXME - need + * a way of ref'ing colors explicitely so GtkGC can + * handle things properly. + */ + for (i=0; i < 8; i++) + gtk_gc_release (clearlooks_style->shade_gc[i]); + + gtk_gc_release (clearlooks_style->spot1_gc); + gtk_gc_release (clearlooks_style->spot2_gc); + gtk_gc_release (clearlooks_style->spot3_gc); + + for (i = 0; i < 5; i++) + { + if (clearlooks_style->radio_pixmap_nonactive[i] != NULL) + { + g_object_unref (clearlooks_style->radio_pixmap_nonactive[i]); + clearlooks_style->radio_pixmap_nonactive[i] = NULL; + g_object_unref (clearlooks_style->radio_pixmap_active[i]); + clearlooks_style->radio_pixmap_active[i] = NULL; + g_object_unref (clearlooks_style->radio_pixmap_inconsistent[i]); + clearlooks_style->radio_pixmap_inconsistent[i] = NULL; + } + + if (clearlooks_style->check_pixmap_nonactive[i] != NULL) + { + g_object_unref (clearlooks_style->check_pixmap_nonactive[i]); + clearlooks_style->check_pixmap_nonactive[i] = NULL; + g_object_unref (clearlooks_style->check_pixmap_active[i]); + clearlooks_style->check_pixmap_active[i] = NULL; + g_object_unref (clearlooks_style->check_pixmap_inconsistent[i]); + clearlooks_style->check_pixmap_inconsistent[i] = NULL; + } + } + + if (clearlooks_style->radio_pixmap_mask != NULL) + g_object_unref (clearlooks_style->radio_pixmap_mask); + + clearlooks_style->radio_pixmap_mask = NULL; + + while (progressbars = g_list_first (progressbars)) + cl_progressbar_remove (progressbars->data); + + if (timer_id != 0) + { + g_source_remove(timer_id); + timer_id = 0; + } + + parent_class->unrealize (style); +} + +static GdkPixbuf * +set_transparency (const GdkPixbuf *pixbuf, gdouble alpha_percent) +{ + GdkPixbuf *target; + guchar *data, *current; + guint x, y, rowstride, height, width; + + g_return_val_if_fail (pixbuf != NULL, NULL); + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); + + /* Returns a copy of pixbuf with it's non-completely-transparent pixels to + have an alpha level "alpha_percent" of their original value. */ + + target = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); + + if (alpha_percent == 1.0) + return target; + width = gdk_pixbuf_get_width (target); + height = gdk_pixbuf_get_height (target); + rowstride = gdk_pixbuf_get_rowstride (target); + data = gdk_pixbuf_get_pixels (target); + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + /* The "4" is the number of chars per pixel, in this case, RGBA, + the 3 means "skip to the alpha" */ + current = data + (y * rowstride) + (x * 4) + 3; + *(current) = (guchar) (*(current) * alpha_percent); + } + } + + return target; +} + +static GdkPixbuf* +scale_or_ref (GdkPixbuf *src, + int width, + int height) +{ + if (width == gdk_pixbuf_get_width (src) && + height == gdk_pixbuf_get_height (src)) { + return g_object_ref (src); + } else { + return gdk_pixbuf_scale_simple (src, + width, height, + GDK_INTERP_BILINEAR); + } +} + +static GdkPixbuf * +render_icon (GtkStyle *style, + const GtkIconSource *source, + GtkTextDirection direction, + GtkStateType state, + GtkIconSize size, + GtkWidget *widget, + const char *detail) +{ + int width = 1; + int height = 1; + GdkPixbuf *scaled; + GdkPixbuf *stated; + GdkPixbuf *base_pixbuf; + GdkScreen *screen; + GtkSettings *settings; + + /* Oddly, style can be NULL in this function, because + * GtkIconSet can be used without a style and if so + * it uses this function. + */ + + base_pixbuf = gtk_icon_source_get_pixbuf (source); + + g_return_val_if_fail (base_pixbuf != NULL, NULL); + + if (widget && gtk_widget_has_screen (widget)) { + screen = gtk_widget_get_screen (widget); + settings = gtk_settings_get_for_screen (screen); + } else if (style->colormap) { + screen = gdk_colormap_get_screen (style->colormap); + settings = gtk_settings_get_for_screen (screen); + } else { + settings = gtk_settings_get_default (); + GTK_NOTE (MULTIHEAD, + g_warning ("Using the default screen for gtk_default_render_icon()")); + } + + + if (size != (GtkIconSize) -1 && !gtk_icon_size_lookup_for_settings (settings, size, &width, &height)) { + g_warning (G_STRLOC ": invalid icon size '%d'", size); + return NULL; + } + + /* If the size was wildcarded, and we're allowed to scale, then scale; otherwise, + * leave it alone. + */ + if (size != (GtkIconSize)-1 && gtk_icon_source_get_size_wildcarded (source)) + scaled = scale_or_ref (base_pixbuf, width, height); + else + scaled = g_object_ref (base_pixbuf); + + /* If the state was wildcarded, then generate a state. */ + if (gtk_icon_source_get_state_wildcarded (source)) { + if (state == GTK_STATE_INSENSITIVE) { + stated = set_transparency (scaled, 0.3); +#if 0 + stated = + gdk_pixbuf_composite_color_simple (scaled, + gdk_pixbuf_get_width (scaled), + gdk_pixbuf_get_height (scaled), + GDK_INTERP_BILINEAR, 128, + gdk_pixbuf_get_width (scaled), + style->bg[state].pixel, + style->bg[state].pixel); +#endif + gdk_pixbuf_saturate_and_pixelate (stated, stated, + 0.1, FALSE); + + g_object_unref (scaled); + } else if (state == GTK_STATE_PRELIGHT) { + stated = gdk_pixbuf_copy (scaled); + + gdk_pixbuf_saturate_and_pixelate (scaled, stated, + 1.2, FALSE); + + g_object_unref (scaled); + } else { + stated = scaled; + } + } + else + stated = scaled; + + return stated; +} + +static void +clearlooks_style_init (ClearlooksStyle * style) +{ +} + +static void +clearlooks_style_class_init (ClearlooksStyleClass * klass) +{ + GtkStyleClass *style_class = GTK_STYLE_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + style_class->realize = clearlooks_style_realize; + style_class->unrealize = clearlooks_style_unrealize; + style_class->init_from_rc = clearlooks_style_init_from_rc; + style_class->draw_focus = draw_focus; + style_class->draw_resize_grip = draw_resize_grip; + style_class->draw_handle = draw_handle; + style_class->draw_vline = draw_vline; + style_class->draw_hline = draw_hline; + style_class->draw_slider = draw_slider; + style_class->draw_shadow_gap = draw_shadow_gap; + style_class->draw_arrow = clearlooks_draw_arrow; + style_class->draw_check = draw_check; + style_class->draw_tab = draw_tab; + style_class->draw_box = draw_box; + style_class->draw_shadow = draw_shadow; + style_class->draw_box_gap = draw_box_gap; + style_class->draw_extension = draw_extension; + style_class->draw_option = draw_option; + style_class->draw_layout = draw_layout; + style_class->render_icon = render_icon; + style_class->draw_flat_box = draw_flat_box; +} + +GType clearlooks_type_style = 0; + +void +clearlooks_style_register_type (GTypeModule * module) +{ + static const GTypeInfo object_info = + { + sizeof (ClearlooksStyleClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) clearlooks_style_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ClearlooksStyle), + 0, /* n_preallocs */ + (GInstanceInitFunc) clearlooks_style_init, + NULL + }; + + clearlooks_type_style = g_type_module_register_type (module, + GTK_TYPE_STYLE, + "ClearlooksStyle", + &object_info, 0); +} diff --git a/libs/clearlooks/clearlooks_style.h b/libs/clearlooks/clearlooks_style.h new file mode 100644 index 0000000000..1e07877bf7 --- /dev/null +++ b/libs/clearlooks/clearlooks_style.h @@ -0,0 +1,108 @@ +/* Clearlooks Engine + * Copyright (C) 2005 Richard Stellingwerff. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by Owen Taylor <otaylor@redhat.com> + * and by Alexander Larsson <alexl@redhat.com> + * Modified by Richard Stellingwerff <remenic@gmail.com> + */ +#include <gtk/gtkstyle.h> + +#include "clearlooks_draw.h" + +typedef struct _ClearlooksStyle ClearlooksStyle; +typedef struct _ClearlooksStyleClass ClearlooksStyleClass; + +extern GType clearlooks_type_style; + +#define CLEARLOOKS_TYPE_STYLE clearlooks_type_style +#define CLEARLOOKS_STYLE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CLEARLOOKS_TYPE_STYLE, ClearlooksStyle)) +#define CLEARLOOKS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLEARLOOKS_TYPE_STYLE, ClearlooksStyleClass)) +#define CLEARLOOKS_IS_STYLE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CLEARLOOKS_TYPE_STYLE)) +#define CLEARLOOKS_IS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLEARLOOKS_TYPE_STYLE)) +#define CLEARLOOKS_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLEARLOOKS_TYPE_STYLE, ClearlooksStyleClass)) + +typedef enum +{ + CL_BORDER_UPPER = 0, + CL_BORDER_LOWER, + CL_BORDER_UPPER_ACTIVE, + CL_BORDER_LOWER_ACTIVE, + CL_BORDER_COUNT +} ClBorderColorType; + +typedef enum +{ + CL_SCROLLBUTTON_BEGIN = 0, + CL_SCROLLBUTTON_END, + CL_SCROLLBUTTON_OTHER +} ClScrollButtonType; + +struct _ClearlooksStyle +{ + GtkStyle parent_instance; + + GdkColor shade[9]; + + GdkColor spot_color; + GdkColor spot1; + GdkColor spot2; + GdkColor spot3; + + GdkColor border[CL_BORDER_COUNT]; + + /* from light to dark */ + GdkGC *shade_gc[9]; + GdkGC *border_gc[CL_BORDER_COUNT]; + + GdkGC *spot1_gc; + GdkGC *spot2_gc; + GdkGC *spot3_gc; + + GdkColor inset_light[5]; + GdkColor inset_dark[5]; + + GdkColor button_g1[5]; + GdkColor button_g2[5]; + GdkColor button_g3[5]; + GdkColor button_g4[5]; + + GdkColor listview_bg[5]; + + GdkPixmap *radio_pixmap_nonactive[5]; + GdkPixmap *radio_pixmap_active[5]; + GdkPixmap *radio_pixmap_inconsistent[5]; + GdkBitmap *radio_pixmap_mask; /* All masks are the same */ + + GdkPixmap *check_pixmap_nonactive[5]; + GdkPixmap *check_pixmap_active[5]; + GdkPixmap *check_pixmap_inconsistent[5]; + + gboolean sunkenmenubar:1; + + guint8 progressbarstyle; + guint8 menubarstyle; + guint8 menuitemstyle; + guint8 listviewitemstyle; +}; + +struct _ClearlooksStyleClass +{ + GtkStyleClass parent_class; +}; + +void clearlooks_style_register_type (GTypeModule *module); diff --git a/libs/clearlooks/clearlooks_theme_main.c b/libs/clearlooks/clearlooks_theme_main.c new file mode 100644 index 0000000000..d30d4dd0b7 --- /dev/null +++ b/libs/clearlooks/clearlooks_theme_main.c @@ -0,0 +1,37 @@ +#include <gmodule.h> +#include <gtk/gtk.h> + +#include "clearlooks_style.h" +#include "clearlooks_rc_style.h" + +G_MODULE_EXPORT void +theme_init (GTypeModule *module) +{ + clearlooks_rc_style_register_type (module); + clearlooks_style_register_type (module); + printf("theme_init() called from internal clearlooks engine!\n"); +} + +G_MODULE_EXPORT void +theme_exit (void) +{ +} + +G_MODULE_EXPORT GtkRcStyle * +theme_create_rc_style (void) +{ + return GTK_RC_STYLE (g_object_new (CLEARLOOKS_TYPE_RC_STYLE, NULL)); +} + +/* The following function will be called by GTK+ when the module + * is loaded and checks to see if we are compatible with the + * version of GTK+ that loads us. + */ +G_MODULE_EXPORT const gchar* g_module_check_init (GModule *module); +const gchar* +g_module_check_init (GModule *module) +{ + return gtk_check_version (GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION - GTK_INTERFACE_AGE); +} diff --git a/libs/clearlooks/cpdll.sh b/libs/clearlooks/cpdll.sh new file mode 100755 index 0000000000..fb101d52a0 --- /dev/null +++ b/libs/clearlooks/cpdll.sh @@ -0,0 +1,2 @@ +mkdir engines +cp libclearlooks.so engines diff --git a/libs/clearlooks/support.c b/libs/clearlooks/support.c new file mode 100644 index 0000000000..358c7f43fb --- /dev/null +++ b/libs/clearlooks/support.c @@ -0,0 +1,981 @@ +#include "support.h" + +/* #define ALWAYS_DITHER_GRADIENTS */ + +GtkTextDirection +get_direction (GtkWidget *widget) +{ + GtkTextDirection dir; + + if (widget) + dir = gtk_widget_get_direction (widget); + else + dir = GTK_TEXT_DIR_LTR; + + return dir; +} + +GdkPixbuf * +generate_bit (unsigned char alpha[], GdkColor *color, double mult) +{ + guint r, g, b; + GdkPixbuf *pixbuf; + unsigned char *pixels; + int w, h, rs; + int x, y; + + r = (color->red >> 8) * mult; + r = MIN(r, 255); + g = (color->green >> 8) * mult; + g = MIN(g, 255); + b = (color->blue >> 8) * mult; + b = MIN(b, 255); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, RADIO_SIZE, RADIO_SIZE); + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + rs = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + + for (y=0; y < h; y++) + { + for (x=0; x < w; x++) + { + pixels[y*rs + x*4 + 0] = r; + pixels[y*rs + x*4 + 1] = g; + pixels[y*rs + x*4 + 2] = b; + if (alpha) + pixels[y*rs + x*4 + 3] = alpha[y*w + x]; + else + pixels[y*rs + x*4 + 3] = 255; + } + } + + return pixbuf; +} + +#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255))) + +GdkPixbuf * +colorize_bit (unsigned char *bit, + unsigned char *alpha, + GdkColor *new_color) +{ + GdkPixbuf *pixbuf; + double intensity; + int x, y; + const guchar *src, *asrc; + guchar *dest; + int dest_rowstride; + int width, height; + guchar *dest_pixels; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, RADIO_SIZE, RADIO_SIZE); + + if (pixbuf == NULL) + return NULL; + + dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + dest_pixels = gdk_pixbuf_get_pixels (pixbuf); + + for (y = 0; y < RADIO_SIZE; y++) + { + src = bit + y * RADIO_SIZE; + asrc = alpha + y * RADIO_SIZE; + dest = dest_pixels + y * dest_rowstride; + + for (x = 0; x < RADIO_SIZE; x++) + { + double dr, dg, db; + + intensity = (src[x] + 0 )/ 255.0; + + if (intensity <= 0.5) + { + /* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */ + dr = (new_color->red * intensity * 2.0) / 65535.0; + dg = (new_color->green * intensity * 2.0) / 65535.0; + db = (new_color->blue * intensity * 2.0) / 65535.0; + } + else + { + /* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */ + dr = (new_color->red + (65535 - new_color->red) * (intensity - 0.5) * 2.0) / 65535.0; + dg = (new_color->green + (65535 - new_color->green) * (intensity - 0.5) * 2.0) / 65535.0; + db = (new_color->blue + (65535 - new_color->blue) * (intensity - 0.5) * 2.0) / 65535.0; + } + + dest[0] = CLAMP_UCHAR (255 * dr); + dest[1] = CLAMP_UCHAR (255 * dg); + dest[2] = CLAMP_UCHAR (255 * db); + + dest[3] = asrc[x]; + dest += 4; + } + } + + return pixbuf; +} + +GdkPixmap * +pixbuf_to_pixmap (GtkStyle *style, + GdkPixbuf *pixbuf, + GdkScreen *screen) +{ + GdkGC *tmp_gc; + GdkPixmap *pixmap; + + pixmap = gdk_pixmap_new (gdk_screen_get_root_window (screen), + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + style->depth); + + gdk_drawable_set_colormap (pixmap, style->colormap); + + tmp_gc = gdk_gc_new (pixmap); + + gdk_pixbuf_render_to_drawable (pixbuf, pixmap, tmp_gc, 0, 0, 0, 0, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + GDK_RGB_DITHER_NORMAL, 0, 0); + + gdk_gc_unref (tmp_gc); + + return pixmap; +} + + +void +rgb_to_hls (gdouble *r, + gdouble *g, + gdouble *b) +{ + gdouble min; + gdouble max; + gdouble red; + gdouble green; + gdouble blue; + gdouble h, l, s; + gdouble delta; + + red = *r; + green = *g; + blue = *b; + + if (red > green) + { + if (red > blue) + max = red; + else + max = blue; + + if (green < blue) + min = green; + else + min = blue; + } + else + { + if (green > blue) + max = green; + else + max = blue; + + if (red < blue) + min = red; + else + min = blue; + } + + l = (max + min) / 2; + s = 0; + h = 0; + + if (max != min) + { + if (l <= 0.5) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + + delta = max -min; + if (red == max) + h = (green - blue) / delta; + else if (green == max) + h = 2 + (blue - red) / delta; + else if (blue == max) + h = 4 + (red - green) / delta; + + h *= 60; + if (h < 0.0) + h += 360; + } + + *r = h; + *g = l; + *b = s; +} + +void +hls_to_rgb (gdouble *h, + gdouble *l, + gdouble *s) +{ + gdouble hue; + gdouble lightness; + gdouble saturation; + gdouble m1, m2; + gdouble r, g, b; + + lightness = *l; + saturation = *s; + + if (lightness <= 0.5) + m2 = lightness * (1 + saturation); + else + m2 = lightness + saturation - lightness * saturation; + + m1 = 2 * lightness - m2; + + if (saturation == 0) + { + *h = lightness; + *l = lightness; + *s = lightness; + } + else + { + hue = *h + 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + r = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + r = m2; + else if (hue < 240) + r = m1 + (m2 - m1) * (240 - hue) / 60; + else + r = m1; + + hue = *h; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + g = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + g = m2; + else if (hue < 240) + g = m1 + (m2 - m1) * (240 - hue) / 60; + else + g = m1; + + hue = *h - 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + b = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + b = m2; + else if (hue < 240) + b = m1 + (m2 - m1) * (240 - hue) / 60; + else + b = m1; + + *h = r; + *l = g; + *s = b; + } +} + +void +shade (GdkColor * a, GdkColor * b, float k) +{ + gdouble red; + gdouble green; + gdouble blue; + + red = (gdouble) a->red / 65535.0; + green = (gdouble) a->green / 65535.0; + blue = (gdouble) a->blue / 65535.0; + + rgb_to_hls (&red, &green, &blue); + + green *= k; + if (green > 1.0) + green = 1.0; + else if (green < 0.0) + green = 0.0; + + blue *= k; + if (blue > 1.0) + blue = 1.0; + else if (blue < 0.0) + blue = 0.0; + + hls_to_rgb (&red, &green, &blue); + + b->red = red * 65535.0; + b->green = green * 65535.0; + b->blue = blue * 65535.0; +} + + +/**************************************************************************/ + +void +arrow_draw_hline (GdkWindow *window, + GdkGC *gc, + int x1, + int x2, + int y, + gboolean last) +{ + if (x2 - x1 < 7 && !last) /* 7 to get garretts pixels, otherwise 6 */ + { + gdk_draw_line (window, gc, x1, y, x2, y); + } + else if (last) + { + /* we don't draw "spikes" for very small arrows */ + if (x2 - x1 <= 9) + { + /*gdk_draw_line (window, gc, x1+1, y, x1+1, y); + gdk_draw_line (window, gc, x2-1, y, x2-1, y);*/ + } + else + { + gdk_draw_line (window, gc, x1+2, y, x1+2, y); + gdk_draw_line (window, gc, x2-2, y, x2-2, y); + } + } + else + { + gdk_draw_line (window, gc, x1, y, x1+2, y); + gdk_draw_line (window, gc, x2-2, y, x2, y); + } +} + +void +arrow_draw_vline (GdkWindow *window, + GdkGC *gc, + int y1, + int y2, + int x, + gboolean last) +{ + if (y2 - y1 < 7 && !last) /* 7 to get garretts pixels */ + gdk_draw_line (window, gc, x, y1, x, y2); + else if (last) + { + /* we don't draw "spikes" for very small arrows */ + if (y2 - y1 > 9) { + gdk_draw_line (window, gc, x, y1+2, x, y1+2); + gdk_draw_line (window, gc, x, y2-2, x, y2-2); + } + } + else + { + gdk_draw_line (window, gc, x, y1, x, y1+2); + gdk_draw_line (window, gc, x, y2-2, x, y2); + } +} + + + +void +draw_arrow (GdkWindow *window, + GdkGC *gc, + GdkRectangle *area, + GtkArrowType arrow_type, + gint x, + gint y, + gint width, + gint height) +{ + gint i, j; + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + if (arrow_type == GTK_ARROW_DOWN) + { + for (i = 0, j = -1; i < height; i++, j++) + arrow_draw_hline (window, gc, x + j, x + width - j - 1, y + i, i == 0); + + } + else if (arrow_type == GTK_ARROW_UP) + { + for (i = height - 1, j = -1; i >= 0; i--, j++) + arrow_draw_hline (window, gc, x + j, x + width - j - 1, y + i, i == height - 1); + } + else if (arrow_type == GTK_ARROW_LEFT) + { + for (i = width - 1, j = -1; i >= 0; i--, j++) + arrow_draw_vline (window, gc, y + j, y + height - j - 1, x + i, i == width - 1); + } + else if (arrow_type == GTK_ARROW_RIGHT) + { + for (i = 0, j = -1; i < width; i++, j++) + arrow_draw_vline (window, gc, y + j, y + height - j - 1, x + i, i == 0); + } + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); +} + +void +calculate_arrow_geometry (GtkArrowType arrow_type, + gint *x, + gint *y, + gint *width, + gint *height) +{ + gint w = *width; + gint h = *height; + + switch (arrow_type) + { + case GTK_ARROW_UP: + case GTK_ARROW_DOWN: + w += (w % 2) - 1; + h = (w / 2 + 1) + 1; + + if (h > *height) + { + h = *height; + w = 2 * (h - 1) - 1; + } + + if (arrow_type == GTK_ARROW_DOWN) + { + if (*height % 2 == 1 || h % 2 == 0) + *height += 1; + } + else + { + if (*height % 2 == 0 || h % 2 == 0) + *height -= 1; + } + break; + + case GTK_ARROW_RIGHT: + case GTK_ARROW_LEFT: + h += (h % 2) - 1; + w = (h / 2 + 1) + 1; + + if (w > *width) + { + w = *width; + h = 2 * (w - 1) - 1; + } + + if (arrow_type == GTK_ARROW_RIGHT) + { + if (*width % 2 == 1 || w % 2 == 0) + *width += 1; + } + else + { + if (*width % 2 == 0 || w % 2 == 0) + *width -= 1; + } + break; + + default: + /* should not be reached */ + break; + } + + *x += (*width - w) / 2; + *y += (*height - h) / 2; + *height = h; + *width = w; +} + + +void gtk_treeview_get_header_index (GtkTreeView *tv, GtkWidget *header, + gint *column_index, gint *columns, +gboolean *resizable) +{ + GList *list; + *column_index = *columns = 0; + list = gtk_tree_view_get_columns (tv); + + do + { + GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(list->data); + if ( column->button == header ) + { + *column_index = *columns; + *resizable = column->resizable; + } + if ( column->visible ) + (*columns)++; + } while ((list = g_list_next(list))); +} + +void gtk_clist_get_header_index (GtkCList *clist, GtkWidget *button, + gint *column_index, gint *columns) +{ + *columns = clist->columns; + int i; + + for (i=0; i<*columns; i++) + { + if (clist->column[i].button == button) + { + *column_index = i; + break; + } + } +} + +gboolean +sanitize_size (GdkWindow *window, + gint *width, + gint *height) +{ + gboolean set_bg = FALSE; + + if ((*width == -1) && (*height == -1)) + { + set_bg = GDK_IS_WINDOW (window); + gdk_window_get_size (window, width, height); + } + else if (*width == -1) + gdk_window_get_size (window, width, NULL); + else if (*height == -1) + gdk_window_get_size (window, NULL, height); + + return set_bg; +} + +static GtkRequisition default_option_indicator_size = { 7, 13 }; +static GtkBorder default_option_indicator_spacing = { 7, 5, 2, 2 }; + +void +option_menu_get_props (GtkWidget *widget, + GtkRequisition *indicator_size, + GtkBorder *indicator_spacing) +{ + GtkRequisition *tmp_size = NULL; + GtkBorder *tmp_spacing = NULL; + + if (widget) + gtk_widget_style_get (widget, "indicator_size", &tmp_size, + "indicator_spacing", &tmp_spacing, NULL); + + if (tmp_size) + { + *indicator_size = *tmp_size; + g_free (tmp_size); + } + else + *indicator_size = default_option_indicator_size; + + if (tmp_spacing) + { + *indicator_spacing = *tmp_spacing; + g_free (tmp_spacing); + } + else + *indicator_spacing = default_option_indicator_spacing; +} + +GtkWidget *special_get_ancestor(GtkWidget * widget, + GType widget_type) +{ + g_return_val_if_fail(GTK_IS_WIDGET(widget), NULL); + + while (widget && widget->parent + && !g_type_is_a(GTK_WIDGET_TYPE(widget->parent), + widget_type)) + widget = widget->parent; + + if (! + (widget && widget->parent + && g_type_is_a(GTK_WIDGET_TYPE(widget->parent), widget_type))) + return NULL; + + return widget; +} + +/* Dithered Gradient Buffers */ +static void +internel_image_buffer_free_pixels (guchar *pixels, gpointer data) +{ + g_free (pixels); +} + +static GdkPixbuf* +internal_image_buffer_new (gint width, gint height) +{ + guchar *buf; + int rowstride; + + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + rowstride = width * 3; + + buf = g_try_malloc (height * rowstride); + + if (!buf) + return NULL; + + return gdk_pixbuf_new_from_data(buf, GDK_COLORSPACE_RGB, + FALSE, 8, + width, height, rowstride, + internel_image_buffer_free_pixels, NULL); +} + +static void +internal_color_get_as_uchars(GdkColor *color, + guchar *red, + guchar *green, + guchar *blue) +{ + *red = (guchar) (color->red / 256.0); + *green = (guchar) (color->green / 256.0); + *blue = (guchar) (color->blue / 256.0); +} + +static GdkPixbuf* +internal_create_horizontal_gradient_image_buffer (gint width, gint height, + GdkColor *from, + GdkColor *to) +{ + int i; + long r, g, b, dr, dg, db; + GdkPixbuf* buffer; + guchar *ptr; + guchar *pixels; + guchar r0, g0, b0; + guchar rf, gf, bf; + int rowstride; + + buffer = internal_image_buffer_new (width, height); + + if (buffer == NULL) + return NULL; + + pixels = gdk_pixbuf_get_pixels (buffer); + ptr = pixels; + rowstride = gdk_pixbuf_get_rowstride (buffer); + + internal_color_get_as_uchars(from, &r0, &g0, &b0); + internal_color_get_as_uchars(to, &rf, &gf, &bf); + + r = r0 << 16; + g = g0 << 16; + b = b0 << 16; + + dr = ((rf-r0)<<16)/width; + dg = ((gf-g0)<<16)/width; + db = ((bf-b0)<<16)/width; + + /* render the first line */ + for (i=0; i<width; i++) + { + *(ptr++) = (guchar)(r>>16); + *(ptr++) = (guchar)(g>>16); + *(ptr++) = (guchar)(b>>16); + + r += dr; + g += dg; + b += db; + } + + /* copy the first line to the other lines */ + for (i=1; i<height; i++) + { + memcpy (&(pixels[i*rowstride]), pixels, rowstride); + } + + return buffer; +} + +static GdkPixbuf* +internal_create_vertical_gradient_image_buffer (gint width, gint height, + GdkColor *from, + GdkColor *to) +{ + gint i, j, max_block, last_block; + long r, g, b, dr, dg, db; + GdkPixbuf *buffer; + + guchar *ptr; + guchar point[4]; + + guchar r0, g0, b0; + guchar rf, gf, bf; + + gint rowstride; + guchar *pixels; + + buffer = internal_image_buffer_new (width, height); + + if (buffer == NULL) + return NULL; + + pixels = gdk_pixbuf_get_pixels (buffer); + rowstride = gdk_pixbuf_get_rowstride (buffer); + + internal_color_get_as_uchars(from, &r0, &g0, &b0); + internal_color_get_as_uchars(to, &rf, &gf, &bf); + + r = r0<<16; + g = g0<<16; + b = b0<<16; + + dr = ((rf-r0)<<16)/height; + dg = ((gf-g0)<<16)/height; + db = ((bf-b0)<<16)/height; + + max_block = width/2; + + for (i=0; i < height; i++) + { + ptr = pixels + i * rowstride; + + ptr[0] = r>>16; + ptr[1] = g>>16; + ptr[2] = b>>16; + + if (width > 1) + { + last_block = 0; + + for (j=1; j <= max_block; j *= 2) + { + memcpy (&(ptr[j*3]), ptr, j*3); + + if ((j*2) >= max_block) + { + last_block = j*2; + } + } + + if ((last_block < width) && (last_block > 0)) + { + memcpy (&(ptr[last_block*3]), ptr, (width - last_block)*3); + } + } + + r += dr; + g += dg; + b += db; + } + + return buffer; +} + +void +draw_vgradient (GdkDrawable *drawable, GdkGC *gc, GtkStyle *style, + int x, int y, int width, int height, + GdkColor *left_color, GdkColor *right_color) +{ + #ifndef ALWAYS_DITHER_GRADIENTS + gboolean dither = ((style->depth > 0) && (style->depth <= 16)); + #endif + + if ((width <= 0) || (height <= 0)) + return; + + if ( left_color == NULL || right_color == NULL ) + { + gdk_draw_rectangle (drawable, gc, TRUE, x, y, width, height); + return; + } + + #ifndef ALWAYS_DITHER_GRADIENTS + if (dither) + #endif + { + GdkPixbuf *image_buffer = NULL; + + image_buffer = internal_create_horizontal_gradient_image_buffer (width, height, left_color, right_color); + + if (image_buffer) + { + gdk_draw_pixbuf(drawable, gc, image_buffer, 0, 0, x, y, width, height, GDK_RGB_DITHER_MAX, 0, 0); + + g_object_unref(image_buffer); + } + } + #ifndef ALWAYS_DITHER_GRADIENTS + else + { + int i; + GdkColor col; + int dr, dg, db; + GdkGCValues old_values; + + gdk_gc_get_values (gc, &old_values); + + if (left_color == right_color ) + { + col = *left_color; + gdk_rgb_find_color (style->colormap, &col); + gdk_gc_set_foreground (gc, &col); + gdk_draw_rectangle (drawable, gc, TRUE, x, y, width, height); + gdk_gc_set_foreground (gc, &old_values.foreground); + return; + } + + col = *left_color; + dr = (right_color->red - left_color->red) / width; + dg = (right_color->green - left_color->green) / width; + db = (right_color->blue - left_color->blue) / width; + + for (i = 0; i < width; i++) + { + gdk_rgb_find_color (style->colormap, &col); + + gdk_gc_set_foreground (gc, &col); + gdk_draw_line (drawable, gc, x + i, y, x + i, y + height - 1); + + col.red += dr; + col.green += dg; + col.blue += db; + } + + gdk_gc_set_foreground (gc, &old_values.foreground); + } + #endif +} + +void +draw_hgradient (GdkDrawable *drawable, GdkGC *gc, GtkStyle *style, + int x, int y, int width, int height, + GdkColor *top_color, GdkColor *bottom_color) +{ + #ifndef ALWAYS_DITHER_GRADIENTS + gboolean dither = ((style->depth > 0) && (style->depth <= 16)); + #endif + + if ((width <= 0) || (height <= 0)) + return; + + #ifndef ALWAYS_DITHER_GRADIENTS + if (dither) + #endif + { + GdkPixbuf *image_buffer = NULL; + + image_buffer = internal_create_vertical_gradient_image_buffer (width, height, top_color, bottom_color); + + if (image_buffer) + { + gdk_draw_pixbuf(drawable, gc, image_buffer, 0, 0, x, y, width, height, GDK_RGB_DITHER_MAX, 0, 0); + + g_object_unref(image_buffer); + } + } + #ifndef ALWAYS_DITHER_GRADIENTS + else + { + int i; + GdkColor col; + int dr, dg, db; + GdkGCValues old_values; + + gdk_gc_get_values (gc, &old_values); + + if (top_color == bottom_color ) + { + col = *top_color; + gdk_rgb_find_color (style->colormap, &col); + gdk_gc_set_foreground (gc, &col); + gdk_draw_rectangle (drawable, gc, TRUE, x, y, width, height); + gdk_gc_set_foreground (gc, &old_values.foreground); + return; + } + + col = *top_color; + dr = (bottom_color->red - top_color->red) / height; + dg = (bottom_color->green - top_color->green) / height; + db = (bottom_color->blue - top_color->blue) / height; + + for (i = 0; i < height; i++) + { + gdk_rgb_find_color (style->colormap, &col); + + gdk_gc_set_foreground (gc, &col); + gdk_draw_line (drawable, gc, x, y + i, x + width - 1, y + i); + + col.red += dr; + col.green += dg; + col.blue += db; + } + + gdk_gc_set_foreground (gc, &old_values.foreground); + } + #endif +} + +void blend (GdkColormap *colormap, + GdkColor *a, GdkColor *b, GdkColor *c, int alpha) +{ + int inAlpha = 100-alpha; + c->red = (a->red * alpha + b->red * inAlpha) / 100; + c->green = (a->green * alpha + b->green * inAlpha) / 100; + c->blue = (a->blue * alpha + b->blue * inAlpha) / 100; + + gdk_rgb_find_color (colormap, c); +} + +GtkWidget *get_parent_window (GtkWidget *widget) +{ + GtkWidget *parent = widget->parent; + + while (parent && GTK_WIDGET_NO_WINDOW (parent)) + parent = parent->parent; + + return parent; +} + +GdkColor *get_parent_bgcolor (GtkWidget *widget) +{ + GtkWidget *parent = get_parent_window (widget); + + if (parent && parent->style) + return &parent->style->bg[GTK_STATE_NORMAL]; + + return NULL; +} + +GtkWidget * +find_combo_box_widget (GtkWidget * widget) +{ + GtkWidget *result = NULL; + + if (widget && !GTK_IS_COMBO_BOX_ENTRY (widget)) + { + if (GTK_IS_COMBO_BOX (widget)) + result = widget; + else + result = find_combo_box_widget(widget->parent); + } + + return result; +} + +gboolean +is_combo_box (GtkWidget * widget) +{ + return (find_combo_box_widget(widget) != NULL); +} diff --git a/libs/clearlooks/support.h b/libs/clearlooks/support.h new file mode 100644 index 0000000000..a1430b40d0 --- /dev/null +++ b/libs/clearlooks/support.h @@ -0,0 +1,110 @@ +#include <gtk/gtk.h> +#include <math.h> +#include <string.h> + +/* GTK 2.2 compatibility */ +#ifndef GTK_IS_COMBO_BOX_ENTRY + #define GTK_IS_COMBO_BOX_ENTRY(x) 0 +#endif +#ifndef GTK_IS_COMBO_BOX + #define GTK_IS_COMBO_BOX(x) 0 +#endif + +#define RADIO_SIZE 13 +#define CHECK_SIZE 13 + +GtkTextDirection +get_direction (GtkWidget *widget); + +GdkPixbuf * +generate_bit (unsigned char alpha[], + GdkColor *color, + double mult); + +GdkPixbuf * +colorize_bit (unsigned char *bit, + unsigned char *alpha, + GdkColor *new_color); + +GdkPixmap * +pixbuf_to_pixmap (GtkStyle *style, + GdkPixbuf *pixbuf, + GdkScreen *screen); + +gboolean +sanitize_size (GdkWindow *window, + gint *width, + gint *height); + +void +rgb_to_hls (gdouble *r, + gdouble *g, + gdouble *b); + +void +hls_to_rgb (gdouble *h, + gdouble *l, + gdouble *s); + +void +shade (GdkColor * a, GdkColor * b, float k); + +void +draw_hgradient (GdkDrawable *drawable, GdkGC *gc, GtkStyle *style, + int x, int y, int width, int height, + GdkColor *top_color, GdkColor *bottom_color); + +void +draw_vgradient (GdkDrawable *drawable, GdkGC *gc, GtkStyle *style, + int x, int y, int width, int height, + GdkColor *left_color, GdkColor *right_color); + +void +arrow_draw_hline (GdkWindow *window, + GdkGC *gc, + int x1, + int x2, + int y, + gboolean last); + +void +arrow_draw_vline (GdkWindow *window, + GdkGC *gc, + int y1, + int y2, + int x, + gboolean last); + +void +draw_arrow (GdkWindow *window, + GdkGC *gc, + GdkRectangle *area, + GtkArrowType arrow_type, + gint x, + gint y, + gint width, + gint height); + +void +calculate_arrow_geometry (GtkArrowType arrow_type, + gint *x, + gint *y, + gint *width, + gint *height); + +GtkWidget *special_get_ancestor(GtkWidget * widget, + GType widget_type); + +void blend (GdkColormap *colormap, + GdkColor *a, GdkColor *b, GdkColor *c, int alpha); + +GtkWidget *get_parent_window (GtkWidget *widget); + +GdkColor *get_parent_bgcolor (GtkWidget *widget); + +gboolean is_combo_box (GtkWidget * widget); + +GtkWidget *find_combo_box_widget (GtkWidget * widget); + +void gtk_clist_get_header_index (GtkCList *clist, GtkWidget *button, + gint *column_index, gint *columns); diff --git a/libs/fst/SConscript b/libs/fst/SConscript index 2cbfb94a9f..771de86dc8 100644 --- a/libs/fst/SConscript +++ b/libs/fst/SConscript @@ -2,6 +2,7 @@ import os import os.path +import sys import glob fst_src = glob.glob('*.c') @@ -22,7 +23,12 @@ c = fst.Object ('vstwin', 'vstwin.c') d = fst.Object ('vsti', 'vsti.c') if fst['VST']: - Default([hackSDK,a,b,c,d]) + if os.access ('vst/aeffectx.h', os.F_OK): + Default([hackSDK,a,b,c,d]) + else: + print 'You have not installed the VST SDK in the correct location.' + print 'Please see http://ardour.org/building_vst_support for more information' + sys.exit (1) env.Alias('tarball', env.Distribute (env['DISTTREE'], fst_src + ['SConscript', diff --git a/libs/gtkmm2ext/SConscript b/libs/gtkmm2ext/SConscript index cf513eb5a1..e654b6cb52 100644 --- a/libs/gtkmm2ext/SConscript +++ b/libs/gtkmm2ext/SConscript @@ -55,7 +55,7 @@ utils.cc version.cc """) -gtkmm2ext.VersionBuild(['version.cc','gtkmm2ext/version.h'], 'SConscript') +gtkmm2ext.VersionBuild(['version.cc','gtkmm2ext/version.h'], []) gtkmm2ext.Append(CCFLAGS="-D_REENTRANT") gtkmm2ext.Append(CCFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") diff --git a/libs/gtkmm2ext/barcontroller.cc b/libs/gtkmm2ext/barcontroller.cc index eefe6ca843..734c4b77e2 100644 --- a/libs/gtkmm2ext/barcontroller.cc +++ b/libs/gtkmm2ext/barcontroller.cc @@ -423,6 +423,7 @@ BarController::switch_to_spinner () remove (); add (spinner); spinner.show (); + spinner.select_region (0, spinner.get_text_length()); spinner.grab_focus (); switching = false; diff --git a/libs/gtkmm2ext/click_box.cc b/libs/gtkmm2ext/click_box.cc index efce988c29..3ab7ea883c 100644 --- a/libs/gtkmm2ext/click_box.cc +++ b/libs/gtkmm2ext/click_box.cc @@ -38,17 +38,18 @@ ClickBox::ClickBox (Gtk::Adjustment *adjp, const string &name, bool round_to_ste twidth = 0; theight = 0; - set_name (name); + add_events (Gdk::BUTTON_RELEASE_MASK| Gdk::BUTTON_PRESS_MASK| Gdk::ENTER_NOTIFY_MASK| Gdk::LEAVE_NOTIFY_MASK); - set_label (); get_adjustment().signal_value_changed().connect (mem_fun (*this, &ClickBox::set_label)); - + signal_style_changed().connect (mem_fun (*this, &ClickBox::style_changed)); signal_button_press_event().connect (mem_fun (*this, &ClickBox::button_press_handler)); signal_button_release_event().connect (mem_fun (*this, &ClickBox::button_release_handler)); + set_name (name); + set_label (); } ClickBox::~ClickBox () @@ -102,6 +103,14 @@ ClickBox::set_label () queue_draw (); } +void +ClickBox::style_changed (const Glib::RefPtr<Gtk::Style>& ignored) +{ + + layout->context_changed (); + layout->get_pixel_size (twidth, theight); +} + bool ClickBox::on_expose_event (GdkEventExpose *ev) { @@ -136,7 +145,7 @@ ClickBox::on_expose_event (GdkEventExpose *ev) win->draw_rectangle (bg_gc, true, draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height); if (twidth && theight) { - win->draw_layout (fg_gc, width - (twidth + 2), (height - theight) + 2, layout); + win->draw_layout (fg_gc, (width - twidth) / 2, (height - theight) / 2, layout); } } diff --git a/libs/gtkmm2ext/gtkmm2ext/click_box.h b/libs/gtkmm2ext/gtkmm2ext/click_box.h index c6f2922f3f..e4aee36ebe 100644 --- a/libs/gtkmm2ext/gtkmm2ext/click_box.h +++ b/libs/gtkmm2ext/gtkmm2ext/click_box.h @@ -54,6 +54,7 @@ class ClickBox : public Gtk::DrawingArea, public AutoSpin int theight; void set_label (); + void style_changed (const Glib::RefPtr<Gtk::Style> &); bool button_press_handler (GdkEventButton *); bool button_release_handler (GdkEventButton *); diff --git a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h index 14af137680..a692e64c9c 100644 --- a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h +++ b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h @@ -101,6 +101,8 @@ class UI : public Receiver, public AbstractUI<UIRequest> bool caller_is_ui_thread (); + static pthread_t thread_id() { return gui_thread; } + /* Gtk-UI specific interfaces */ bool running (); diff --git a/libs/gtkmm2ext/tearoff.cc b/libs/gtkmm2ext/tearoff.cc index 529cca15e3..e4a9207195 100644 --- a/libs/gtkmm2ext/tearoff.cc +++ b/libs/gtkmm2ext/tearoff.cc @@ -109,7 +109,7 @@ TearOff::tearoff_click (GdkEventButton* ev) own_window.show_all (); hide (); Detach (); - return TRUE; + return true; } gint @@ -121,19 +121,25 @@ TearOff::close_click (GdkEventButton* ev) own_window.hide (); show_all (); Attach (); - return TRUE; + return true; } gint TearOff::window_button_press (GdkEventButton* ev) { + if (dragging) { + dragging = false; + own_window.remove_modal_grab(); + return true; + } + dragging = true; drag_x = ev->x_root; drag_y = ev->y_root; own_window.add_modal_grab(); - return TRUE; + return true; } gint @@ -141,7 +147,7 @@ TearOff::window_button_release (GdkEventButton* ev) { dragging = false; own_window.remove_modal_grab(); - return TRUE; + return true; } gint @@ -163,7 +169,7 @@ TearOff::window_motion (GdkEventMotion* ev) own_window.get_pointer (mx, my); if (!dragging) { - return TRUE; + return true; } x_delta = ev->x_root - drag_x; @@ -175,7 +181,7 @@ TearOff::window_motion (GdkEventMotion* ev) drag_x = ev->x_root; drag_y = ev->y_root; - return TRUE; + return true; } bool diff --git a/libs/libsndfile/SConscript b/libs/libsndfile/SConscript index b1c29e5487..f8e9fc5ecb 100644 --- a/libs/libsndfile/SConscript +++ b/libs/libsndfile/SConscript @@ -25,7 +25,7 @@ if conf.CheckCHeader('/System/Library/Frameworks/CoreServices.framework/Headers/ sndfile = conf.Finish() -libsndfile = sndfile.SharedLibrary('sndfile', sndfile_files) +libsndfile = sndfile.SharedLibrary('sndfile-ardour', sndfile_files) sndfile_h = sndfile.Command('src/sndfile.h', ['src/sndfile.h.in'], 'cd libs/libsndfile && ./configure && cd -', ENV=os.environ) diff --git a/libs/midi++2/SConscript b/libs/midi++2/SConscript index 65d0882dce..477d49c6ca 100644 --- a/libs/midi++2/SConscript +++ b/libs/midi++2/SConscript @@ -51,7 +51,7 @@ elif env['SYSMIDI'] == 'CoreMIDI': midi2.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") midi2.Append(CCFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") -midi2.VersionBuild(['version.cc','midi++/version.h'], 'SConscript') +midi2.VersionBuild(['version.cc','midi++/version.h'], []) libmidi2 = midi2.SharedLibrary('midi++', [ sources, sysdep_src ]) diff --git a/libs/midi++2/midi++/factory.h b/libs/midi++2/midi++/factory.h index e29d7543f8..1d5c7e0b30 100644 --- a/libs/midi++2/midi++/factory.h +++ b/libs/midi++2/midi++/factory.h @@ -30,9 +30,8 @@ namespace MIDI { class PortFactory { public: Port *create_port (PortRequest &req, void* data); - - static void add_port_request (std::vector<PortRequest *> &reqs, - const std::string &reqstr); + + static bool ignore_duplicate_devices (Port::Type); }; } // namespace MIDI diff --git a/libs/midi++2/midifactory.cc b/libs/midi++2/midifactory.cc index d8119e362e..de4a246bcf 100644 --- a/libs/midi++2/midifactory.cc +++ b/libs/midi++2/midifactory.cc @@ -89,20 +89,28 @@ PortFactory::create_port (PortRequest &req, void* data) return port; } -void -PortFactory::add_port_request (vector<PortRequest *> &reqs, - const string &str) - +bool +PortFactory::ignore_duplicate_devices (Port::Type type) { - PortRequest *req; + bool ret = false; - req = new PortRequest; - req->devname = strdup (str.c_str()); - req->tagname = strdup (str.c_str()); + switch (type) { +#ifdef WITH_ALSA + case Port::ALSA_Sequencer: + ret = true; + break; +#endif // WITH_ALSA - req->mode = O_RDWR; - req->type = Port::ALSA_RawMidi; +#if WITH_COREMIDI + case Port::CoreMidi_MidiPort: + ret = true; + break; +#endif // WITH_COREMIDI + + default: + break; + } - reqs.push_back (req); + return ret; } diff --git a/libs/midi++2/midimanager.cc b/libs/midi++2/midimanager.cc index bfe8f147b6..970674232d 100644 --- a/libs/midi++2/midimanager.cc +++ b/libs/midi++2/midimanager.cc @@ -66,41 +66,43 @@ Manager::add_port (PortRequest &req) PortMap::iterator existing; pair<string, Port *> newpair; - if ((existing = ports_by_device.find (req.devname)) != - ports_by_device.end()) { - - port = (*existing).second; - if (port->mode() == req.mode) { + if (!PortFactory::ignore_duplicate_devices (req.type)) { + + if ((existing = ports_by_device.find (req.devname)) != ports_by_device.end()) { + + port = (*existing).second; + + if (port->mode() == req.mode) { + + /* Same mode - reuse the port, and just + create a new tag entry. + */ + + newpair.first = req.tagname; + newpair.second = port; + + ports_by_tag.insert (newpair); + return port; + } - /* Same mode - reuse the port, and just - create a new tag entry. + /* If the existing is duplex, and this request + is not, then fail, because most drivers won't + allow opening twice with duplex and non-duplex + operation. */ - - newpair.first = req.tagname; - newpair.second = port; - - ports_by_tag.insert (newpair); - return port; - } - - /* If the existing is duplex, and this request - is not, then fail, because most drivers won't - allow opening twice with duplex and non-duplex - operation. - */ - - if ((req.mode == O_RDWR && port->mode() != O_RDWR) || - (req.mode != O_RDWR && port->mode() == O_RDWR)) { - error << "MIDIManager: port tagged \"" - << req.tagname - << "\" cannot be opened duplex and non-duplex" - << endmsg; - return 0; + + if ((req.mode == O_RDWR && port->mode() != O_RDWR) || + (req.mode != O_RDWR && port->mode() == O_RDWR)) { + error << "MIDIManager: port tagged \"" + << req.tagname + << "\" cannot be opened duplex and non-duplex" + << endmsg; + return 0; + } + + /* modes must be different or complementary */ } - - /* modes must be different or complementary */ } - port = factory.create_port (req, api_data); if (port == 0) { diff --git a/libs/pbd/SConscript b/libs/pbd/SConscript index 4b15dd70d1..f474834fd8 100644 --- a/libs/pbd/SConscript +++ b/libs/pbd/SConscript @@ -55,7 +55,7 @@ pbd.Merge ([ libraries['sigc2'], libraries['glibmm2'], libraries['glib2'] ]) -pbd.VersionBuild(['version.cc','pbd/version.h'], 'SConscript') +pbd.VersionBuild(['version.cc','pbd/version.h'], []) libpbd = pbd.SharedLibrary('pbd', pbd_files) Default(libpbd) diff --git a/libs/pbd/controllable.cc b/libs/pbd/controllable.cc index 80c6811e6a..2264a955ae 100644 --- a/libs/pbd/controllable.cc +++ b/libs/pbd/controllable.cc @@ -6,7 +6,7 @@ using namespace PBD; -sigc::signal<void,Controllable*> Controllable::GoingAway; +sigc::signal<void,Controllable*> Controllable::Destroyed; sigc::signal<bool,Controllable*> Controllable::StartLearning; sigc::signal<void,Controllable*> Controllable::StopLearning; diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc index 60d39c91e2..832c54acd8 100644 --- a/libs/pbd/convert.cc +++ b/libs/pbd/convert.cc @@ -19,6 +19,10 @@ #include <cmath> #include <stdint.h> +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include <inttypes.h> #include "pbd/convert.h" @@ -106,12 +110,12 @@ atof (const string& s) } vector<string> -internationalize (const char **array) +internationalize (const char *package_name, const char **array) { vector<string> v; for (uint32_t i = 0; array[i]; ++i) { - v.push_back (_(array[i])); + v.push_back (dgettext(package_name, array[i])); } return v; @@ -190,23 +194,44 @@ url_decode (string& url) } } +#if 0 string length2string (const int32_t frames, const float sample_rate) { - int secs = (int) (frames / sample_rate); - int hrs = secs / 3600; + int32_t secs = (int32_t) (frames / sample_rate); + int32_t hrs = secs / 3600; secs -= (hrs * 3600); - int mins = secs / 60; + int32_t mins = secs / 60; secs -= (mins * 60); - int total_secs = (hrs * 3600) + (mins * 60) + secs; - int frames_remaining = (int) floor (frames - (total_secs * sample_rate)); + int32_t total_secs = (hrs * 3600) + (mins * 60) + secs; + int32_t frames_remaining = (int) floor (frames - (total_secs * sample_rate)); float fractional_secs = (float) frames_remaining / sample_rate; char duration_str[32]; - sprintf (duration_str, "%02d:%02d:%05.2f", hrs, mins, (float) secs + fractional_secs); + sprintf (duration_str, "%02" PRIi32 ":%02" PRIi32 ":%05.2f", hrs, mins, (float) secs + fractional_secs); return duration_str; } +#endif + +string +length2string (const int64_t frames, const double sample_rate) +{ + int64_t secs = (int64_t) floor (frames / sample_rate); + int64_t hrs = secs / 3600LL; + secs -= (hrs * 3600LL); + int64_t mins = secs / 60LL; + secs -= (mins * 60LL); + + int64_t total_secs = (hrs * 3600LL) + (mins * 60LL) + secs; + int64_t frames_remaining = (int64_t) floor (frames - (total_secs * sample_rate)); + float fractional_secs = (float) frames_remaining / sample_rate; + + char duration_str[64]; + sprintf (duration_str, "%02" PRIi64 ":%02" PRIi64 ":%05.2f", hrs, mins, (float) secs + fractional_secs); + + return duration_str; +} } // namespace PBD diff --git a/libs/pbd/pbd/command.h b/libs/pbd/pbd/command.h index 7c367e7462..8044b51a83 100644 --- a/libs/pbd/pbd/command.h +++ b/libs/pbd/pbd/command.h @@ -21,10 +21,9 @@ #ifndef __lib_pbd_command_h__ #define __lib_pbd_command_h__ -#include <pbd/stateful.h> -#include <pbd/destructible.h> +#include <pbd/statefuldestructible.h> -class Command : public Stateful, public PBD::Destructible +class Command : public PBD::StatefulDestructible { public: virtual ~Command() {} diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index ff8f8a9b52..c88eb298bc 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -6,17 +6,16 @@ #include <sigc++/trackable.h> #include <sigc++/signal.h> -#include <pbd/stateful.h> -#include <pbd/id.h> +#include <pbd/statefuldestructible.h> class XMLNode; namespace PBD { -class Controllable : public virtual sigc::trackable, public Stateful { +class Controllable : public PBD::StatefulDestructible { public: Controllable (std::string name); - virtual ~Controllable() { GoingAway (this); } + virtual ~Controllable() { Destroyed (this); } virtual void set_value (float) = 0; virtual float get_value (void) const = 0; @@ -25,11 +24,11 @@ class Controllable : public virtual sigc::trackable, public Stateful { sigc::signal<void> LearningFinished; - static sigc::signal<void,Controllable*> GoingAway; - static sigc::signal<bool,PBD::Controllable*> StartLearning; static sigc::signal<void,PBD::Controllable*> StopLearning; + static sigc::signal<void,Controllable*> Destroyed; + sigc::signal<void> Changed; int set_state (const XMLNode&); diff --git a/libs/pbd/pbd/convert.h b/libs/pbd/pbd/convert.h index 12e63ba6fc..55006529ae 100644 --- a/libs/pbd/pbd/convert.h +++ b/libs/pbd/pbd/convert.h @@ -31,9 +31,10 @@ int atoi (const std::string&); double atof (const std::string&); void url_decode (std::string&); -std::string length2string (const int32_t frames, const float sample_rate); +// std::string length2string (const int32_t frames, const float sample_rate); +std::string length2string (const int64_t frames, const double sample_rate); -std::vector<std::string> internationalize (const char **); +std::vector<std::string> internationalize (const char *, const char **); } //namespace PBD diff --git a/libs/pbd/pbd/crossthread.h b/libs/pbd/pbd/crossthread.h new file mode 100644 index 0000000000..413dea024e --- /dev/null +++ b/libs/pbd/pbd/crossthread.h @@ -0,0 +1,38 @@ +#ifndef __pbd__crossthread_h__ +#define __pbd__crossthread_h__ + +#include <pbd/abstract_ui.h> +#include <sigc++/sigc++.h> +#include <pthread.h> + +template<class RequestType> +void +call_slot_from_thread_or_dispatch_it (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot) +{ + /* when called, this function will determine whether the calling thread + is the same as thread specified by the first argument. if it is, + the we execute the slot. if not, we ask the interface given by the second + argument to call the slot. + */ + + if (pthread_self() == thread_id) { + theSlot (); + } else { + ui.call_slot (theSlot); + } +} + +template<class RequestType> +sigc::slot<void> +crossthread_safe (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot) +{ + /* this function returns a slot that will ensure that theSlot is either + called by the specified thread or passed to the interface via + AbstractUI::call_slot(). + */ + + return sigc::bind (sigc::ptr_fun (call_slot_from_thread_or_dispatch_it<RequestType>), + thread_id, ui, theSlot); +} + +#endif /* __pbd__crossthread_h__ */ diff --git a/libs/pbd/pbd/destructible.h b/libs/pbd/pbd/destructible.h index 6692ff564c..7c50806334 100644 --- a/libs/pbd/pbd/destructible.h +++ b/libs/pbd/pbd/destructible.h @@ -5,14 +5,22 @@ namespace PBD { -class Destructible : public virtual sigc::trackable { - public: - Destructible() {} - virtual ~Destructible () {} +/* be very very careful using this class. it does not inherit from sigc::trackable and thus + should only be used in multiple-inheritance situations involving another type + that does inherit from sigc::trackable (or sigc::trackable itself) +*/ +class ThingWithGoingAway { + public: + virtual ~ThingWithGoingAway () {} sigc::signal<void> GoingAway; +}; +class Destructible : public sigc::trackable, public ThingWithGoingAway { + public: + virtual ~Destructible () {} void drop_references () const { GoingAway(); } + }; } diff --git a/libs/pbd/pbd/memento_command.h b/libs/pbd/pbd/memento_command.h index d42972d546..fe1aa8e7d0 100644 --- a/libs/pbd/pbd/memento_command.h +++ b/libs/pbd/pbd/memento_command.h @@ -28,19 +28,11 @@ using std::endl; #include <pbd/command.h> #include <pbd/stacktrace.h> #include <pbd/xml++.h> +#include <pbd/shiva.h> + #include <sigc++/slot.h> #include <typeinfo> -/* grrr, strict C++ says that static member functions are not C functions, but we also want - to be able to pack this into a sigc::ptr_fun and not sigc::mem_fun, so we have to make - it a genuine function rather than a member. -*/ - -static void object_death (Command* mc) { - cerr << "\n\n\n---> OBJECT DEATH FIRED FOR " << mc << endl; - delete mc; -} - /** This command class is initialized with before and after mementos * (from Stateful::get_state()), so undo becomes restoring the before * memento, and redo is restoring the after memento. @@ -55,8 +47,8 @@ class MementoCommand : public Command XMLNode *after ) : obj(object), before(before), after(after) { - cerr << "MC @ " << this << " is a " << typeid (obj_T).name() << endl; - obj.GoingAway.connect (sigc::bind (sigc::ptr_fun (object_death), static_cast<Command*>(this))); + /* catch destruction of the object */ + new PBD::Shiva<obj_T,MementoCommand<obj_T> > (object, *this); } ~MementoCommand () { diff --git a/libs/pbd/pbd/rcu.h b/libs/pbd/pbd/rcu.h index e81db8ba87..a8f3cdd5bc 100644 --- a/libs/pbd/pbd/rcu.h +++ b/libs/pbd/pbd/rcu.h @@ -17,7 +17,7 @@ class RCUManager virtual ~RCUManager() { delete m_rcu_value; } - boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (&m_rcu_value)); } + boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (the_pointer())); } virtual boost::shared_ptr<T> write_copy () = 0; virtual bool update (boost::shared_ptr<T> new_value) = 0; diff --git a/libs/pbd/pbd/shiva.h b/libs/pbd/pbd/shiva.h index 5110f48332..53b613ea2b 100644 --- a/libs/pbd/pbd/shiva.h +++ b/libs/pbd/pbd/shiva.h @@ -5,28 +5,96 @@ namespace PBD { -template<typename ObjectWithGoingAway, typename ObjectToBeDestroyed> - /* named after the Hindu god Shiva, The Destroyer */ +template<typename ObjectWithGoingAway, typename ObjectToBeDestroyed> class Shiva { public: Shiva (ObjectWithGoingAway& emitter, ObjectToBeDestroyed& receiver) { /* if the emitter goes away, destroy the receiver */ - _connection1 = emitter.GoingAway.connect + _connection = emitter.GoingAway.connect (sigc::bind (sigc::mem_fun (*this, &Shiva<ObjectWithGoingAway,ObjectToBeDestroyed>::destroy), &receiver)); + } + + ~Shiva() { + forget (); + } + + private: + sigc::connection _connection; + + void destroy (ObjectToBeDestroyed* obj) { + delete obj; + forget (); + } + + void forget () { + _connection.disconnect (); + } + +}; + +template<typename ObjectWithGoingAway, typename ObjectToBeDestroyed> +class ProxyShiva { + public: + ProxyShiva (ObjectWithGoingAway& emitter, ObjectToBeDestroyed& receiver, void (*callback)(ObjectToBeDestroyed*, ObjectWithGoingAway*)) { + + /* if the emitter goes away, destroy the receiver */ + + _callback = callback; + _callback_argument1 = &receiver; + _callback_argument2 = &emitter; + + _connection = emitter.GoingAway.connect + (sigc::bind (sigc::mem_fun + (*this, &ProxyShiva<ObjectWithGoingAway,ObjectToBeDestroyed>::destroy), + &receiver)); + } + + ~ProxyShiva() { + forget (); + } + + private: + sigc::connection _connection; + void (*_callback) (ObjectToBeDestroyed*, ObjectWithGoingAway*); + ObjectToBeDestroyed* _callback_argument1; + ObjectWithGoingAway* _callback_argument2; + + void destroy (ObjectToBeDestroyed* obj) { + /* callback must destroy obj if appropriate, not done here */ + _callback (obj, _callback_argument2); + forget (); + } + + void forget () { + _connection.disconnect (); + } +}; + +template<typename ObjectWithGoingAway, typename ObjectToBeDestroyed> +class PairedShiva { + public: + PairedShiva (ObjectWithGoingAway& emitter, ObjectToBeDestroyed& receiver) { + + /* if the emitter goes away, destroy the receiver */ + + _connection1 = emitter.GoingAway.connect + (sigc::bind (sigc::mem_fun + (*this, &PairedShiva<ObjectWithGoingAway,ObjectToBeDestroyed>::destroy), + &receiver)); /* if the receiver goes away, forget all this nonsense */ _connection2 = receiver.GoingAway.connect - (sigc::mem_fun (*this, &Shiva<ObjectWithGoingAway,ObjectToBeDestroyed>::forget)); + (sigc::mem_fun (*this, &PairedShiva<ObjectWithGoingAway,ObjectToBeDestroyed>::forget)); } - ~Shiva() { + ~PairedShiva() { forget (); } diff --git a/libs/pbd/pbd/statefuldestructible.h b/libs/pbd/pbd/statefuldestructible.h index e78cc4bdaa..708c10fc8e 100644 --- a/libs/pbd/pbd/statefuldestructible.h +++ b/libs/pbd/pbd/statefuldestructible.h @@ -5,9 +5,21 @@ #include <pbd/destructible.h> namespace PBD { + class StatefulDestructible : public Stateful, public Destructible { }; + +/* be very very careful using this class. it does not inherit from sigc::trackable and thus + should only be used in multiple-inheritance situations involving another type + that does inherit from sigc::trackable (or sigc::trackable itself) +*/ + +class StatefulThingWithGoingAway : public Stateful, public ThingWithGoingAway +{ +}; + } + #endif /* __pbd_stateful_destructible_h__ */ diff --git a/libs/pbd/pbd/undo.h b/libs/pbd/pbd/undo.h index 943c115af2..eb46750e4f 100644 --- a/libs/pbd/pbd/undo.h +++ b/libs/pbd/pbd/undo.h @@ -70,9 +70,11 @@ class UndoTransaction : public Command struct timeval _timestamp; std::string _name; bool _clearing; + + friend void command_death (UndoTransaction*, Command *); }; -class UndoHistory +class UndoHistory : public sigc::trackable { public: UndoHistory(); @@ -95,6 +97,8 @@ class UndoHistory XMLNode &get_state(); void save_state(); + sigc::signal<void> Changed; + private: bool _clearing; std::list<UndoTransaction*> UndoList; diff --git a/libs/pbd/pbd/xml++.h b/libs/pbd/pbd/xml++.h index 5dcb4f084a..70e231e717 100644 --- a/libs/pbd/pbd/xml++.h +++ b/libs/pbd/pbd/xml++.h @@ -87,9 +87,10 @@ public: const string & set_content (const string &); XMLNode *add_content(const string & = string()); - const XMLNodeList & children (const string & = string()) const; + const XMLNodeList & children (const string& str = string()) const; XMLNode *add_child (const char *); XMLNode *add_child_copy (const XMLNode&); + XMLNode *child (const char*) const; void add_child_nocopy (XMLNode&); const XMLPropertyList & properties() const { return _proplist; }; diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index af408a24a4..277b83bfce 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -24,31 +24,13 @@ #include <pbd/undo.h> #include <pbd/xml++.h> +#include <pbd/shiva.h> #include <sigc++/bind.h> using namespace std; using namespace sigc; -/* grrr, strict C++ says that static member functions are not C functions, but we also want - to be able to pack this into a sigc::ptr_fun and not sigc::mem_fun, so we have to make - it a genuine function rather than a member. -*/ - -static void command_death (UndoTransaction* ut, Command* c) -{ - if (ut->clearing()) { - return; - } - - ut->remove_command (c); - - if (ut->empty()) { - delete ut; - } -} - - UndoTransaction::UndoTransaction () { _clearing = false; @@ -68,6 +50,20 @@ UndoTransaction::~UndoTransaction () clear (); } +void +command_death (UndoTransaction* ut, Command* c) +{ + if (ut->clearing()) { + return; + } + + ut->remove_command (c); + + if (ut->empty()) { + delete ut; + } +} + UndoTransaction& UndoTransaction::operator= (const UndoTransaction& rhs) { @@ -81,7 +77,8 @@ UndoTransaction::operator= (const UndoTransaction& rhs) void UndoTransaction::add_command (Command *const action) { - action->GoingAway.connect (bind (sigc::ptr_fun (command_death), this, const_cast<Command*>(action))); + /* catch death */ + new PBD::ProxyShiva<Command,UndoTransaction> (*action, *this, &command_death); actions.push_back (action); } @@ -160,6 +157,8 @@ UndoHistory::add (UndoTransaction* const ut) UndoList.push_back (ut); /* we are now owners of the transaction */ + + Changed (); /* EMIT SIGNAL */ } void @@ -171,6 +170,8 @@ UndoHistory::remove (UndoTransaction* const ut) UndoList.remove (ut); RedoList.remove (ut); + + Changed (); /* EMIT SIGNAL */ } void @@ -185,6 +186,8 @@ UndoHistory::undo (unsigned int n) ut->undo (); RedoList.push_back (ut); } + + Changed (); /* EMIT SIGNAL */ } void @@ -199,6 +202,8 @@ UndoHistory::redo (unsigned int n) ut->redo (); UndoList.push_back (ut); } + + Changed (); /* EMIT SIGNAL */ } void @@ -207,6 +212,9 @@ UndoHistory::clear_redo () _clearing = true; RedoList.clear (); _clearing = false; + + Changed (); /* EMIT SIGNAL */ + } void @@ -215,6 +223,8 @@ UndoHistory::clear_undo () _clearing = true; UndoList.clear (); _clearing = false; + + Changed (); /* EMIT SIGNAL */ } void @@ -222,6 +232,8 @@ UndoHistory::clear () { clear_undo (); clear_redo (); + + Changed (); /* EMIT SIGNAL */ } XMLNode & UndoHistory::get_state() diff --git a/libs/pbd/whitespace.cc b/libs/pbd/whitespace.cc index e35a8a8c0e..53616133ad 100644 --- a/libs/pbd/whitespace.cc +++ b/libs/pbd/whitespace.cc @@ -11,6 +11,10 @@ strip_whitespace_edges (string& str) len = str.length(); + if (len == 1) { + return; + } + /* strip front */ for (i = 0; i < len; ++i) { @@ -19,12 +23,21 @@ strip_whitespace_edges (string& str) } } + if (i == len) { + /* its all whitespace, not much we can do */ + return; + } + /* strip back */ if (len > 1) { s = i; i = len - 1; + + if (s == i) { + return; + } do { if (isgraph (str[i]) || i == 0) { diff --git a/libs/pbd/xml++.cc b/libs/pbd/xml++.cc index 03fa116279..8d783d59f2 100644 --- a/libs/pbd/xml++.cc +++ b/libs/pbd/xml++.cc @@ -110,7 +110,7 @@ XMLTree::write(void) const doc = xmlNewDoc((xmlChar *) "1.0"); xmlSetDocCompressMode(doc, _compression); writenode(doc, _root, doc->children, 1); - result = xmlSaveFormatFile(_filename.c_str(), doc, 1); + result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1); xmlFreeDoc(doc); if (result == -1) { @@ -216,13 +216,35 @@ XMLNode::set_content(const string & c) return _content; } +XMLNode* +XMLNode::child (const char *name) const +{ + /* returns first child matching name */ + + XMLNodeConstIterator cur; + + if (name == 0) { + return 0; + } + + for (cur = _children.begin(); cur != _children.end(); ++cur) { + if ((*cur)->name() == name) { + return *cur; + } + } + + return 0; +} + const XMLNodeList & -XMLNode::children(const string & n) const +XMLNode::children(const string& n) const { + /* returns all children matching name */ + static XMLNodeList retval; XMLNodeConstIterator cur; - if (n.length() == 0) { + if (n.empty()) { return _children; } diff --git a/libs/surfaces/control_protocol/SConscript b/libs/surfaces/control_protocol/SConscript index ce59b1c67c..88aeeda376 100644 --- a/libs/surfaces/control_protocol/SConscript +++ b/libs/surfaces/control_protocol/SConscript @@ -33,6 +33,7 @@ cp.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") cp.Merge ([ libraries['ardour'], + libraries['sndfile-ardour'], libraries['sigc2'], libraries['pbd'], libraries['midi++2'], diff --git a/libs/surfaces/control_protocol/basic_ui.cc b/libs/surfaces/control_protocol/basic_ui.cc index 07e000ab20..e6642d3394 100644 --- a/libs/surfaces/control_protocol/basic_ui.cc +++ b/libs/surfaces/control_protocol/basic_ui.cc @@ -55,7 +55,7 @@ BasicUI::register_thread (std::string name) void BasicUI::loop_toggle () { - if (Config->get_auto_loop()) { + if (session->get_play_loop()) { session->request_play_loop (false); } else { session->request_play_loop (true); @@ -107,7 +107,7 @@ BasicUI::transport_play (bool from_last_start) { bool rolling = session->transport_rolling (); - if (Config->get_auto_loop()) { + if (session->get_play_loop()) { session->request_play_loop (false); } diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc index 2e2d943244..ea85a32a77 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.cc +++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc @@ -718,7 +718,7 @@ TranzportControlProtocol::update_state () /* global */ - if (Config->get_auto_loop()) { + if (session->get_play_loop()) { pending_lights[LightLoop] = true; } else { pending_lights[LightLoop] = false; @@ -760,6 +760,14 @@ TranzportControlProtocol::update_state () } } + if (pending_lights[LightTrackrec] != lights[LightTrackrec]) { + if (pending_lights[LightTrackrec]) { + light_on (LightTrackrec); + } else { + light_off (LightTrackrec); + } + } + if (pending_lights[LightTrackmute] != lights[LightTrackmute]) { if (pending_lights[LightTrackmute]) { light_on (LightTrackmute); |