Given this yaml:
{CR: {cmd: fade, color: blue, panel: 0, value: 30, fout: 0.5, fint: 5},OL: {cmd: text, value: Blu at 30% on all, color: white, time: 5, position: [540,100], size: 50}}
With this code:
bool SEMTools::decodeYaml(QString yaml)
{
try
{
YAML::Node root = YAML::Load(yaml.toStdString().c_str());
YAML::Node::iterator i;
for (i = root.begin(); i != root.end(); i++)
{
qDebug() << (*i).first.as<QString>();
}
return true;
}
catch (YAML::TypedBadConversion<QString> const &e)
{
qDebug() << e.what();
}
return false;
}
I'm able to retrieve the lead keys: CR and OL.
For each one I also need to retrieve the whole object:
CR: {cmd: fade, color: blue, panel: 0, value: 30, fout: 0.5, fint: 5}
and
OL: {cmd: text, value: Blu at 30% on all, color: white, time: 5, position: [540,100], size: 50}
I tried with:
qDebug() << (*i).as<QString>();
but my application crashes with this error:
terminate called after throwing an instance of 'YAML::InvalidNode'
what(): invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa
What is the right syntax to get the strings above?
(*i).first is the key, (*i).second is its value.
Therefore, (*i) is the whole object as you call it (key + value). It's simply not a string, which is why you can't retrieve it via .as<QString>(). Each the key and the value are a YAML::Node just like root, and you can do .as<QString>() only on the key because it's a string. On the value, you can do (*i).second["cmd"].as<QString>() etc.
If you want the value to be a string instead of a nested YAML structure, you shouldn't input it as nested YAML structure.
Related
I would like to be able to find keys in a Rust BTreeSet that are strictly lower and greater than a specified key.
For example, given the set { "1", "3" }, and the search key is "2" then the answer should be ("1", "3"). In the cases either where either lower or greater value does not exist None should be returned.
I can achieve the result that I am looking for by calling the range() method on the BTreeSet twice.
Is there is a way to do this using a single search, like there is in C++? C++'s std::set has a bi-directional iterator:
// $CXX -std=c++17 less-than.c++ -o less-than && ./less-than
#include <cassert>
#include <optional>
#include <set>
#include <string>
#include <iostream>
using std::optional;
using std::pair;
using std::set;
using std::string;
pair<optional<string>, optional<string>> bounding_box(
const set<string>& space,
const string& point)
{
if (space.empty()) { return {}; }
optional<string> gt_bound;
optional<string> lt_bound;
const auto ge_bound_it = space.lower_bound(point);
if (ge_bound_it != space.end()) {
if (*ge_bound_it == point) {
// lower_bound returned an equal point, use the next one
// if it exists
const auto gt_bound_it = std::next(ge_bound_it, 1);
if (gt_bound_it != space.end()) {
gt_bound = *gt_bound_it;
}
} else {
gt_bound = *ge_bound_it;
}
}
if (ge_bound_it != space.begin()) {
lt_bound = *std::next(ge_bound_it, -1);
}
return {lt_bound, gt_bound};
}
int main() {
{
const auto box = bounding_box({"1", "3"}, "2");
assert(box.first);
assert(*box.first == "1");
assert(box.second);
assert(*box.second == "3");
}
{
const auto box = bounding_box({"1", "3"}, "4");
assert(box.first);
assert(*box.first == "3");
assert(!box.second);
}
{
const auto box = bounding_box({"1", "3"}, "0");
assert(!box.first);
assert(box.second);
assert(*box.second == "1");
}
{
const auto box = bounding_box({"3", "3"}, "3");
assert(!box.first);
assert(!box.second);
}
{
const auto box = bounding_box({"3", "4"}, "3");
assert(!box.first);
assert(box.second);
assert(*box.second == "4");
}
{
const auto box = bounding_box({}, "3");
assert(!box.first);
assert(!box.second);
}
}
The search method is a bit of a hot spot and I wonder if there is an idiomatic way to do this in Rust.
No, there is no way to do this in a single search; you need to call range twice.
There have been discussions about enhancing BTreeMap / BTreeSet to have a "cursor" API. Recently, a pull request was opened to do so, but it was closed because it was deemed that there should be more discussion about how such an API should look and work.
Perhaps you will be the one to spearhead the discussion about such an API?
See also:
How to get the lower bound and upper bound of an element in Rust BTreeSet?
There is not a cursor API in Rust, as said in Shepmaster's answer. You can sometimes simulate it when your iterator implements DoubleEndedIterator with next() and next_back().
However, if I understand well what you are trying to do, you do not need this stuff because a set is ordered. You can write your code by going through each pair and stop when the second item is greater than your "point":
use std::collections::BTreeSet;
fn find_bounds<'a>(set: &'a BTreeSet<&str>, point: &str) -> (Option<&'a str>, Option<&'a str>) {
let mut it = set.iter();
let mut lower = match it.next() {
None => return (None, None),
Some(s) if *s > point => return (None, Some(s)),
Some(s) => s,
};
while let Some(upper) = it.next() {
if *upper > point {
return (Some(lower), Some(*upper));
}
lower = upper;
}
(Some(lower), None)
}
#[test]
fn tests() {
let mut s = BTreeSet::new();
s.insert("a");
s.insert("c");
s.insert("t");
s.insert("g");
assert_eq!(find_bounds(&s, "f"), (Some("c"), Some("g")));
assert_eq!(find_bounds(&s, "z"), (Some("t"), None));
assert_eq!(find_bounds(&s, " "), (None, Some("a")));
}
The code is not well written, but it works.
I've developed a video player based on Qt and QtGstreamer. It is used to play live streams (RTSP). I have to add the possibility for the user to take snapshots while he is playing a live stream without perturbing the video playback.
Here the graph of the pipeline I've made:
-->queue-->autovideosink
uridecodebin-->videoflip-->tee--|
| -->queue->videoconvert-->pngenc-->filesink
|
|->audioconvert-->autoaudiosink
I use the pad-added signal from uridecodebin to add and link dynamically my elements to the pipeline, function of the received caps.
void Player::onPadAdded(const QGst::PadPtr &pad)
{
QGst::CapsPtr caps = pad->currentCaps();
if (caps->toString().startsWith("video/x-raw")) {
qDebug("Received 'video/x-raw' caps");
handleNewVideoPad(pad);
}
else if (caps->toString().startsWith("audio/x-raw")) {
qDebug("Received 'audio/x-raw' caps");
if (!m_audioEnabled) {
qDebug("Audio is disabled in the player. Ignoring...");
return;
}
handleNewAudioPad(pad);
}
else {
qWarning("Unsuported caps, arborting ...!");
return;
}
}
[...]
void Player::handleNewVideoPad(QGst::PadPtr pad)
{
m_player->videoTeeVideoSrcPad = m_player->videoTee->getRequestPad("src_%u");
// Add video elements
m_player->pipeline->add(m_player->videoFlip);
m_player->pipeline->add(m_player->videoTee);
m_player->pipeline->add(m_player->videoQueue);
m_player->pipeline->add(m_player->videoSink);
// Add snap elements
m_player->pipeline->add(m_player->snapQueue);
m_player->pipeline->add(m_player->snapConverter);
m_player->pipeline->add(m_player->snapEncoder);
m_player->pipeline->add(m_player->snapSink);
// Link video elements
m_player->videoFlip->link(m_player->videoTee);
m_player->videoQueue->link(m_player->videoSink);
// Link snap elements
m_player->snapQueue->link(m_player->snapConverter);
m_player->snapConverter->link(m_player->snapEncoder);
m_player->snapEncoder->link(m_player->snapSink);
// Lock snap elements
m_player->snapQueue->setStateLocked(true);
m_player->snapConverter->setStateLocked(true);
m_player->snapEncoder->setStateLocked(true);
m_player->snapSink->setStateLocked(true);
m_player->videoFlip->setState(QGst::StatePlaying);
m_player->videoTee->setState(QGst::StatePlaying);
m_player->videoQueue->setState(QGst::StatePlaying);
m_player->videoSink->setState(QGst::StatePlaying);
// Link pads
m_player->videoTeeVideoSrcPad->link(m_player->videoQueue->getStaticPad("sink"));
pad->link(m_player->videoSinkPad);
m_player->videoLinked = true;
}
The method to take a snapshot:
void Player::takeSnapshot()
{
QDateTime dateTime = QDateTime::currentDateTime();
QString snapLocation = QString("/%1/snap_%2.png").arg(m_snapDir).arg(dateTime.toString(Qt::ISODate));
m_player->inSnapshotCaputre = true;
if (m_player->videoTeeSnapSrcPad) {
m_player->videoTee->releaseRequestPad(m_player->videoTeeSnapSrcPad);
m_player->videoTeeSnapSrcPad.clear();
}
m_player->videoTeeSnapSrcPad = m_player->videoTee->getRequestPad("src_%u");
// Stop the snapshot branch
m_player->snapQueue->setState(QGst::StateNull);
m_player->snapConverter->setState(QGst::StateNull);
m_player->snapEncoder->setState(QGst::StateNull);
m_player->snapSink->setState(QGst::StateNull);
// Link Tee src pad to snap queue sink pad
m_player->videoTeeSnapSrcPad->link(m_player->snapQueue->getStaticPad("sink"));
// Set the snapshot location property
m_player->snapSink->setProperty("location", snapLocation);
// Unlock snapshot branch
m_player->snapQueue->setStateLocked(false);
m_player->snapConverter->setStateLocked(false);
m_player->snapEncoder->setStateLocked(false);
m_player->snapSink->setStateLocked(false);
m_player->videoTeeSnapSrcPad->setActive(true);
// Synch snapshot branch state with parent
m_player->snapQueue->syncStateWithParent();
m_player->snapConverter->syncStateWithParent();
m_player->snapEncoder->syncStateWithParent();
m_player->snapSink->syncStateWithParent();
}
The bus message callback:
void Player::onBusMessage(const QGst::MessagePtr & message)
{
QGst::ElementPtr source = message->source().staticCast<QGst::Element>();
switch (message->type()) {
case QGst::MessageEos: { //End of stream. We reached the end of the file.
qDebug("Message End Off Stream");
if (m_player->inSnapshotCaputre) {
blockSignals(true);
pause();
play();
blockSignals(false);
m_player->inSnapshotCaputre = false;
}
else {
m_eos = true;
stop();
}
break;
}
[...]
}
The problem is:
When I set the snapshot property to true of the pngenc element, I receive the EOS event which stop my pipeline, so I need to restart it, which freeze the video playback for about half a second, which in not acceptable in my case.
When I set the snapshot property to false of the pngenc element, I have no pipeline perturbations, but my png file keeps growing until I call again the Player::takeSnapshot() method.
Where am I wrong ? Is there a better way to do it ?
I've tried unsuccessfully creating a QGst::Bin element for my snapshot branch. What about pad probe ?
Thanks by advance
You can take the last-sample property on any sink, e.g. your video sink. This contains a GstSample, which has a buffer with the very latest video frame in it. You can take that as a snapshot, and e.g. with gst_video_convert_sample() or the async variant of it, convert it to a PNG/JPG/whatever.
See https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseSink.html#GstBaseSink--last-sample and https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideo.html#gst-video-convert-sample
Alternatively, you would have to shut down the filesink snapshot pipeline after the first frame. For example by having a pad probe to know when the first frame happened, and then injecting an EOS event to prevent further PNG frames to be appended to the same file.
Thanks to #sebastian-droge answer, I found the solution, using gst_video_convert_sample and the last-sample property of my video sink.
The solution I've implemented is:
void Player::takeSnapshot()
{
QDateTime currentDate = QDateTime::currentDateTime();
QString location = QString("%1/snap_%2.png").arg(QDir::homePath()).arg(currentDate.toString(Qt::ISODate));
QImage snapShot;
QImage::Format snapFormat;
QGlib::Value val = m_videoSink->property("last-sample");
GstSample *videoSample = (GstSample *)g_value_get_boxed(val);
QGst::SamplePtr sample = QGst::SamplePtr::wrap(videoSample);
QGst::SamplePtr convertedSample;
QGst::BufferPtr buffer;
QGst::CapsPtr caps = sample->caps();
QGst::MapInfo mapInfo;
GError *err = NULL;
GstCaps * capsTo = NULL;
const QGst::StructurePtr structure = caps->internalStructure(0);
int width, height;
width = structure.data()->value("width").get<int>();
height = structure.data()->value("height").get<int>();
qDebug() << "Sample caps:" << structure.data()->toString();
/*
* { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx },
* { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
* { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB },
* { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 }
*/
snapFormat = QImage::Format_RGB888;
capsTo = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGB",
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
NULL);
convertedSample = QGst::SamplePtr::wrap(gst_video_convert_sample(videoSample, capsTo, GST_SECOND, &err));
if (convertedSample.isNull()) {
qWarning() << "gst_video_convert_sample Failed:" << err->message;
}
else {
qDebug() << "Converted sample caps:" << convertedSample->caps()->toString();
buffer = convertedSample->buffer();
buffer->map(mapInfo, QGst::MapRead);
snapShot = QImage((const uchar *)mapInfo.data(),
width,
height,
snapFormat);
qDebug() << "Saving snap to" << location;
snapShot.save(location);
buffer->unmap(mapInfo);
}
val.clear();
sample.clear();
convertedSample.clear();
buffer.clear();
caps.clear();
g_clear_error(&err);
if (capsTo)
gst_caps_unref(capsTo);
}
I've create a simple test application, which implement this solution. The code is available on my Github
As part of a project my group has to program an Arduino to get a signal from an RFID, and pass data into Processing depending on said signal. For example, when a tag is sensed by the Arduino, String data would be passed into Processing and handled there.
We have the Arduino part working but Processing is throwing up errors. In our project we must wait for user input at certain points.
public int readRFID()
{
int tagNumber = 0;
//Has to be contained in try/catch because of errors with null
try
{
if ( myPort.available() > 0) { // If data is available,
val = myPort.readStringUntil('\n');
} // read it and store it in val
//Convert val into integer and set to tagNumber
tagNumber = Integer.parseInt(val);
}
//An error occurs if val is null, which is the case every time unless it actually gets data
catch(Exception e)
{
tagNumber = 0;
}
return tagNumber;
}
At the end, tagNumber should be returned as either 0 or a positive value (when a tag is present and there is a number to be read in). In the method that calls this method we have this:
//RFID SCANNING GOES HERE
int tag = 0;
while (tag == 0)
{
//When RFID reads as null, this method should return 0 until a tag
//is read in
tag = readRFID();
}
//userChoice is set to the number read in -1 because tags are numbered 1, 2, 3, 4
//but userChoice will refer to an index in an array that starts at 0
userChoice = tag - 1;
We are not sure why this code isn't working - all we're sure of is that the method has to be continuously called until a tag is present. Any suggestions would be greatly appreciated.
I'm a student programmer and I am using Qt to build some GUI applications for work and I have been running into moc issues over and over again. I was hoping for a solution to the current problem that I am having; however, if anyone more veteraned in Qt could shed some light on how to properly handle these files while making changes to your cpp file(s) I'd appreciate any help. In my most recent change (sorry I can't post what it did look like, because it's obviously been restructured) I was validating data by nesting a function inside of my checkData function. Because I would like a specific error to appear for each field that might be invalid I began to create a function for each QLineEdit. I realized that this would not work (or at least make more work) then instead of just providing sequenced checks of information. Below is the new code without the original nested function:
void InjectionDialog::checkData() {
bool validateFluidVelocity;
QString tempStrFluidVelocity;
tempStrFluidVelocity = ui->lineEditFluidVelocity->text();
double convertedFluidVelocity =
tempStrFluidVelocity.toDouble(&validateFluidVelocity);
if (validateFluidVelocity == false) {
QErrorMessage validateErrorFluidVelocityError;
validateErrorFluidVelocityError.
showMessage("Fluid velocity input is invalid");
validateErrorFluidVelocityError.exec();
}
else {
transData.lineEditFluidVelocity = convertedFluidVelocity;
}
bool validateFluidMassFlow;
QString tempStrFluidMassFlow;
tempStrFluidMassFlow = ui->lineEditFluidMassFlow->text();
double convertedFluidMassFlow =
tempStrFluidMassFlow.toDouble(&validateFluidMassFlow);
if (validateFluidMassFlow == false) {
QErrorMessage validateErrorFluidMassFlowError;
validateErrorFluidMassFlowError.
showMessage("Fluid mass flow input is invalid");
validateErrorFluidMassFlowError.exec();
}
else {
transData.lineEditFluidMassFlow = convertedFluidMassFlow;
}
bool validateParticleVelocity;
QString tempStrParticleVelocity;
tempStrParticleVelocity = ui->lineEditParticleVelocity->text();
double convertedParticleVelocity =
tempStrParticleVelocity.toDouble(&validateParticleVelocity);
if (validateParticleVelocity == false) {
QErrorMessage validateErrorParticleVelocity;
validateErrorParticleVelocity.
showMessage("Particle velocity input is invalid");
validateErrorParticleVelocity.exec();
}
else {
transData.lineEditParitcle_sic_Velocity = convertedParticleVelocity;
}
bool validateParticleMassFlow;
QString tempStrParticleMassFlow;
tempStrParticleMassFlow = ui->lineEditParticleMassFlow->text();
double convertedParticleMassFlow =
tempStrParticleMassFlow.toDouble(&validateParticleMassFlow);
if (validateParticleMassFlow == false) {
QErrorMessage validateErrorParticleMassFlow;
validateErrorParticleMassFlow.
showMessage("Particle mass flow input is invalid");
validateErrorParticleMassFlow.exec();
}
else {
transData.lineEditParticleMassFlow = convertedParticleMassFlow;
}
}
Initially I had InjectionDialog::checkFluidVelociy for the first check but decided against it pretty quickly. Now with the code restructured I receive the error:
In function 'InjectionDialog::checkFluidVelocity(QMetaObject::Call, int, void**)':
this error is referenced to moc_injectionDialog.o
unidentified reference to 'InjectionDialog::checkFluidVelocity()'
this error is referenced to moc_injectiondialog.cpp
In moc_injectiondialog I have the following I have the following listed:
/****************************************************************************
** Meta object code from reading C++ file 'injectiondialog.h'
**
** Created: Sat Jan 7 21:58:22 2012
** by: The Qt Meta Object Compiler version 62 (Qt 4.7.4)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "../InjectionGUI/injectiondialog.h"
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'injectiondialog.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 62
#error "This file was generated using the moc from 4.7.4. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
static const uint qt_meta_data_InjectionDialog[] = {
// content:
5, // revision
0, // classname
0, 0, // classinfo
2, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
// slots: signature, parameters, type, tag, flags
17, 16, 16, 16, 0x08,
29, 16, 16, 16, 0x08,
0 // eod
};
static const char qt_meta_stringdata_InjectionDialog[] = {
"InjectionDialog\0\0checkData()\0"
"checkFluidVelocity()\0"
};
const QMetaObject InjectionDialog::staticMetaObject = {
{ &QDialog::staticMetaObject, qt_meta_stringdata_InjectionDialog,
qt_meta_data_InjectionDialog, 0 }
};
#ifdef Q_NO_DATA_RELOCATION
const QMetaObject &InjectionDialog::getStaticMetaObject() { return staticMetaObject; }
#endif //Q_NO_DATA_RELOCATION
const QMetaObject *InjectionDialog::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
}
void *InjectionDialog::qt_metacast(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_InjectionDialog))
return static_cast<void*>(const_cast< InjectionDialog*>(this));
return QDialog::qt_metacast(_clname);
}
int InjectionDialog::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QDialog::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
switch (_id) {
case 0: checkData(); break;
case 1: checkFluidVelocity(); break;
default: ;
}
_id -= 2;
}
return _id;
}
QT_END_MOC_NAMESPACE
I have looked over some of the other moc_file posts and most of them say to have Qt clean and rebuild the project. I have ran a project clean up and rebuild all to try to overhaul this moc file but have not had any success in getting rid of the error. It seems like a bug but I couldn't find anything online about it so maybe it's something I don't know about. Thanks in advance for any help you can offer.
Whenever I have MOC problems, I Build->Clean All and then Build->Run qmake (Qt Creator IDE). If that doesn't solve my problem, I go into my project folder and delete moc_* files and any other junk that Clean doesn't remove - basically leaving nothing but headers, source and resources.
Go to you moc file which is throwing the error. At the top, there will be an include statement, which includes the header file for that window, NOT ui_.h, just .h
check in that file if there is a reference to the widget which is causing the error.
I have a tree control with checkboxes next to each node that allows for checked, unchecked and middle checked states on the nodes. When clicking a node, the parent and children are updated. The code I found that does the trick uses bit shifting and I'm trying to understand what exactly is happening.
Can someone explain the following code? Or even better, rewrite this code so it is easier to understand?
// click event handler
private function eventMessageTree_itemCheckHandler(event:TreeEvent):void {
var node:ITreeNode = ITreeNode(event.item);
var checkState:uint = TreecheckboxItemRenderer(event.itemRenderer).checkBox.checkState;
updateParents(node, checkState);
updateChilds(node, checkState);
}
private function updateChilds(item:ITreeNode, value:uint):void {
var middle:Boolean = (value & 2 << 1) == (2 << 1);
var selected:Boolean = (value & 1 << 1) == (1 << 1);
if (item.children.length > 0 && !middle) {
for each (var childNode:ITreeNode in item.children) {
childNode.checked = value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0";
updateChilds(childNode, value);
}
}
}
private function updateParents(item:ITreeNode, value:uint): void {
var checkValue:String = (value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0");
var parentNode:ITreeNode = item.parent;
if (parentNode) {
for each (var childNode:ITreeNode in parentNode.children) {
if (childNode.checked != checkValue) {
checkValue = "2";
}
}
parentNode.checked = checkValue;
updateParents(parentNode, value);
}
}
It looks like the checkState value in the control can be either 1, 2, or 4 (or possibly 0, 2, and 4?):
public static const CONTROL_UNCHECKED:uint = 1; // not checked, and some descendants are
public static const CONTROL_CHECKED:uint = 2; // checked, and all descendants are
public static const CONTROL_MIDDLE:uint = 4; // not checked, but some descendants are
while the checked value in the nodes can be either 0, 1, or 2:
public static const UNCHECKED:uint = 0; // not checked, and some descendants are
public static const CHECKED:uint = 1; // checked, and all descendants are
public static const MIDDLE:uint = 2; // not checked, but some descendants are
That's really confusing. Ideally these would be the same set of constants.
To update:
private function controlStateToNodeState(value:uint):uint {
return value / 2;
}
...
updateParents(node, controlStateToNodeState(checkState));
updateChilds(node, controlStateToNodeState(checkState));
...
/** Updates the descendants of the node based on state:
* If value is CHECKED, all children are CHECKED
* If value is UNCHECKED, all children are UNCHECKED
* If value is MIDDLE, children are left alone
*/
private function updateChilds(item:ITreeNode, value:uint):void {
if (value == MIDDLE) {
return; // if value is MIDDLE, children are left alone
}
// not middle, so update all children to my state
for each (var childNode:ITreeNode in item.children) {
childNode.checked = value;
updateChilds(childNode, value);
}
}
}
/**
* Updates the ancestor nodes based on state:
* If value is CHECKED, ancestors are made MIDDLE if not already checked
* If value is MIDDLE, ancestors are made middle (they should not already be CHECKED)
*/
private function updateParents(item:ITreeNode, value:uint): void {
...
}
Basically, an expression like this:
var middle:Boolean = (value & 2 << 1) == (2 << 1);
Is counter-intuitive. You usually test bits by shifting the constant 1 to the left, since that lets the number of bits shifted be the same as the index of the bit, counting the LSB (rightmost) bit as bit number 0.
Also, there's no point in testing the result with a == comparison, since it's always going to be either 0 or non-zero, so you can at least test for something simpler if your language requires that.
In C and C++, which by default interpret a non-zero integer as "true", the comparison is totally unnecessary and just serves to introduce clutter, repetition, and increase the risk of bugs.
I'd write this like so:
var middle:Boolean = (value & (1 << 2)) != 0;
The extra parenthesis should help make it clearer how things are grouped. Note how "2 << 1" was rewritten as "1 << 2". This is not just a "switch", you need to compute the proper shift to get the same bit value, 4 in this case.
Of course you could put the bit-testing into subroutines and call them, to make the code more readable.