27 |
27 |
28 /*! \class MMF::MediaNode |
28 /*! \class MMF::MediaNode |
29 \internal |
29 \internal |
30 */ |
30 */ |
31 |
31 |
32 MMF::MediaNode::MediaNode(QObject *parent) : QObject::QObject(parent) |
32 MMF::MediaNode::MediaNode(QObject *parent) |
33 , m_source(0) |
33 : QObject(parent) |
34 , m_target(0) |
34 , m_mediaObject(qobject_cast<MediaObject *>(this)) |
35 , m_isApplied(false) |
35 , m_input(0) |
36 { |
36 { |
|
37 |
37 } |
38 } |
38 |
39 |
39 bool MMF::MediaNode::connectMediaNode(MediaNode *target) |
40 MMF::MediaNode::~MediaNode() |
40 { |
41 { |
41 m_target = target; |
42 // Phonon framework ensures nodes are disconnected before being destroyed. |
42 m_target->setSource(this); |
43 Q_ASSERT_X(!m_mediaObject, Q_FUNC_INFO, |
43 |
44 "Media node not disconnected before destruction"); |
44 return applyNodesOnMediaObject(target); |
|
45 } |
45 } |
46 |
46 |
47 bool MMF::MediaNode::disconnectMediaNode(MediaNode *target) |
47 bool MMF::MediaNode::connectOutput(MediaNode *output) |
48 { |
48 { |
49 Q_UNUSED(target); |
49 Q_ASSERT_X(output, Q_FUNC_INFO, "Null output pointer"); |
50 m_target = 0; |
50 |
51 m_isApplied = false; |
51 bool connected = false; |
52 return true; |
52 |
|
53 // Check that this connection will not result in a graph which |
|
54 // containing more than one MediaObject |
|
55 const bool mediaObjectMisMatch = |
|
56 m_mediaObject |
|
57 && output->m_mediaObject |
|
58 && m_mediaObject != output->m_mediaObject; |
|
59 |
|
60 const bool canConnect = |
|
61 !output->isMediaObject() |
|
62 && !output->m_input |
|
63 && !m_outputs.contains(output); |
|
64 |
|
65 if (canConnect && !mediaObjectMisMatch) { |
|
66 output->m_input = this; |
|
67 m_outputs += output; |
|
68 updateMediaObject(); |
|
69 connected = true; |
|
70 } |
|
71 |
|
72 return connected; |
53 } |
73 } |
54 |
74 |
55 void MMF::MediaNode::setSource(MediaNode *source) |
75 bool MMF::MediaNode::disconnectOutput(MediaNode *output) |
56 { |
76 { |
57 m_source = source; |
77 Q_ASSERT_X(output, Q_FUNC_INFO, "Null output pointer"); |
|
78 |
|
79 bool disconnected = false; |
|
80 |
|
81 if (m_outputs.contains(output) && this == output->m_input) { |
|
82 output->m_input = 0; |
|
83 const bool removed = m_outputs.removeOne(output); |
|
84 Q_ASSERT_X(removed, Q_FUNC_INFO, "Output removal failed"); |
|
85 |
|
86 Q_ASSERT_X(!m_outputs.contains(output), Q_FUNC_INFO, |
|
87 "Output list contains duplicate entries"); |
|
88 |
|
89 // Perform traversal across each of the two graphs separately |
|
90 updateMediaObject(); |
|
91 output->updateMediaObject(); |
|
92 |
|
93 disconnected = true; |
|
94 } |
|
95 |
|
96 return disconnected; |
58 } |
97 } |
59 |
98 |
60 MMF::MediaNode *MMF::MediaNode::source() const |
99 bool MMF::MediaNode::isMediaObject() const |
61 { |
100 { |
62 return m_source; |
101 return (qobject_cast<const MediaObject *>(this) != 0); |
63 } |
102 } |
64 |
103 |
65 MMF::MediaNode *MMF::MediaNode::target() const |
104 void MMF::MediaNode::updateMediaObject() |
66 { |
105 { |
67 return m_target; |
106 QList<MediaNode *> nodes; |
|
107 MediaObject *mediaObject = 0; |
|
108 |
|
109 // Traverse the graph, collecting a list of nodes, and locating |
|
110 // the MediaObject node, if present |
|
111 visit(nodes, mediaObject); |
|
112 |
|
113 MediaNode *node = 0; |
|
114 foreach(node, nodes) |
|
115 node->setMediaObject(mediaObject); |
68 } |
116 } |
69 |
117 |
70 bool MMF::MediaNode::applyNodesOnMediaObject(MediaNode *) |
118 void MMF::MediaNode::setMediaObject(MediaObject *mediaObject) |
71 { |
119 { |
72 // Algorithmically, this can be expressed in a more efficient way by |
120 if(!isMediaObject() && m_mediaObject != mediaObject) { |
73 // exercising available assumptions, but it complicates code for input |
121 if (!mediaObject) |
74 // data(length of the graph) which typically is very small. |
122 disconnectMediaObject(m_mediaObject); |
|
123 else { |
|
124 Q_ASSERT_X(!m_mediaObject, Q_FUNC_INFO, "MediaObject already set"); |
|
125 connectMediaObject(mediaObject); |
|
126 } |
|
127 m_mediaObject = mediaObject; |
|
128 } |
|
129 } |
75 |
130 |
76 // First, we go to the very beginning of the graph. |
131 void MMF::MediaNode::visit(QList<MediaNode *>& visited, MediaObject*& mediaObject) |
77 MMF::MediaNode *current = this; |
132 { |
78 do { |
133 if (isMediaObject()) { |
79 MediaNode *const candidate = current->source(); |
134 // There can never be more than one MediaObject per graph, due to the |
80 if (candidate) |
135 // mediaObjectMisMatch test in connectOutput(). |
81 current = candidate; |
136 Q_ASSERT_X(!mediaObject, Q_FUNC_INFO, "MediaObject already found"); |
82 else |
137 mediaObject = static_cast<MediaObject *>(this); |
83 break; |
|
84 } |
|
85 while (current); |
|
86 |
|
87 // Now we do two things, while walking to the other end: |
|
88 // 1. Find the MediaObject, if present |
|
89 // 2. Collect a list of all unapplied MediaNodes |
|
90 |
|
91 QList<MediaNode *> unapplied; |
|
92 MMF::MediaObject *mo = 0; |
|
93 |
|
94 do { |
|
95 if (!current->m_isApplied) |
|
96 unapplied.append(current); |
|
97 |
|
98 if (!mo) |
|
99 mo = qobject_cast<MMF::MediaObject *>(current); |
|
100 |
|
101 current = current->target(); |
|
102 } |
|
103 while (current); |
|
104 |
|
105 // Now, lets activate all the objects, if we found the MediaObject. |
|
106 |
|
107 if (mo) { |
|
108 for (int i = 0; i < unapplied.count(); ++i) { |
|
109 MediaNode *const at = unapplied.at(i); |
|
110 |
|
111 // We don't want to apply MediaObject on itself. |
|
112 if (at != mo) |
|
113 at->activateOnMediaObject(mo); |
|
114 } |
|
115 } |
138 } |
116 |
139 |
117 return true; |
140 visited += this; |
|
141 |
|
142 if (m_input && !visited.contains(m_input)) |
|
143 m_input->visit(visited, mediaObject); |
|
144 |
|
145 MediaNode *output = 0; |
|
146 foreach (output, m_outputs) |
|
147 if (!visited.contains(output)) |
|
148 output->visit(visited, mediaObject); |
118 } |
149 } |
119 |
150 |
120 QT_END_NAMESPACE |
151 QT_END_NAMESPACE |
121 |
152 |