[qet] [4084] Primitive part : Change the cursor shape, according to the current available behavior

[ Thread Index | Date Index | More lists.tuxfamily.org/qet Archives ]


Revision: 4084
Author:   blacksun
Date:     2015-08-03 19:26:57 +0200 (Mon, 03 Aug 2015)
Log Message:
-----------
Primitive part : Change the cursor shape, according to the current available behavior

Modified Paths:
--------------
    trunk/sources/editor/graphicspart/customelementgraphicpart.cpp
    trunk/sources/editor/graphicspart/customelementgraphicpart.h
    trunk/sources/editor/graphicspart/partarc.cpp
    trunk/sources/editor/graphicspart/partarc.h
    trunk/sources/editor/graphicspart/partellipse.cpp
    trunk/sources/editor/graphicspart/partellipse.h
    trunk/sources/editor/graphicspart/partline.cpp
    trunk/sources/editor/graphicspart/partline.h
    trunk/sources/editor/graphicspart/partpolygon.cpp
    trunk/sources/editor/graphicspart/partpolygon.h
    trunk/sources/editor/graphicspart/partrectangle.cpp
    trunk/sources/editor/graphicspart/partrectangle.h

Modified: trunk/sources/editor/graphicspart/customelementgraphicpart.cpp
===================================================================
--- trunk/sources/editor/graphicspart/customelementgraphicpart.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/customelementgraphicpart.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -36,10 +36,7 @@
 	_color(BlackColor),
 	_antialiased(false)
 {
-	setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
-#if QT_VERSION >= 0x040600
-	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
-#endif
+	setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
 	setAcceptHoverEvents(true);
 }
 
@@ -427,6 +424,14 @@
 	QGraphicsObject::hoverEnterEvent(event);
 }
 
+void CustomElementGraphicPart::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (isSelected())
+		setCursor(Qt::OpenHandCursor);
+
+	QGraphicsObject::hoverMoveEvent(event);
+}
+
 /**
  * @brief CustomElementGraphicPart::hoverLeaveEvent
  * Reimplemented from QGraphicsObject.
@@ -436,6 +441,7 @@
 void CustomElementGraphicPart::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
 {
 	m_hovered = false;
+	unsetCursor();
 	QGraphicsObject::hoverLeaveEvent(event);
 }
 

Modified: trunk/sources/editor/graphicspart/customelementgraphicpart.h
===================================================================
--- trunk/sources/editor/graphicspart/customelementgraphicpart.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/customelementgraphicpart.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -103,6 +103,7 @@
 
 		QVariant itemChange(GraphicsItemChange change, const QVariant &value);
 		void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+		void hoverMoveEvent (QGraphicsSceneHoverEvent *event);
 		void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
 
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);

Modified: trunk/sources/editor/graphicspart/partarc.cpp
===================================================================
--- trunk/sources/editor/graphicspart/partarc.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partarc.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -65,8 +65,14 @@
 	
 	if (isSelected())
 	{
+		painter->save();
+		QPen pen(Qt::DotLine);
+		pen.setWidth(1);
+		pen.setCosmetic(true);
+		painter->setPen(pen);
 			//Draw the ellipse in black
 		painter -> drawEllipse(rect());
+		painter->restore();
 
 			//Draw the arc in red
 		t.setColor(Qt::red);
@@ -143,7 +149,7 @@
 	shape.arcTo(m_rect, m_start_angle/16, m_span_angle/16);
 
 	QPainterPathStroker pps;
-	pps.setWidth(penWeight());
+	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
 	shape = pps.createStroke(shape);
 
 	if (isSelected())
@@ -165,6 +171,29 @@
 	return (pps.createStroke(shape));
 }
 
+void PartArc::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (!isSelected())
+	{
+		CustomElementGraphicPart::hoverMoveEvent(event);
+		return;
+	}
+
+	int handler = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+
+	if (handler >= 0)
+	{
+		if (handler == 0 || handler == 2 || handler == 5 || handler == 7)
+			setCursor(Qt::SizeAllCursor);
+		else if (handler == 1 || handler == 6)
+			setCursor(Qt::SizeVerCursor);
+		else if (handler == 3 || handler == 4)
+			setCursor(Qt::SizeHorCursor);
+	}
+	else
+		CustomElementGraphicPart::hoverMoveEvent(event);
+}
+
 /**
  * @brief PartArc::mousePressEvent
  * Handle mouse press event
@@ -172,21 +201,24 @@
  */
 void PartArc::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
