#include <simgear/structure/SGBinding.hxx>
#include <simgear/props/props_io.hxx>
+#include <simgear/debug/BufferedLogCallback.hxx>
+#include <simgear/scene/tsync/terrasync.hxx>
#include <Scripting/NasalSys.hxx>
#include <Main/fg_os.hxx>
#include "FGFontCache.hxx"
#include "FGColor.hxx"
+using std::string;
+
enum format_type { f_INVALID, f_INT, f_LONG, f_FLOAT, f_DOUBLE, f_STRING };
static const int FORMAT_BUFSIZE = 255;
static const int RESIZE_MARGIN = 7;
return type;
}
+////////////////////////////////////////////////////////////////////////
+
+//
+// Custom subclass of puPopup to implement "draggable" windows in the
+// interface. Note that this is a subclass of puPopup, not
+// puDialogBox. Sadly, PUI (mis)uses subclassing to implement a
+// boolean property: modality. That means that we can't implement
+// dragging of both puPopup windows and puDialogBoxes with the same
+// code. Rather than duplicate code, I've chosen to implement only
+// "non-modal dragability" here. Modal dialog boxes (like the exit
+// confirmation) are not draggable.
+//
+class fgPopup : public puPopup {
+public:
+ fgPopup(int x, int y, bool r = true, bool d = true) :
+ puPopup(x, y), _draggable(d), _resizable(r), _dragging(false)
+ {}
+ int checkHit(int b, int up, int x, int y);
+ int checkKey(int key, int updown);
+ int getHitObjects(puObject *, int x, int y);
+ bool checkHitCanvas(puObject *, int x, int y);
+ puObject *getKeyObject(puObject *, int key);
+ puObject *getActiveInputField(puObject *);
+ void applySize(puObject *);
+private:
+ enum { LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8 };
+ bool _draggable;
+ bool _resizable;
+ bool _dragging;
+ int _resizing;
+ int _start_cursor;
+ int _cursor;
+ int _dlgX, _dlgY, _dlgW, _dlgH;
+ int _startX, _startY;
+};
+
+
+class fgValueList {
+public:
+ fgValueList(SGPropertyNode *p);
+ virtual ~fgValueList();
+ virtual void update();
+
+protected:
+ char **_list;
+
+private:
+ void make_list();
+ void destroy_list();
+ SGPropertyNode_ptr _props;
+};
+
+
+class fgList : public fgValueList, public puaList, public GUI_ID {
+public:
+ fgList(int x1, int y1, int x2, int y2, SGPropertyNode *p, int sw) :
+ fgValueList(p), puaList(x1, y1, x2, y2, _list, sw), GUI_ID(FGCLASS_LIST) {}
+ void update();
+};
+
+class fgComboBox : public fgValueList, public puaComboBox {
+public:
+ fgComboBox(int x1, int y1, int x2, int y2, SGPropertyNode *p, bool editable) :
+ fgValueList(p),
+ puaComboBox(x1, y1, x2, y2, _list, editable),
+ _inHit(false)
+ {}
+
+ void update();
+
+ virtual void setSize(int w, int h);
+
+ virtual int checkHit(int b, int up, int x, int y);
+
+ virtual void recalc_bbox();
+private:
+ bool _inHit;
+};
+
+class LogList : public puaList, public FGPUIDialog::ActiveWidget, public GUI_ID {
+public:
+ LogList(int x1, int y1, int x2, int y2, int sw) :
+ puaList(x1, y1, x2, y2, sw),
+ GUI_ID(FGCLASS_LOGLIST)
+ {
+ m_buffer = NULL;
+ m_stamp = 0;
+ }
+
+ void setBuffer(simgear::BufferedLogCallback* buf);
+
+ virtual void update();
+private:
+ std::vector<unsigned char*> m_items;
+ simgear::BufferedLogCallback* m_buffer;
+ unsigned int m_stamp;
+};
+
+class fgSelectBox : public fgValueList, public puaSelectBox {
+public:
+ fgSelectBox(int x1, int y1, int x2, int y2, SGPropertyNode *p) :
+ fgValueList(p), puaSelectBox(x1, y1, x2, y2, _list) {}
+};
////////////////////////////////////////////////////////////////////////
// Implementation of GUIInfo.
FGPUIDialog *dialog;
SGPropertyNode_ptr node;
- vector <SGBinding *> bindings;
+ std::vector <SGBinding *> bindings;
int key;
string label, legend, text, format;
format_type fmt_type;
}
-\f
/**
* Key handler.
*/
int hit = getHitObjects(this, x, y);
if (hit & PUCLASS_LIST) // ctrl-click in property browser (toggle bool)
return result;
- if (!global_resize && hit & (PUCLASS_BUTTON|PUCLASS_ONESHOT|PUCLASS_INPUT
- |PUCLASS_LARGEINPUT|PUCLASS_SCROLLBAR))
+ if( !global_resize
+ && ( (hit & (PUCLASS_BUTTON|PUCLASS_ONESHOT|PUCLASS_INPUT
+ |PUCLASS_LARGEINPUT|PUCLASS_SCROLLBAR))
+ // The canvas should handle drag events on its own so exit
+ // here if mouse is over a CanvasWidget
+ || (!global_drag && checkHitCanvas(this, x, y))
+ ) )
+ {
return result;
+ }
getPosition(&_dlgX, &_dlgY);
getSize(&_dlgW, &_dlgH);
return type;
}
+bool fgPopup::checkHitCanvas(puObject* object, int x, int y)
+{
+ if( !object->isVisible() )
+ return 0;
+
+ if( object->getType() & PUCLASS_GROUP )
+ {
+ for( puObject* obj = ((puGroup*)object)->getFirstChild();
+ obj;
+ obj = obj->getNextObject() )
+ {
+ if( checkHitCanvas(obj, x, y) )
+ return true;
+ }
+ }
+
+ int cx, cy, cw, ch;
+ object->getAbsolutePosition(&cx, &cy);
+ object->getSize(&cw, &ch);
+ if( x >= cx && x < cx + cw
+ && y >= cy && y < cy + ch
+ && dynamic_cast<CanvasWidget*>(object) )
+ return true;
+ return false;
+}
+
void fgPopup::applySize(puObject *object)
{
// compound plib widgets use setUserData() for internal purposes, so refuse
aDlg->setNeedsLayout();
}
-\f////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
// Callbacks.
////////////////////////////////////////////////////////////////////////
}
-\f
////////////////////////////////////////////////////////////////////////
// Static helper functions.
////////////////////////////////////////////////////////////////////////
node->setIntValue(object->getIntegerValue());
break;
case props::FLOAT:
- case props::DOUBLE:
node->setFloatValue(object->getFloatValue());
break;
+ case props::DOUBLE:
+ {
+ // puObject only provides float, not double, which causes precision/rounding issues
+ // with some numerical values (try "114.2").
+ // Work around: obtain string value, and manually convert with proper double precision.
+ const char *s = object->getStringValue();
+ node->setDoubleValue(atof(s));
+ break;
+ }
default:
const char *s = object->getStringValue();
if (s)
}
-\f
////////////////////////////////////////////////////////////////////////
// Implementation of FGDialog.
////////////////////////////////////////////////////////////////////////
_conditionalObjects[j]->update(this);
}
+ for (unsigned int i = 0; i < _activeWidgets.size(); i++) {
+ _activeWidgets[i]->update();
+ }
+
if (_needsRelayout) {
relayout();
}
ScrolledWaypointList* obj = new ScrolledWaypointList(x, y, width, height);
setupObject(obj, props);
return obj;
+
+ } else if (type == "loglist") {
+ LogList* obj = new LogList(x, y, width, height, 20);
+ string logClass = props->getStringValue("logclass");
+ if (logClass == "terrasync") {
+ simgear::SGTerraSync* tsync = (simgear::SGTerraSync*) globals->get_subsystem("terrasync");
+ obj->setBuffer(tsync->log());
+ } else {
+ FGNasalSys* nasal = (FGNasalSys*) globals->get_subsystem("nasal");
+ obj->setBuffer(nasal->log());
+ }
+
+ setupObject(obj, props);
+ _activeWidgets.push_back(obj);
+ return obj;
} else {
return 0;
}
}
SGPropertyNode *dest = fgGetNode("/sim/bindings/gui", true);
- vector<SGPropertyNode_ptr> bindings = props->getChildren("binding");
+ std::vector<SGPropertyNode_ptr> bindings = props->getChildren("binding");
if (bindings.size() > 0) {
info->key = props->getIntValue("keynum", -1);
if (props->hasValue("key"))
return key;
}
-void\fFGPUIDialog::relayout()
+void FGPUIDialog::relayout()
{
_needsRelayout = false;
-\f
////////////////////////////////////////////////////////////////////////
// Implementation of fgValueList and derived pui widgets
////////////////////////////////////////////////////////////////////////
vname = _props->getStringValue("property-name");
}
- vector<SGPropertyNode_ptr> value_nodes = values->getChildren(vname);
+ std::vector<SGPropertyNode_ptr> value_nodes = values->getChildren(vname);
_list = new char *[value_nodes.size() + 1];
unsigned int i;
for (i = 0; i < value_nodes.size(); i++) {
setTopItem(top);
}
+////////////////////////////////////////////////////////////////////////
+// Implementation of fgLogList
+////////////////////////////////////////////////////////////////////////
+
+void LogList::update()
+{
+ if (!m_buffer) return;
+
+ if (m_stamp != m_buffer->stamp()) {
+ m_stamp = m_buffer->threadsafeCopy(m_items);
+ m_items.push_back(NULL); // terminator value
+ newList((char**) m_items.data());
+ setTopItem(m_items.size() - 1); // scroll to bottom of list
+ }
+}
+
+void LogList::setBuffer(simgear::BufferedLogCallback* buf)
+{
+ m_buffer = buf;
+ m_stamp = m_buffer->stamp() - 1; // force an update
+ update();
+}
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of fgComboBox
+////////////////////////////////////////////////////////////////////////
+
+
void fgComboBox::update()
{
if (_inHit) {