444 lines
10 KiB
C++
444 lines
10 KiB
C++
/***********************************************************************
|
|
* fobject.cpp - Object container base class of all widget objects *
|
|
* *
|
|
* This file is part of the FINAL CUT widget toolkit *
|
|
* *
|
|
* Copyright 2015-2020 Markus Gans *
|
|
* *
|
|
* FINAL CUT is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation; either version 3 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* FINAL CUT is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with this program. If not, see *
|
|
* <http://www.gnu.org/licenses/>. *
|
|
***********************************************************************/
|
|
|
|
#include <memory>
|
|
|
|
#include "final/emptyfstring.h"
|
|
#include "final/fevent.h"
|
|
#include "final/fc.h"
|
|
#include "final/fobject.h"
|
|
|
|
namespace finalcut
|
|
{
|
|
|
|
// static class attributes
|
|
bool FObject::timer_modify_lock;
|
|
FObject::FTimerList* FObject::timer_list{nullptr};
|
|
const FString* fc::emptyFString::empty_string{nullptr};
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// class FObject
|
|
//----------------------------------------------------------------------
|
|
|
|
// constructors and destructor
|
|
//----------------------------------------------------------------------
|
|
FObject::FObject (FObject* parent)
|
|
: parent_obj{parent}
|
|
{
|
|
if ( parent ) // add object to parent
|
|
{
|
|
parent->addChild(this);
|
|
}
|
|
else
|
|
{
|
|
timer_modify_lock = false;
|
|
|
|
if ( ! timer_list )
|
|
{
|
|
try
|
|
{
|
|
timer_list = new FTimerList;
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
badAllocOutput ("FTimerList");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
FObject::~FObject() // destructor
|
|
{
|
|
delOwnTimers(); // Delete all timers of this object
|
|
|
|
if ( ! has_parent && timer_list )
|
|
{
|
|
delete timer_list;
|
|
timer_list = nullptr;
|
|
}
|
|
|
|
if ( ! has_parent && ! fc::emptyFString::isNull() )
|
|
fc::emptyFString::clear();
|
|
|
|
// Delete children objects
|
|
if ( hasChildren() )
|
|
{
|
|
auto delete_list = children_list;
|
|
|
|
for (auto&& obj : delete_list)
|
|
delete obj;
|
|
}
|
|
|
|
if ( parent_obj )
|
|
parent_obj->delChild(this);
|
|
|
|
parent_obj = nullptr;
|
|
}
|
|
|
|
// public methods of FObject
|
|
//----------------------------------------------------------------------
|
|
FObject* FObject::getChild (int index) const
|
|
{
|
|
// returns the child for the index number
|
|
|
|
if ( ! hasChildren() )
|
|
return nullptr;
|
|
|
|
if ( index <= 0 || index > int(numOfChildren()) )
|
|
return nullptr;
|
|
|
|
auto iter = begin();
|
|
std::advance (iter, index - 1);
|
|
return *iter;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::isChild (const FObject* obj) const
|
|
{
|
|
// Find out if obj is a child object of mine
|
|
|
|
while ( obj )
|
|
{
|
|
obj = obj->getParent();
|
|
|
|
if ( obj == this )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::removeParent()
|
|
{
|
|
if ( ! hasParent() )
|
|
return;
|
|
|
|
getParent()->delChild(this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::addChild (FObject* obj)
|
|
{
|
|
// Adds an object obj to the children list
|
|
|
|
if ( ! obj )
|
|
return;
|
|
|
|
if ( max_children != UNLIMITED && max_children <= numOfChildren() )
|
|
throw std::length_error ("max. child objects reached");
|
|
|
|
if ( obj->parent_obj )
|
|
obj->parent_obj->delChild(obj);
|
|
|
|
obj->parent_obj = this;
|
|
obj->has_parent = true;
|
|
children_list.push_back(obj);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::delChild (FObject* obj)
|
|
{
|
|
// Deletes the child object obj from children list
|
|
|
|
if ( ! obj )
|
|
return;
|
|
|
|
if ( hasChildren() )
|
|
{
|
|
obj->parent_obj = nullptr;
|
|
obj->has_parent = false;
|
|
children_list.remove(obj);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::setParent (FObject* parent)
|
|
{
|
|
// Sets a new parent object
|
|
|
|
if ( ! parent )
|
|
return;
|
|
|
|
removeParent();
|
|
parent_obj = parent;
|
|
has_parent = true;
|
|
parent->children_list.push_back(this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::event (FEvent* ev)
|
|
{
|
|
// Receives events on this object
|
|
|
|
if ( ev->getType() == fc::Timer_Event )
|
|
{
|
|
onTimer ( static_cast<FTimerEvent*>(ev) );
|
|
}
|
|
else if ( ev->getType() == fc::User_Event )
|
|
{
|
|
onUserEvent ( static_cast<FUserEvent*>(ev) );
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::getCurrentTime (timeval* time)
|
|
{
|
|
// Get the current time as timeval struct
|
|
|
|
gettimeofday(time, nullptr);
|
|
|
|
// NTP fix
|
|
while ( time->tv_usec >= 1000000 )
|
|
{
|
|
time->tv_usec -= 1000000;
|
|
time->tv_sec++;
|
|
}
|
|
|
|
while ( time->tv_usec < 0 )
|
|
{
|
|
if ( time->tv_sec > 0 )
|
|
{
|
|
time->tv_usec += 1000000;
|
|
time->tv_sec--;
|
|
}
|
|
else
|
|
{
|
|
time->tv_usec = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::isTimeout (const timeval* time, uInt64 timeout)
|
|
{
|
|
// Checks whether the specified time span (timeout in µs) has elapse
|
|
|
|
struct timeval now{};
|
|
struct timeval diff{};
|
|
|
|
FObject::getCurrentTime(&now);
|
|
diff.tv_sec = now.tv_sec - time->tv_sec;
|
|
diff.tv_usec = now.tv_usec - time->tv_usec;
|
|
|
|
if ( diff.tv_usec < 0 )
|
|
{
|
|
diff.tv_sec--;
|
|
diff.tv_usec += 1000000;
|
|
}
|
|
|
|
const auto diff_usec = uInt64((diff.tv_sec * 1000000) + diff.tv_usec);
|
|
return ( diff_usec > timeout );
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int FObject::addTimer (int interval)
|
|
{
|
|
// Create a timer and returns the timer identifier number
|
|
// (interval in ms)
|
|
|
|
timeval time_interval{};
|
|
timeval currentTime{};
|
|
int id{1};
|
|
timer_modify_lock = true;
|
|
|
|
// find an unused timer id
|
|
if ( ! timer_list->empty() )
|
|
{
|
|
auto iter = timer_list->begin();
|
|
const auto& last = timer_list->end();
|
|
|
|
while ( iter != last )
|
|
{
|
|
if ( iter->id == id )
|
|
{
|
|
iter = timer_list->begin();
|
|
id++;
|
|
continue;
|
|
}
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
if ( id <= 0 || id > int(timer_list->size() + 1) )
|
|
return 0;
|
|
|
|
time_interval.tv_sec = interval / 1000;
|
|
time_interval.tv_usec = (interval % 1000) * 1000;
|
|
getCurrentTime (¤tTime);
|
|
timeval timeout = currentTime + time_interval;
|
|
FTimerData t{ id, time_interval, timeout, this };
|
|
|
|
// insert in list sorted by timeout
|
|
auto iter = timer_list->begin();
|
|
const auto& last = timer_list->end();
|
|
|
|
while ( iter != last && iter->timeout < t.timeout )
|
|
++iter;
|
|
|
|
timer_list->insert (iter, t);
|
|
timer_modify_lock = false;
|
|
return id;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::delTimer (int id) const
|
|
{
|
|
// Deletes a timer by using the timer identifier number
|
|
|
|
if ( id <= 0 )
|
|
return false;
|
|
|
|
timer_modify_lock = true;
|
|
auto iter = timer_list->begin();
|
|
const auto& last = timer_list->end();
|
|
|
|
while ( iter != last && iter->id != id )
|
|
++iter;
|
|
|
|
if ( iter != last )
|
|
{
|
|
timer_list->erase(iter);
|
|
timer_modify_lock = false;
|
|
return true;
|
|
}
|
|
|
|
timer_modify_lock = false;
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::delOwnTimers() const
|
|
{
|
|
// Deletes all timers of this object
|
|
|
|
if ( ! timer_list )
|
|
return false;
|
|
|
|
if ( timer_list->empty() )
|
|
return false;
|
|
|
|
timer_modify_lock = true;
|
|
auto iter = timer_list->begin();
|
|
|
|
while ( iter != timer_list->end() )
|
|
{
|
|
if ( iter->object == this )
|
|
iter = timer_list->erase(iter);
|
|
else
|
|
++iter;
|
|
}
|
|
|
|
timer_modify_lock = false;
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool FObject::delAllTimers() const
|
|
{
|
|
// Deletes all timers of all objects
|
|
|
|
if ( ! timer_list )
|
|
return false;
|
|
|
|
if ( timer_list->empty() )
|
|
return false;
|
|
|
|
timer_modify_lock = true;
|
|
timer_list->clear();
|
|
timer_list->shrink_to_fit();
|
|
timer_modify_lock = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
// protected methods of FObject
|
|
//----------------------------------------------------------------------
|
|
void FObject::onTimer (FTimerEvent*)
|
|
{
|
|
// This event handler can be reimplemented in a subclass
|
|
// to receive timer events for this object
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::onUserEvent (FUserEvent*)
|
|
{
|
|
// This event handler can be reimplemented in a subclass
|
|
// to receive user events for this object
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
uInt FObject::processTimerEvent()
|
|
{
|
|
timeval currentTime{};
|
|
uInt activated{0};
|
|
|
|
getCurrentTime (¤tTime);
|
|
|
|
if ( isTimerInUpdating() )
|
|
return 0;
|
|
|
|
if ( ! timer_list )
|
|
return 0;
|
|
|
|
if ( timer_list->empty() )
|
|
return 0;
|
|
|
|
for (auto&& timer : *timer_list)
|
|
{
|
|
if ( ! timer.id
|
|
|| ! timer.object
|
|
|| currentTime < timer.timeout ) // no timer expired
|
|
break;
|
|
|
|
timer.timeout += timer.interval;
|
|
|
|
if ( timer.timeout < currentTime )
|
|
timer.timeout = currentTime + timer.interval;
|
|
|
|
if ( timer.interval.tv_usec > 0 || timer.interval.tv_sec > 0 )
|
|
activated++;
|
|
|
|
FTimerEvent t_ev(fc::Timer_Event, timer.id);
|
|
performTimerAction (timer.object, &t_ev);
|
|
}
|
|
|
|
return activated;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void FObject::performTimerAction (FObject*, FEvent*)
|
|
{
|
|
// This method must be reimplemented in a subclass
|
|
// to process the passed object and timer event
|
|
}
|
|
|
|
} // namespace finalcut
|