-	if (isSelected() && event->button() == Qt::LeftButton)
+	if (event->button() == Qt::LeftButton)
 	{
-		m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+		setCursor(Qt::ClosedHandCursor);
+		if (isSelected())
+		{
+			m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
 
-		if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
-		{
-			m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
-			m_undo_command->setText(tr("Modifier un arc"));
-			m_undo_command->enableAnimation();
+			if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
+			{
+				m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
+				m_undo_command->setText(tr("Modifier un arc"));
+				m_undo_command->enableAnimation();
+				return;
+			}
 		}
-		else
-			CustomElementGraphicPart::mousePressEvent(event);
 	}
-	else
-		CustomElementGraphicPart::mousePressEvent(event);
+
+	CustomElementGraphicPart::mousePressEvent(event);
 }
 
 /**
@@ -213,6 +245,9 @@
  */
 void PartArc::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 {
+	if (event->button() == Qt::LeftButton)
+		setCursor(Qt::OpenHandCursor);
+
 	if (m_handler_index >= 0 && m_handler_index <= 7)
 	{
 		if (!m_rect.isValid())

Modified: trunk/sources/editor/graphicspart/partarc.h
===================================================================
--- trunk/sources/editor/graphicspart/partarc.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partarc.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -59,6 +59,7 @@
 		virtual QPainterPath shadowShape() const;
 
 	protected:
+		virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

Modified: trunk/sources/editor/graphicspart/partellipse.cpp
===================================================================
--- trunk/sources/editor/graphicspart/partellipse.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partellipse.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -145,7 +145,7 @@
 	shape.addEllipse(m_rect);
 
 	QPainterPathStroker pps;
-	pps.setWidth(penWeight());
+	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
 	shape = pps.createStroke(shape);
 
 	if (isSelected())
@@ -166,6 +166,29 @@
 	return (pps.createStroke(shape));
 }
 
+void PartEllipse::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (!isSelected())
+	{
+		CustomElementGraphicPart::hoverMoveEvent(event);
+		return;
+	}
+
+	int handler = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+
+	if (handler >= 0)
+	{
+		if (handler == 0 || handler == 2 || handler == 5 || handler == 7)
+			setCursor(Qt::SizeAllCursor);
+		else if (handler == 1 || handler == 6)
+			setCursor(Qt::SizeVerCursor);
+		else if (handler == 3 || handler == 4)
+			setCursor(Qt::SizeHorCursor);
+	}
+	else
+		CustomElementGraphicPart::hoverMoveEvent(event);
+}
+
 /**
  * @brief PartEllipse::mousePressEvent
  * Handle mouse press event
@@ -173,21 +196,24 @@
  */
 void PartEllipse::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
-	if (isSelected() && event->button() == Qt::LeftButton)
+	if (event->button() == Qt::LeftButton)
 	{
-		m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+		setCursor(Qt::ClosedHandCursor);
+		if (isSelected())
+		{
+			m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
 
-		if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
-		{
-			m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
-			m_undo_command->setText(tr("Modifier une ellipse"));
-			m_undo_command->enableAnimation();
+			if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
+			{
+				m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
+				m_undo_command->setText(tr("Modifier une ellipse"));
+				m_undo_command->enableAnimation();
+				return;
+			}
 		}
-		else
-			CustomElementGraphicPart::mousePressEvent(event);
 	}
-	else
-		CustomElementGraphicPart::mousePressEvent(event);
+
+	CustomElementGraphicPart::mousePressEvent(event);
 }
 
 /**
@@ -196,7 +222,7 @@
  * @param event
  */
 void PartEllipse::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
