Unity 8
menucontentactivator.cpp
1 /*
2  * Copyright (C) 2013 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Nick Dedekind <nick.dedekind@canonical.com>
17  */
18 
19  #include "menucontentactivator.h"
20 
21 // Essentially a QTimer wrapper
22 class ContentTimer : public UnityIndicators::AbstractTimer
23 {
24  Q_OBJECT
25 public:
26  ContentTimer(QObject *parent) : UnityIndicators::AbstractTimer(parent) {
27  m_timer.setSingleShot(false);
28  connect(&m_timer, &QTimer::timeout,
29  this, &UnityIndicators::AbstractTimer::timeout);
30  }
31  int interval() const override { return m_timer.interval(); }
32  void setInterval(int msecs) override { m_timer.setInterval(msecs); }
33  void start() override { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34  void stop() override { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
35 private:
36  QTimer m_timer;
37 };
38 
39 class MenuContentActivatorPrivate : public QObject
40 {
41  Q_OBJECT
42 public:
43  MenuContentActivatorPrivate(MenuContentActivator* parent)
44  : m_running(false),
45  m_baseIndex(0),
46  m_delta(0),
47  m_count(0),
48  m_timer(nullptr),
49  q(parent)
50  {}
51 
52  ~MenuContentActivatorPrivate()
53  {
54  qDeleteAll(m_content);
55  m_content.clear();
56  }
57 
58  int findNextInactiveDelta(bool* finished = nullptr);
59 
60  static int content_count(QQmlListProperty<MenuContentState> *prop);
61  static MenuContentState* content_at(QQmlListProperty<MenuContentState> *prop, int index);
62 
63  bool m_running;
64  int m_baseIndex;
65  int m_delta;
66  int m_count;
67  UnityIndicators::AbstractTimer* m_timer;
68  QMap<int, MenuContentState*> m_content;
69  MenuContentActivator* q;
70 };
71 
72 MenuContentActivator::MenuContentActivator(QObject* parent)
73  : QObject(parent),
74  d(new MenuContentActivatorPrivate(this))
75 {
76  qRegisterMetaType<QQmlListProperty<MenuContentState> > ("QQmlListProperty<MenuContentState>");
77 
78  setContentTimer(new ContentTimer(this));
79  d->m_timer->setInterval(75);
80 }
81 
82 MenuContentActivator::~MenuContentActivator()
83 {
84  delete d;
85 }
86 
87 void MenuContentActivator::restart()
88 {
89  // when we start, make sure we have the base index in the list.
90  setMenuContentState(d->m_baseIndex, true);
91  setDelta(0);
92 
93  // check if we've finished before starting the timer.
94  bool finished = false;
95  d->findNextInactiveDelta(&finished);
96  if (!finished) {
97  d->m_timer->start();
98  } else {
99  d->m_timer->stop();
100  }
101 
102  if (!d->m_running) {
103  d->m_running = true;
104  Q_EMIT runningChanged(true);
105  }
106 }
107 
108 void MenuContentActivator::stop()
109 {
110  d->m_timer->stop();
111  if (!d->m_running) {
112  d->m_running = false;
113  Q_EMIT runningChanged(false);
114  }
115 }
116 
117 void MenuContentActivator::clear()
118 {
119  qDeleteAll(d->m_content);
120  d->m_content.clear();
121 
122  setDelta(0);
123  d->m_timer->stop();
124 
125  Q_EMIT contentChanged();
126 }
127 
128 bool MenuContentActivator::isMenuContentActive(int index) const
129 {
130  if (d->m_content.contains(index))
131  return d->m_content[index]->isActive();
132  return false;
133 }
134 
135 void MenuContentActivator::setRunning(bool running)
136 {
137  if (running) {
138  restart();
139  } else {
140  stop();
141  }
142 }
143 
144 bool MenuContentActivator::isRunning() const
145 {
146  return d->m_running;
147 }
148 
149 void MenuContentActivator::setBaseIndex(int index)
150 {
151  if (d->m_baseIndex != index) {
152  d->m_baseIndex = index;
153 
154  if (isRunning()) {
155  restart();
156  }
157 
158  Q_EMIT baseIndexChanged(index);
159  }
160 }
161 
162 int MenuContentActivator::baseIndex() const
163 {
164  return d->m_baseIndex;
165 }
166 
167 void MenuContentActivator::setCount(int count)
168 {
169  if (d->m_count != count) {
170  d->m_count = count;
171  Q_EMIT countChanged(count);
172 
173  if (isRunning()) {
174  restart();
175  }
176  }
177 }
178 
179 int MenuContentActivator::count() const
180 {
181  return d->m_count;
182 }
183 
184 void MenuContentActivator::setDelta(int delta)
185 {
186  if (d->m_delta != delta) {
187  d->m_delta = delta;
188  Q_EMIT deltaChanged(d->m_delta);
189  }
190 }
191 
192 int MenuContentActivator::delta() const
193 {
194  return d->m_delta;
195 }
196 
197 QQmlListProperty<MenuContentState> MenuContentActivator::content()
198 {
199  return QQmlListProperty<MenuContentState>(this,
200  0,
201  MenuContentActivatorPrivate::content_count,
202  MenuContentActivatorPrivate::content_at);
203 }
204 
205 void MenuContentActivator::onTimeout()
206 {
207  bool finished = false;
208  int tempDelta = d->findNextInactiveDelta(&finished);
209  if (!finished) {
210  setMenuContentState(d->m_baseIndex + tempDelta, true);
211  setDelta(tempDelta);
212  }
213 
214  if (finished) {
215  d->m_timer->stop();
216  }
217 }
218 
219 void MenuContentActivator::setContentTimer(UnityIndicators::AbstractTimer *timer)
220 {
221  int interval = 0;
222  bool timerWasRunning = false;
223 
224  // can be null when called from the constructor
225  if (d->m_timer) {
226  interval = d->m_timer->interval();
227  timerWasRunning = d->m_timer->isRunning();
228  if (d->m_timer->parent() == this) {
229  delete d->m_timer;
230  }
231  }
232 
233  d->m_timer = timer;
234  timer->setInterval(interval);
235  connect(timer, &UnityIndicators::AbstractTimer::timeout,
236  this, &MenuContentActivator::onTimeout);
237  if (timerWasRunning) {
238  d->m_timer->start();
239  }
240 }
241 
242 void MenuContentActivator::setMenuContentState(int index, bool active)
243 {
244  if (d->m_content.contains(index)) {
245  d->m_content[index]->setActive(active);
246  } else {
247  d->m_content[index] = new MenuContentState(active);
248  Q_EMIT contentChanged();
249  }
250 }
251 
252 int MenuContentActivatorPrivate::findNextInactiveDelta(bool* finished)
253 {
254  if (m_count == 0 || m_baseIndex >= m_count) {
255  if (finished) *finished = true;
256  return 0;
257  }
258 
259  int tmpDelta = m_delta;
260  bool topReached = false, bottomReached = false;
261  while(true) {
262 
263  // prechecks for bottom and top limits.
264  if (tmpDelta > 0 && bottomReached) tmpDelta = -tmpDelta;
265  if (tmpDelta < 0 && topReached) tmpDelta = (-tmpDelta) + 1;
266 
267  if (tmpDelta > 0) {
268  // negative of baseIndex
269  tmpDelta = -tmpDelta;
270  // reached the bottom?
271  if (m_baseIndex + tmpDelta < 0) {
272  bottomReached = true;
273  // if we've reached the top as well, then we know we're done.
274  if (topReached) {
275  if (finished) *finished = true;
276  return 0;
277  }
278  continue;
279  }
280  } else {
281  // positive of baseIndex
282  tmpDelta = (-tmpDelta) + 1;
283  // reached the top?
284  if (m_baseIndex + tmpDelta >= m_count) {
285  topReached = true;
286  // if we've reached the bottom as well, then we know we're done.
287  if (bottomReached) {
288  if (finished) *finished = true;
289  return 0;
290  }
291  continue;
292  }
293  }
294 
295  if (q->isMenuContentActive(m_baseIndex + tmpDelta)) {
296  continue;
297  }
298  break;
299  }
300  if (finished) *finished = false;
301  return tmpDelta;
302 }
303 
304 int MenuContentActivatorPrivate::content_count(QQmlListProperty<MenuContentState> *prop)
305 {
306  MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
307  // we'll create MenuContentState on demand.
308  return p->count();
309 }
310 
311 MenuContentState* MenuContentActivatorPrivate::content_at(QQmlListProperty<MenuContentState> *prop, int index)
312 {
313  MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
314  MenuContentActivatorPrivate *d = p->d;
315 
316  if (!d->m_content.contains(index)) {
317  MenuContentState* content = new MenuContentState(false);
318  d->m_content[index] = content;
319  return content;
320  }
321 
322  return d->m_content[index];
323 }
324 
325 MenuContentState::MenuContentState(bool active)
326  : m_active(active)
327 {
328 }
329 
330 bool MenuContentState::isActive() const
331 {
332  return m_active;
333 }
334 
335 void MenuContentState::setActive(bool active)
336 {
337  if (m_active != active) {
338  m_active = active;
339  Q_EMIT activeChanged();
340  }
341 }
342 
343 // Because we are defining a new QObject-based class (ContentTimer) here.
344 #include "menucontentactivator.moc"