]> git.mxchange.org Git - flightgear.git/blobdiff - src/GUI/FGPUIDialog.cxx
Support for multiple data dirs.
[flightgear.git] / src / GUI / FGPUIDialog.cxx
index 61bba82b680095f8ecc4cb55f5ae529bfd0e9d38..5620ae0fc6c9d95a6587301da1f469d8348883e5 100644 (file)
@@ -6,6 +6,8 @@
 
 #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 "property_list.hxx"
 #include "layout.hxx"
 #include "WaypointList.hxx"
+#include "CanvasWidget.hxx"
 #include "MapWidget.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;
@@ -82,6 +87,109 @@ validate_format(const char *f)
     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.
@@ -98,7 +206,7 @@ struct GUIInfo
 
     FGPUIDialog *dialog;
     SGPropertyNode_ptr node;
-    vector <SGBinding *> bindings;
+    std::vector <SGBinding *> bindings;
     int key;
     string label, legend, text, format;
     format_type fmt_type;
@@ -138,7 +246,6 @@ void GUIInfo::apply_format(SGPropertyNode *n)
 }
 
 
-\f
 /**
  * Key handler.
  */
@@ -224,9 +331,16 @@ int fgPopup::checkHit(int button, int updown, int x, int y)
         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);
@@ -339,6 +453,32 @@ int fgPopup::getHitObjects(puObject *object, int x, int y)
     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
@@ -404,7 +544,8 @@ void FGPUIDialog::ConditionalObject::update(FGPUIDialog* aDlg)
   
   aDlg->setNeedsLayout();
 }
-\f////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
 // Callbacks.
 ////////////////////////////////////////////////////////////////////////
 
@@ -427,7 +568,6 @@ action_callback (puObject *object)
 }
 
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Static helper functions.
 ////////////////////////////////////////////////////////////////////////
@@ -490,9 +630,17 @@ copy_from_pui (puObject *object, SGPropertyNode *node)
         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)
@@ -502,7 +650,6 @@ copy_from_pui (puObject *object, SGPropertyNode *node)
 }
 
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of FGDialog.
 ////////////////////////////////////////////////////////////////////////
@@ -625,6 +772,10 @@ FGPUIDialog::update ()
     _conditionalObjects[j]->update(this);
   }
   
+  for (unsigned int i = 0; i < _activeWidgets.size(); i++) {
+    _activeWidgets[i]->update();
+  }
+  
   if (_needsRelayout) {
     relayout();
   }
@@ -827,6 +978,13 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh
         MapWidget* mapWidget = new MapWidget(x, y, x + width, y + height);
         setupObject(mapWidget, props);
         return mapWidget;
+    } else if (type == "canvas") {
+        CanvasWidget* canvasWidget = new CanvasWidget( x, y,
+                                                       x + width, y + height,
+                                                       props,
+                                                       _module );
+        setupObject(canvasWidget, props);
+        return canvasWidget;
     } else if (type == "combo") {
         fgComboBox *obj = new fgComboBox(x, y, x + width, y + height, props,
                 props->getBoolValue("editable", false));
@@ -893,6 +1051,21 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh
         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;
     }
@@ -982,7 +1155,7 @@ FGPUIDialog::setupObject (puObject *object, SGPropertyNode *props)
     }
 
     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"))
@@ -1170,7 +1343,7 @@ FGPUIDialog::getKeyCode(const char *str)
     return key;
 }
 
-void\fFGPUIDialog::relayout()
+void FGPUIDialog::relayout()
 {
   _needsRelayout = false;
   
@@ -1275,7 +1448,6 @@ FGPUIDialog::PropertyObject::PropertyObject(const char *n,
 
 
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of fgValueList and derived pui widgets
 ////////////////////////////////////////////////////////////////////////
@@ -1315,7 +1487,7 @@ fgValueList::make_list()
     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++) {
@@ -1344,6 +1516,34 @@ fgList::update()
     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) {