-{
+{		
 	if(m_handler_index >= 0 && m_handler_index <= 7)
 	{
 		QPointF pos_ = event->modifiers() == Qt::ControlModifier ? event->pos() : mapFromScene(elementScene()->snapToGrid(event->scenePos()));
@@ -214,6 +240,9 @@
  */
 void PartEllipse::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 {
+	if (event->button() == Qt::LeftButton)
+		setCursor(Qt::OpenHandCursor);
+
 	if (m_handler_index >= 0 && m_handler_index <= 7)
 	{
 		if (!m_rect.isValid())

Modified: trunk/sources/editor/graphicspart/partellipse.h
===================================================================
--- trunk/sources/editor/graphicspart/partellipse.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partellipse.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -61,6 +61,7 @@
 		virtual QPainterPath shadowShape() const;
 
 	protected:
+		virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

Modified: trunk/sources/editor/graphicspart/partline.cpp
===================================================================
--- trunk/sources/editor/graphicspart/partline.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partline.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -146,21 +146,25 @@
  */
 void PartLine::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
-	if(isSelected() && event->button() == Qt::LeftButton)
+	if(event->button() == Qt::LeftButton)
 	{
-		m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForLine(m_line));
+		setCursor(Qt::ClosedHandCursor);
 
-		if(m_handler_index >= 0 && m_handler_index <= 1) //User click on an handler
+		if (isSelected())
 		{
-			m_undo_command = new QPropertyUndoCommand(this, "line", QVariant(m_line));
-			m_undo_command->setText(tr("Modifier une ligne"));
-			m_undo_command->enableAnimation();
+			m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForLine(m_line));
+
+			if(m_handler_index >= 0 && m_handler_index <= 1) //User click on an handler
+			{
+				m_undo_command = new QPropertyUndoCommand(this, "line", QVariant(m_line));
+				m_undo_command->setText(tr("Modifier une ligne"));
+				m_undo_command->enableAnimation();
+				return;
+			}
 		}
-		else
-			CustomElementGraphicPart::mousePressEvent(event);
 	}
-	else
-		CustomElementGraphicPart::mousePressEvent(event);
+
+	CustomElementGraphicPart::mousePressEvent(event);
 }
 
 /**
@@ -187,6 +191,9 @@
  */
 void PartLine::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 {
+	if (event->button() == Qt::LeftButton)
+		setCursor(Qt::OpenHandCursor);
+
 	if (m_handler_index >= 0 && m_handler_index <= 1)
 	{
 		m_undo_command->setNewValue(QVariant(m_line));
@@ -233,7 +240,7 @@
 	}
 
 	QPainterPathStroker pps;
-	pps.setWidth(penWeight());
+	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
 	shape = pps.createStroke(shape);
 
 	if (isSelected())
@@ -526,6 +533,20 @@
 	emit secondEndLengthChanged();
 }
 
+void PartLine::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (!isSelected())
+	{
+		CustomElementGraphicPart::hoverMoveEvent(event);
+		return;
+	}
+
+	if (m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForLine(m_line)) >= 0)
+		setCursor(Qt::SizeAllCursor);
+	else
+		CustomElementGraphicPart::hoverMoveEvent(event);
+}
+
 /**
  * @brief PartLine::path
  * @return this line has a QPainterPath.

Modified: trunk/sources/editor/graphicspart/partline.h
===================================================================
--- trunk/sources/editor/graphicspart/partline.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partline.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -96,6 +96,7 @@
 		void setSecondEndLength(const qreal &l);
 
 	protected:
+		virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

Modified: trunk/sources/editor/graphicspart/partpolygon.cpp
===================================================================
--- trunk/sources/editor/graphicspart/partpolygon.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partpolygon.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -248,6 +248,20 @@
 	emit closedChange();
 }
 
+void PartPolygon::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (!isSelected())
+	{
+		CustomElementGraphicPart::hoverMoveEvent(event);
+		return;
+	}
+
+	if (m_handler.pointIsHoverHandler(event->pos(), m_polygon) >= 0)
+		setCursor(Qt::SizeAllCursor);
+	else
+		CustomElementGraphicPart::hoverMoveEvent(event);
+}
+
 /**
  * @brief PartPolygon::mousePressEvent
  * Handle mouse press event
@@ -255,20 +269,23 @@
  */
 void PartPolygon::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
-	if (isSelected() && event->button() == Qt::LeftButton)
+	if (event->button() == Qt::LeftButton)
 	{
-		m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_polygon);
+		setCursor(Qt::ClosedHandCursor);
+		if(isSelected())
+		{
+			m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_polygon);
 
-		if(m_handler_index >= 0) //User click on an handler
-		{
-			m_undo_command = new QPropertyUndoCommand(this, "polygon", QVariant(m_polygon));
-			m_undo_command->setText(tr("Modifier un polygone"));
+			if(m_handler_index >= 0) //User click on an handler
+			{
+				m_undo_command = new QPropertyUndoCommand(this, "polygon", QVariant(m_polygon));
+				m_undo_command->setText(tr("Modifier un polygone"));
+				return;
+			}
 		}
