Files
QControlKit/QSharp/QCue.cs
T
2020-06-10 17:19:15 -05:00

706 lines
20 KiB
C#

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace QSharp
{
public class QCue
{
public QWorkspace workspace;
public Dictionary<string, object> cueData;
public List<QCue> childCues;
public Dictionary<string, QCue> childCuesUIDMap;
public int sortIndex;
private bool needsSortChildCues;//?
private bool needsNotifyCueUpdated;//?
public bool ignoreUpdates;
public QCue()
{
init();
}
public QCue(QWorkspace workspace)
{
init();
this.workspace = workspace;
}
public QCue(JToken dict, QWorkspace workspace)
{
init();
this.workspace = workspace;
//this breaks things???
//it definitely is in the original but somehow the recursion is not working as I expect..
//but cue population still works without this as child cues are populated in a later
/*JToken children = dict[QOSCKey.Cues];
if(children != null && children.Type == JTokenType.Array)
{
Console.WriteLine($"[cue] new cue being created with {children.Count()} childCues");
foreach (var aChildDict in children)
{
string uid = (string)aChildDict[QOSCKey.UID];
//Console.WriteLine($"[cue] childDict with uid: {uid} being processed");
if (uid == null)
continue;
string name = dict[QOSCKey.UID].ToString();
Console.WriteLine($"[cue] calling new cue from {name}");
//QCue child = new QCue(aChildDict, workspace);
*//*childCues.Add(child);
childCuesUIDMap.Add(uid, child);*//*
}
}*/
updatePropertiesWithDictionary(dict);
}
public void init()
{
cueData = new Dictionary<string, object> {
{ QOSCKey.Flagged, false },
{ QOSCKey.Armed, true },
{ QOSCKey.PreWait, 0.0 },
{ QOSCKey.PercentPreWaitElapsed, 0.0 },
{ QOSCKey.PercentActionElapsed, 0.0 },
{ QOSCKey.PostWait, 0.0 },
{ QOSCKey.PercentPostWaitElapsed, 0.0 },
{ QOSCKey.IsPanicking, false },
{ QOSCKey.IsTailingOut, false },
{ QOSCKey.IsRunning, false },
{ QOSCKey.IsLoaded, false },
{ QOSCKey.IsPaused, false },
{ QOSCKey.IsBroken, false },
{ QOSCKey.IsOverridden, false },
{ QOSCKey.ContinueMode, 0 }
};
childCues = new List<QCue>();
childCuesUIDMap = new Dictionary<string, QCue>();
}
public string description
{
get
{
return $"(Cue: {this}) name: {name} [id:{uid} number:{number} type: {type}";
}
}
//isEqual?
//hash?
//compare?
public bool isEqualToCue(QCue cue) { throw new NotImplementedException(); }
public void setWorkspace(QWorkspace workspace)
{
if(this.workspace != workspace)
{
this.workspace = workspace;
}
}
public void addChildCue(QCue cue)
{
string uid = propertyForKey(QOSCKey.UID).ToString();
addChildCue(cue, uid);
}
public void addChildCue(QCue cue, string uid)
{
if (uid.Length == 0)
return;
childCues.Add(cue);
childCuesUIDMap.Add(uid, cue);
//some sorting of the childCues needs to be done?
//reset sorting index
}
public void removeChildCue(QCue cue) { throw new NotImplementedException(); }
public void removeAllChildCues() { throw new NotImplementedException(); }
public void removeChildCuesWithIDs(List<string> uids) { throw new NotImplementedException(); }
//copied: yes
//implemented: no
#region Class Methods
public string iconForType(string type) { throw new NotImplementedException(); }
public bool cueTypeIsAudio(string type) { throw new NotImplementedException(); }
public bool cueTypeIsVideo(string type) { throw new NotImplementedException(); }
public bool cueTypeIsGroup(string type) { throw new NotImplementedException(); }
public bool cueTypeIsCueList(string type) { throw new NotImplementedException(); }
public List<string> fadeModeTitles() { throw new NotImplementedException(); }
#endregion
//KVC Compliance... do I need this?
//copied: yes
//implemented: not all
#region Convenience Accessors
public List<QCue> cues
{
get
{
List<QCue> cues = (List<QCue>)propertyForKey(QOSCKey.Cues);
if (cues == null)
return new List<QCue>();
return cues;
}
}
public string parentID
{
get
{
return propertyForKey(QOSCKey.Parent).ToString();
}
}
public string playbackPositionID() { throw new NotImplementedException(); }
public string name
{
get
{
return propertyForKey(QOSCKey.Name).ToString();
}
}
public string number
{
get
{
object num = propertyForKey(QOSCKey.Number);
if (num != null)
return num.ToString();
else
return "";
}
}
public string uid
{
get
{
return propertyForKey(QOSCKey.UID).ToString();
}
}
public string listName
{
get
{
return propertyForKey(QOSCKey.ListName).ToString();
}
}
public string type
{
get
{
return propertyForKey(QOSCKey.Type).ToString();
}
}
public string notes
{
get
{
return propertyForKey(QOSCKey.Notes).ToString();
}
}
public bool IsFlagged
{
get
{
return (bool)propertyForKey(QOSCKey.Flagged);
}
}
public bool IsOverridden
{
get
{
return (bool)propertyForKey(QOSCKey.IsOverridden);
}
}
public bool IsBroken
{
get
{
return (bool)propertyForKey(QOSCKey.IsBroken);
}
}
public bool IsRunning
{
get
{
return (bool)propertyForKey(QOSCKey.IsRunning);
}
}
public bool IsTailingOut
{
get
{
return (bool)propertyForKey(QOSCKey.IsTailingOut);
}
}
public bool IsPanicking
{
get
{
return (bool)propertyForKey(QOSCKey.IsPanicking);
}
}
public bool IsGroup
{
get
{
if (type.Equals(QCueType.Group))
return true;
if (type.Equals(QCueType.CueList))
return true;
if (type.Equals(QCueType.Cart))
return true;
return false;
}
}
public bool IsCueList
{
get
{
if ( type.Equals(QCueType.CueList))
return true;
return false;
}
}
public string displayName
{
get
{
string number = this.number;
if(number != null)
{
if (number.Length > 0)
return $"{number} \u00b7 {nonEmptyName}";
else
return nonEmptyName;
}
else
return nonEmptyName;
}
}
public string nonEmptyName
{
get
{
//TODO
string nonEmptyName;
if (name != null && !name.Equals(""))
nonEmptyName = name;
else if (listName != null && !listName.Equals(""))
nonEmptyName = listName;
else
nonEmptyName = $"Untitled {type} Cue";
return nonEmptyName;
}
}
public string workspaceName{ get { return workspace.name; } }
public double currentDuration
{
get
{
//try v4 current duration key first
if (propertyForKey(QOSCKey.CurrentDuration) != null)
return (double)propertyForKey(QOSCKey.CurrentDuration);
else
return (double)propertyForKey(QOSCKey.Duration);
}
}
public string audioFadeModeName
{
get
{
//TODO
if (type.Equals(QCueType.Fade))
return "";
else
return null;
}
}
public string geoFadeModeName
{
get
{
//TODO
if (type.Equals(QCueType.Fade))
return "";
else
return null;
}
}
public string surfaceName{ get { return propertyForKey("surfaceName").ToString(); } }
public string patchName
{
get
{
//TODO
return propertyForKey("patchDecription").ToString();
}
}
//TODO: THIS IS WHERE THINGS START TO GET TRICKY!
//TODO add object color method
public string color {
get {
object col = propertyForKey(QOSCKey.ColorName);
if (col != null)
return col.ToString();
else
return "none";
}
}
//TODO add quaternion property and setter
public Size surfaceSize() { throw new NotImplementedException(); }
public Size cueSize() { throw new NotImplementedException(); }
public List<string> availableSurfaceName() { throw new NotImplementedException(); }
public List<string> propertyKeys
{
get
{
//TODO add null check?
return cueData.Keys.ToList();
}
}
public bool hasChildren
{
get
{
return cues.Count > 0;
}
}
public QCue firstCue
{
get
{
return cues.First();
}
}
public QCue lastCue
{
get
{
return cues.Last();
}
}
public void setIgnoreUpdates(bool ignoreUpdates)
{
if(this.ignoreUpdates != ignoreUpdates)
{
this.ignoreUpdates = ignoreUpdates;
if (!this.ignoreUpdates)
{
//TODO: update local with QLab
}
}
}
#endregion
//Copied: Yes
//Implemented: Not all
#region Mutators
public void setCues(List<QCue> cues)
{
}
public void setName() { }
public void setNumber() { }
public void setUid() { }
public void setListName() { }
public void setType() { }
public void setFlagged() { }
#endregion
//Copied: Yes
//Implemented: Not all
#region Update Methods
public bool updatePropertiesWithDictionary(JToken dict)
{
bool cueUpdated = false;
//TODO
JObject dictObj = (JObject)dict;
foreach (var obj in dictObj)
{
//Console.WriteLine($"[cue] found property {obj.Key} ");
JToken value = obj.Value;
if (obj.Key.Equals(QOSCKey.Cues))
{
if (value.Type != JTokenType.Array)
continue;
updateChildCuesWithPropertiesArray(value, false);
}
else
{
bool didSetProptery = setProperty(value, obj.Key, false);
if (didSetProptery)
cueUpdated = true;
}
}
if (!cueUpdated)
return false;
Console.WriteLine($"[cue] {nonEmptyName} has been updated.");
//something about notifying cueUpdated ? OnCueUpdated event handler maybe?
return true;
}
//enqueue updated notification?
public bool updateChildCuesWithPropertiesArray(JToken value, bool removeUnused)
{
if (!workspace.connected)
return false;
List<string> previousUids = null;
if (removeUnused)
previousUids = allChildCueUids();
int index = 0;
foreach (var dict in value)
{
string uid = (string)dict[QOSCKey.UID];
if (uid == null)
continue;
if (removeUnused)
previousUids.Remove(uid);
QCue child = cueWithID(uid, false);
if (child != null)
{
bool didUpdateProperties = updatePropertiesWithDictionary(dict);
if (didUpdateProperties)
needsNotifyCueUpdated = true;
if (child.sortIndex != index)
{
child.sortIndex = index;
needsSortChildCues = true;
needsNotifyCueUpdated = true;
}
}
else
{
child = new QCue(dict, workspace);
child.sortIndex = index;
addChildCue(child, uid);
needsNotifyCueUpdated = true;
}
index++;
}
return needsNotifyCueUpdated;
//TODO: TEST?!
}
#endregion
//Methods Copied: Yes
//Methods Implemented: Not all
#region Children Cues
public List<string> allChildCueUids()
{
List<string> uids = new List<string>(childCuesUIDMap.Keys);
//TODO
return uids;
}
public QCue cueAtIndex(int index)
{
//TODO
return new QCue();
}
public QCue cueWithID(string uid)
{
return cueWithID(uid, true);
}
public QCue cueWithID(string uid, bool includeChildren)
{
//Console.WriteLine($"[cue] searching for cue with id: {uid} - includeChildren: {includeChildren}");
if(childCuesUIDMap.ContainsKey(uid))
return childCuesUIDMap[uid];
if (!includeChildren)
return null;
foreach(var cue in cues)
{
if (!cue.IsGroup)
continue;
QCue childCue = cue.cueWithID(uid, includeChildren);
if (childCue != null)
return childCue;
}
return null;
}
public QCue cueWithNumber(string number)
{
//TODO
return new QCue();
}
public object propertyForKey(string key)
{
//TODO: implerment special key checks color, surfaceName, patchDescription,
if (key.Equals(QOSCKey.Cues))
{
return childCues;
}
if (cueData.ContainsKey(key))
{
return cueData[key];
}
return null;
}
public bool setProperty(object value, string key)
{
return setProperty( value, key, workspace.defaultSendUpdatesOSC);
}
public bool setProperty(object value, string key, bool osc)
{
//TODO:
object existingValue = null;
if(cueData.ContainsKey(key))
existingValue = cueData[key];
if(existingValue != null)
{
if (existingValue == value || existingValue.Equals(value))
{
return false;
}
}
//Console.WriteLine($"[cue] attempting set property: {key} with type: {value.GetType()} and value: {value}");
if (workspace.connectedToQLab3 && key.Equals(QOSCKey.Type) && existingValue.Equals(QCueType.CueList) && value.Equals(QCueType.Group))
{
return false;
}
if (key.Equals(QOSCKey.Cues))
{
if (value.GetType() != typeof(List<QCue>))
return false;
Dictionary<string,QCue> newChildCuesUIDMap = new Dictionary<string, QCue>();
int index = 0;
string uid = null;
foreach(var aCue in (List<QCue>)value)
{
uid = aCue.uid;
if (uid == null)
continue;
aCue.sortIndex = index;
newChildCuesUIDMap.Add(uid, aCue);
index++;
}
childCues = (List<QCue>)value;
childCuesUIDMap = newChildCuesUIDMap;
}
else if(key.Equals(QOSCKey.PlaybackPositionId))
{
if (!IsCueList)
return false;
if (value != null && value.GetType() != typeof(string))
return false;
if (value.Equals("none"))
value = null;
if (value != null)
cueData[key] = value;
else
cueData.Remove(key);
}
else
{
if (value != null)
cueData[key] = value;
else
cueData.Remove(key);
}
//Console.WriteLine($"[cue] set property: {key} with type: {value.GetType()} and value: {value}");
//check for type key and update icon? do I want to do this here?
return true;
}
#endregion
//TODO
public void sendAllPropertiesToQLab() { }
//TODO: I don't think I'll do this one
public void pullDownPropertyForKey(string key) { }
public void setPlaybackPositionID(string cueID, bool osc) {
setProperty(cueID, QOSCKey.PlaybackPositionId, osc);
}
//Methods Copied: Yes
//Methods Implemented: Yes
#region Actions
public void start() { workspace.startCue(this); }
public void stop() { workspace.stopCue(this); }
public void pause() { workspace.pauseCue(this); }
public void reset() { workspace.resetCue(this); }
public void load() { workspace.loadCue(this); }
public void resume() { workspace.resumeCue(this); }
public void hardStop() { workspace.hardStopCue(this); }
public void hardPause() { workspace.hardPauseCue(this); }
public void togglePause() { workspace.togglePauseCue(this); }
public void preview() { workspace.previewCue(this); }
public void panic() { workspace.panicCue(this); }
#endregion
#region Printing
//this still needs fixing
public void Print()
{
Console.WriteLine($"{displayName} : {color}");
if (IsGroup)
{
if (cues.Count() > 0)
{
foreach (var cue in cues)
{
cue.Print();
}
}
}
}
#endregion
}
}