summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/interface/inputseq.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/interface/inputseq.cpp')
-rw-r--r--src/osd/interface/inputseq.cpp210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/osd/interface/inputseq.cpp b/src/osd/interface/inputseq.cpp
new file mode 100644
index 00000000000..da1f2b98b26
--- /dev/null
+++ b/src/osd/interface/inputseq.cpp
@@ -0,0 +1,210 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ inputseq.cpp
+
+ A combination of hosts inputs that can be assigned to a control.
+
+***************************************************************************/
+
+#include "inputseq.h"
+
+
+namespace osd {
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// additional expanded input codes for sequences
+constexpr input_code input_seq::end_code;
+constexpr input_code input_seq::default_code;
+constexpr input_code input_seq::not_code;
+constexpr input_code input_seq::or_code;
+
+// constant sequences
+const input_seq input_seq::empty_seq;
+
+
+
+//**************************************************************************
+// INPUT SEQ
+//**************************************************************************
+
+//-------------------------------------------------
+// operator+= - append a code to the end of an
+// input sequence
+//-------------------------------------------------
+
+input_seq &input_seq::operator+=(input_code code) noexcept
+{
+ // if not enough room, return false
+ const int curlength = length();
+ if (curlength < m_code.size())
+ {
+ m_code[curlength] = code;
+ if ((curlength + 1) < m_code.size())
+ m_code[curlength + 1] = end_code;
+ }
+ return *this;
+}
+
+
+//-------------------------------------------------
+// operator|= - append a code to a sequence; if
+// the sequence is non-empty, insert an OR
+// before the new code
+//-------------------------------------------------
+
+input_seq &input_seq::operator|=(input_code code) noexcept
+{
+ // overwrite end/default with the new code
+ if (m_code[0] == default_code)
+ {
+ m_code[0] = code;
+ m_code[1] = end_code;
+ }
+ else
+ {
+ // otherwise, append an OR token and then the new code
+ const int curlength = length();
+ if ((curlength + 1) < m_code.size())
+ {
+ m_code[curlength] = or_code;
+ m_code[curlength + 1] = code;
+ if ((curlength + 2) < m_code.size())
+ m_code[curlength + 2] = end_code;
+ }
+ }
+ return *this;
+}
+
+
+//-------------------------------------------------
+// length - return the length of the sequence
+//-------------------------------------------------
+
+int input_seq::length() const noexcept
+{
+ // find the end token; error if none found
+ for (int seqnum = 0; seqnum < m_code.size(); seqnum++)
+ if (m_code[seqnum] == end_code)
+ return seqnum;
+ return m_code.size();
+}
+
+
+//-------------------------------------------------
+// is_valid - return true if a given sequence is
+// valid
+//-------------------------------------------------
+
+bool input_seq::is_valid() const noexcept
+{
+ // "default" can only be of length 1
+ if (m_code[0] == default_code)
+ return m_code[1] == end_code;
+
+ // scan the sequence for valid codes
+ input_item_class lastclass = ITEM_CLASS_INVALID;
+ input_code lastcode = INPUT_CODE_INVALID;
+ decltype(m_code) positive_codes;
+ decltype(m_code) negative_codes;
+ auto positive_codes_end = positive_codes.begin();
+ auto negative_codes_end = negative_codes.begin();
+ for (input_code code : m_code)
+ {
+ // invalid codes are never permitted
+ if (code == INPUT_CODE_INVALID)
+ return false;
+
+ // if we hit an OR or the end, validate the previous chunk
+ if (code == or_code || code == end_code)
+ {
+ // must be at least one positive code
+ if (positive_codes.begin() == positive_codes_end)
+ return false;
+
+ // last code must not have been an internal code
+ if (lastcode.internal())
+ return false;
+
+ // if this is the end, we're ok
+ if (code == end_code)
+ return true;
+
+ // reset the state for the next chunk
+ positive_codes_end = positive_codes.begin();
+ negative_codes_end = negative_codes.begin();
+ lastclass = ITEM_CLASS_INVALID;
+ }
+ else if (code == not_code)
+ {
+ // if we hit a NOT, make sure we don't have a double
+ if (lastcode == not_code)
+ return false;
+ }
+ else
+ {
+ // track positive codes, and don't allow positive and negative for the same code
+ if (lastcode != not_code)
+ {
+ *positive_codes_end++ = code;
+ if (std::find(negative_codes.begin(), negative_codes_end, code) != negative_codes_end)
+ return false;
+ }
+ else
+ {
+ *negative_codes_end++ = code;
+ if (std::find(positive_codes.begin(), positive_codes_end, code) != positive_codes_end)
+ return false;
+ }
+
+ // non-switch items can't have a NOT
+ input_item_class itemclass = code.item_class();
+ if (itemclass != ITEM_CLASS_SWITCH && lastcode == not_code)
+ return false;
+
+ // absolute/relative items must all be the same class
+ if ((lastclass == ITEM_CLASS_ABSOLUTE && itemclass != ITEM_CLASS_ABSOLUTE) ||
+ (lastclass == ITEM_CLASS_RELATIVE && itemclass != ITEM_CLASS_RELATIVE))
+ return false;
+ }
+
+ // remember the last code
+ lastcode = code;
+ }
+
+ // if we got here, we were missing an END token; fail
+ return false;
+}
+
+
+//-------------------------------------------------
+// backspace - "backspace" over the last entry in
+// a sequence
+//-------------------------------------------------
+
+void input_seq::backspace() noexcept
+{
+ // if we have at least one entry, remove it
+ const int curlength = length();
+ if (curlength > 0)
+ m_code[curlength - 1] = end_code;
+}
+
+
+//-------------------------------------------------
+// replace - replace all instances of oldcode
+// with newcode in a sequence
+//-------------------------------------------------
+
+void input_seq::replace(input_code oldcode, input_code newcode) noexcept
+{
+ for (input_code &elem : m_code)
+ if (elem == oldcode)
+ elem = newcode;
+}
+
+} // namespace osd