-		else
-			CustomElementGraphicPart::mousePressEvent(event);
 	}
-	else
-		CustomElementGraphicPart::mousePressEvent(event);
+
+	CustomElementGraphicPart::mousePressEvent(event);
 }
 
 /**
@@ -296,6 +313,9 @@
  */
 void PartPolygon::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 {
+	if (event->button() == Qt::LeftButton)
+		setCursor(Qt::OpenHandCursor);
+
 	if (m_handler_index >= 0)
 	{
 		m_undo_command->setNewValue(QVariant(m_polygon));
@@ -320,7 +340,7 @@
 		shape.lineTo(m_polygon.first());
 
 	QPainterPathStroker pps;
-	pps.setWidth(penWeight());
+	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
 	shape = pps.createStroke(shape);
 
 	if (isSelected())

Modified: trunk/sources/editor/graphicspart/partpolygon.h
===================================================================
--- trunk/sources/editor/graphicspart/partpolygon.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partpolygon.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -85,6 +85,7 @@
 		void setClosed (bool close);
 
 	protected:
+		virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

Modified: trunk/sources/editor/graphicspart/partrectangle.cpp
===================================================================
--- trunk/sources/editor/graphicspart/partrectangle.cpp	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partrectangle.cpp	2015-08-03 17:26:57 UTC (rev 4084)
@@ -161,7 +161,7 @@
 	shape.addRect(m_rect);
 
 	QPainterPathStroker pps;
-	pps.setWidth(penWeight());
+	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
 	shape = pps.createStroke(shape);
 
 	if (isSelected())
@@ -237,6 +237,29 @@
 	setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))));
 }
 
+void PartRectangle::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+	if (!isSelected())
+	{
+		CustomElementGraphicPart::hoverMoveEvent(event);
+		return;
+	}
+
+	int handler = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+
+	if (handler >= 0)
+	{
+		if (handler == 0 || handler == 2 || handler == 5 || handler == 7)
+			setCursor(Qt::SizeAllCursor);
+		else if (handler == 1 || handler == 6)
+			setCursor(Qt::SizeVerCursor);
+		else if (handler == 3 || handler == 4)
+			setCursor(Qt::SizeHorCursor);
+	}
+	else
+		CustomElementGraphicPart::hoverMoveEvent(event);
+}
+
 /**
  * @brief PartRectangle::mousePressEvent
  * Handle mouse press event
@@ -244,21 +267,24 @@
  */
 void PartRectangle::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
-	if (isSelected() && event->button() == Qt::LeftButton)
+	if (event->button() == Qt::LeftButton)
 	{
-		m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
+		setCursor(Qt::ClosedHandCursor);
+		if(isSelected())
+		{
+			m_handler_index = m_handler.pointIsHoverHandler(event->pos(), m_handler.pointsForRect(m_rect));
 
-		if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
-		{
-			m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
-			m_undo_command->setText(tr("Modifier un rectangle"));
-			m_undo_command->enableAnimation();
+			if(m_handler_index >= 0 && m_handler_index <= 7) //User click on an handler
+			{
+				m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
+				m_undo_command->setText(tr("Modifier un rectangle"));
+				m_undo_command->enableAnimation();
+				return;
+			}
 		}
-		else
-			CustomElementGraphicPart::mousePressEvent(event);
 	}
-	else
-		CustomElementGraphicPart::mousePressEvent(event);
+
+	CustomElementGraphicPart::mousePressEvent(event);
 }
 
 /**
@@ -285,6 +311,9 @@
  */
 void PartRectangle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 {
+	if (event->button() == Qt::LeftButton)
+		setCursor(Qt::OpenHandCursor);
+
 	if (m_handler_index >= 0 && m_handler_index <= 7)
 	{
 		if (!m_rect.isValid())

Modified: trunk/sources/editor/graphicspart/partrectangle.h
===================================================================
--- trunk/sources/editor/graphicspart/partrectangle.h	2015-08-03 05:43:44 UTC (rev 4083)
+++ trunk/sources/editor/graphicspart/partrectangle.h	2015-08-03 17:26:57 UTC (rev 4084)
@@ -75,6 +75,7 @@
 		virtual void handleUserTransformation(const QRectF &, const QRectF &);
 
 	protected:
+		virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
 		virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
 		virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/