]> git.mxchange.org Git - flightgear.git/blob - src/GUI/WaypointList.cxx
accomodate changes to osgDB::DatabasePager interface
[flightgear.git] / src / GUI / WaypointList.cxx
1
2
3 #ifdef HAVE_CONFIG_H
4 #  include "config.h"
5 #endif
6
7 #include "WaypointList.hxx"
8
9 #include <algorithm>
10 #include <boost/tuple/tuple.hpp>
11
12 #include <plib/puAux.h>
13
14 #include <simgear/route/waypoint.hxx>
15 #include <simgear/structure/callback.hxx>
16 #include <simgear/sg_inlines.h>
17
18 #include <Main/globals.hxx>
19 #include <Main/fg_props.hxx>
20
21 #include <Navaids/positioned.hxx>
22 #include <Autopilot/route_mgr.hxx>
23
24 using namespace flightgear;
25
26 enum {
27   SCROLL_NO = 0,
28   SCROLL_UP,
29   SCROLL_DOWN
30 };
31   
32 static const double BLINK_TIME = 0.3;
33 static const int DRAG_START_DISTANCE_PX = 5;
34   
35 class RouteManagerWaypointModel : 
36   public WaypointList::Model, 
37   public SGPropertyChangeListener
38 {
39 public:
40   RouteManagerWaypointModel()
41   {
42     _rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
43     
44     SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
45     routeEdited->addChangeListener(this);
46   }
47   
48   virtual ~RouteManagerWaypointModel()
49   {
50     SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
51     routeEdited->removeChangeListener(this);
52   }
53   
54 // implement WaypointList::Model
55   virtual unsigned int numWaypoints() const
56   {
57     return _rm->numWaypts();
58   }
59   
60   virtual int currentWaypoint() const
61   {
62     return _rm->currentIndex();
63   }
64   
65   virtual flightgear::Waypt* waypointAt(unsigned int index) const
66   {
67     if (index >= numWaypoints()) {
68       return NULL;
69     }
70     
71     return _rm->wayptAtIndex(index);
72   }
73
74   virtual void deleteAt(unsigned int index)
75   {
76     _rm->removeWayptAtIndex(index);
77   }
78   
79   virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex)
80   {
81     SG_LOG(SG_GENERAL, SG_INFO, "moveWaypoint: from " << srcIndex << " to " << destIndex);
82     if (destIndex > srcIndex) {
83       --destIndex;
84     }
85     
86     int currentWpIndex = currentWaypoint();
87     WayptRef w(_rm->removeWayptAtIndex(srcIndex));
88     SG_LOG(SG_GENERAL, SG_INFO, "wpt:" << w->ident());
89     _rm->insertWayptAtIndex(w, destIndex);
90
91     if (srcIndex == currentWpIndex) {
92         // current waypoint was moved
93         _rm->jumpToIndex(destIndex);
94     }
95   }
96   
97   virtual void setUpdateCallback(SGCallback* cb)
98   {
99     _cb = cb;
100   }
101     
102 // implement SGPropertyChangeListener
103   void valueChanged(SGPropertyNode *prop)
104   {
105     if (_cb) {
106       (*_cb)();
107     }
108   }
109 private:
110   FGRouteMgr* _rm;
111   SGCallback* _cb;
112 };
113
114 //////////////////////////////////////////////////////////////////////////////
115
116 static void drawClippedString(puFont& font, const char* s, int x, int y, int maxWidth)
117 {
118   int fullWidth = font.getStringWidth(s);
119   if (fullWidth <= maxWidth) { // common case, easy and efficent
120     font.drawString(s, x, y);
121     return;
122   }
123   
124   std::string buf(s);
125   int len = buf.size();
126   do {
127     buf.resize(--len);
128     fullWidth = font.getStringWidth(buf.c_str());
129   } while (fullWidth > maxWidth);
130   
131   font.drawString(buf.c_str(), x, y);
132 }
133
134 //////////////////////////////////////////////////////////////////////////////
135
136 WaypointList::WaypointList(int x, int y, int width, int height) :
137   puFrame(x, y, width, height),
138   GUI_ID(FGCLASS_WAYPOINTLIST),
139   _scrollPx(0),
140   _dragging(false),
141   _dragScroll(SCROLL_NO),
142   _showLatLon(false),
143   _model(NULL),
144   _updateCallback(NULL),
145   _scrollCallback(NULL),
146   _blink(false)
147 {
148   // pretend to be a list, so fgPopup doesn't mess with our mouse events
149   type |= PUCLASS_LIST;  
150   setModel(new RouteManagerWaypointModel());
151   setSize(width, height);
152   setValue(-1);
153   
154   _blinkTimer.stamp();
155 }
156
157 WaypointList::~WaypointList()
158 {
159   delete _model;
160   delete _updateCallback;
161   delete _scrollCallback;
162 }
163
164 void WaypointList::setUpdateCallback(SGCallback* cb)
165 {
166   _updateCallback = cb;
167 }
168
169 void WaypointList::setScrollCallback(SGCallback* cb)
170 {
171   _scrollCallback = cb;
172 }
173
174 void WaypointList::setSize(int width, int height)
175 {
176   double scrollP = getVScrollPercent();
177   _heightPx = height;
178   puFrame::setSize(width, height);
179   
180   if (wantsVScroll()) {
181     setVScrollPercent(scrollP);
182   } else {
183     _scrollPx = 0;
184   }
185 }
186
187 int WaypointList::checkHit ( int button, int updown, int x, int y )
188 {
189   if ( isHit( x, y ) || ( puActiveWidget () == this ) )
190   {
191     doHit ( button, updown, x, y ) ;
192     return TRUE ;
193   }
194
195   return FALSE ;
196 }
197
198
199 void WaypointList::doHit( int button, int updown, int x, int y )
200 {
201   puFrame::doHit(button, updown, x, y);  
202   if (updown == PU_DRAG) {
203     handleDrag(x, y);
204     return;
205   }
206   
207   if (button != active_mouse_button) {
208     return;
209   }
210       
211   if (updown == PU_UP) {
212     puDeactivateWidget();
213     if (_dragging) {
214       doDrop(x, y);
215       return;
216     }
217   } else if (updown == PU_DOWN) {
218     puSetActiveWidget(this, x, y);
219     _mouseDownX = x;
220     _mouseDownY = y;
221     return;
222   }
223   
224 // update selection
225   int row = rowForY(y - abox.min[1]);
226   if (row >= (int) _model->numWaypoints()) {
227     row = -1; // 'no selection'
228   }
229
230   if (row == getSelected()) {
231     _showLatLon = !_showLatLon;
232     puPostRefresh();
233     return;
234   }
235   
236   setSelected(row);
237 }
238
239 void WaypointList::handleDrag(int x, int y)
240 {
241   if (!_dragging) {
242     // don't start drags immediately, require a certain mouse movement first
243     int manhattanLength = abs(x - _mouseDownX) + abs(y - _mouseDownY);
244     if (manhattanLength < DRAG_START_DISTANCE_PX) {
245       return;
246     }
247     
248     _dragSourceRow = rowForY(y - abox.min[1]);
249     Waypt* wp = _model->waypointAt(_dragSourceRow);
250     if (!wp || wp->flag(WPT_GENERATED)) {
251       return; // don't allow generated points to be dragged
252     }
253     
254     _dragging = true;
255     _dragScroll = SCROLL_NO;
256   }
257   
258   if (y < abox.min[1]) {
259     if (_dragScroll != SCROLL_DOWN) {
260       _dragScroll = SCROLL_DOWN;
261       _dragScrollTime.stamp();
262     }
263   } else if (y > abox.max[1]) {
264     if (_dragScroll != SCROLL_UP) {
265       _dragScroll = SCROLL_UP;
266       _dragScrollTime.stamp();
267     }
268   } else {
269     _dragScroll = SCROLL_NO;
270     _dragTargetRow = rowForY(y - abox.min[1] - (rowHeightPx() / 2));
271   }
272 }
273
274 void WaypointList::doDrop(int x, int y)
275 {
276   _dragging = false;
277   puDeactivateWidget();
278   
279   SG_LOG(SG_GENERAL, SG_INFO, "doDrop");
280   
281   if ((y < abox.min[1]) || (y >= abox.max[1])) {
282     SG_LOG(SG_GENERAL, SG_INFO, "y out of bounds:" << y);
283     return;
284   }
285   
286   if (_dragSourceRow == _dragTargetRow) {
287     SG_LOG(SG_GENERAL, SG_INFO, "source and target row match");
288     return;
289   }
290   
291   _model->moveWaypointToIndex(_dragSourceRow, _dragTargetRow);
292   
293   // keep row indexes linged up when moving an item down the list
294   if (_dragSourceRow < _dragTargetRow) {
295     --_dragTargetRow;
296   }
297   
298   setSelected(_dragTargetRow);
299 }
300
301 void WaypointList::invokeDownCallback(void)
302 {
303   _dragging = false;
304   _dragScroll = SCROLL_NO;
305   SG_LOG(SG_GENERAL, SG_INFO, "cancel drag");
306 }
307
308 int WaypointList::rowForY(int y) const
309 {
310   if (!_model) {
311     return -1;
312   }
313   
314   // flip y to increase down, not up (as rows do)
315   int flipY = _heightPx - y;
316   int row = (flipY + _scrollPx) / rowHeightPx();
317   return row;
318 }
319
320 void WaypointList::draw( int dx, int dy )
321 {
322   puFrame::draw(dx, dy);
323
324   if (!_model) {
325     return;
326   }
327
328   if (_dragScroll != SCROLL_NO) {
329     doDragScroll();
330   }
331   
332   double dt = (SGTimeStamp::now() - _blinkTimer).toSecs();
333   if (dt > BLINK_TIME) {
334     _blinkTimer.stamp();
335     _blink = !_blink;
336   }
337   
338   glEnable(GL_SCISSOR_TEST);
339   GLint sx = (int) abox.min[0],
340     sy = abox.min[1];
341   GLsizei w = (GLsizei) abox.max[0] - abox.min[0],
342     h = _heightPx;
343     
344   sx += border_thickness;
345   sy += border_thickness;
346   w -= 2 * border_thickness;
347   h -= 2 * border_thickness;
348     
349   glScissor(sx + dx, sy + dy, w, h);
350   int row = firstVisibleRow(), 
351     final = lastVisibleRow(),
352     rowHeight = rowHeightPx(),
353     y = rowHeight;
354   
355   y -= (_scrollPx % rowHeight); // partially draw the first row
356   
357   _arrowWidth = legendFont.getStringWidth(">");
358   for ( ; row <= final; ++row, y += rowHeight) {
359     drawRow(dx, dy, row, y);
360   } // of row drawing iteration
361   
362   glDisable(GL_SCISSOR_TEST);
363     
364   if (_dragging) {
365     // draw the insert marker after the rows
366     int insertY = (_dragTargetRow * rowHeight) - _scrollPx;
367     SG_CLAMP_RANGE(insertY, 0, std::min(_heightPx, totalHeightPx()));
368     
369     glColor4f(1.0f, 0.5f, 0.0f, 0.8f);
370     glLineWidth(3.0f);
371     glBegin(GL_LINES);
372       glVertex2f(dx + abox.min[0], dy + abox.max[1] - insertY);
373       glVertex2f(dx + abox.max[0], dy + abox.max[1] - insertY);
374     glEnd();
375   }
376 }
377
378 void WaypointList::drawRow(int dx, int dy, int rowIndex, int y)
379 {
380   flightgear::Waypt* wp(_model->waypointAt(rowIndex));
381     
382   bool isSelected = (rowIndex == getSelected());
383   bool isCurrent = (rowIndex == _model->currentWaypoint());
384   bool isDragSource = (_dragging && (rowIndex == _dragSourceRow));
385   
386   puBox bkgBox = abox;
387   bkgBox.min[1] = abox.max[1] - y;
388   bkgBox.max[1] = bkgBox.min[1] + rowHeightPx();
389   
390   puColour col;
391   puFont* f = &legendFont;
392   bool drawBox = false;
393   
394   if (wp->flag(WPT_MISS)) {
395     drawBox = true;
396     puSetColor(col, 1.0, 0.0, 0.0, 0.3);  // red
397   } else if (wp->flag(WPT_ARRIVAL)) {
398     drawBox = true;
399     puSetColor(col, 0.0, 0.0, 0.0, 0.3);
400   } else if (wp->flag(WPT_DEPARTURE)) {
401     drawBox = true;
402     puSetColor(col, 0.0, 0.0, 0.0, 0.3);
403   }
404   
405   if (isDragSource) {
406     // draw later, on *top* of text string
407   } else if (isSelected) { // -PLAIN means selected, apparently
408     bkgBox.draw(dx, dy, -PUSTYLE_PLAIN, colour, false, 0);
409   } else if (drawBox) {
410     bkgBox.draw(dx, dy, PUSTYLE_PLAIN, &col, false, 0);
411   }
412   
413   if (isCurrent) {
414     glColor4f (1.0, 0.5, 0.0, 1.0) ;
415   } else {
416     glColor4fv ( colour [ PUCOL_LEGEND ] ) ;
417   }
418   
419   int xx = dx + abox.min[0] + PUSTR_LGAP;
420   int yy = dy + abox.max[1] - y ;
421   yy += 4; // center text in row height
422   
423   if (isCurrent) {
424     f->drawString(">", xx, yy);
425   }
426   
427   int x = xx;
428   x += _arrowWidth + PUSTR_LGAP;
429   
430   // row textual data
431
432   char buffer[128];
433   int count = ::snprintf(buffer, 128, "%03d   %-5s", rowIndex, wp->ident().c_str());
434  
435   FGPositioned* src = wp->source(); 
436   if (src && !src->name().empty() && (src->name() != wp->ident())) { 
437     // append name if present, and different to id
438     ::snprintf(buffer + count, 128 - count, " (%s)", src->name().c_str());
439   }
440
441   drawClippedString(legendFont, buffer, x, yy, 300);
442   x += 300 + PUSTR_LGAP;
443   
444   if (_showLatLon) {
445     SGGeod p(wp->position());
446     char ns = (p.getLatitudeDeg() > 0.0) ? 'N' : 'S';
447     char ew = (p.getLongitudeDeg() > 0.0) ? 'E' : 'W';
448     
449     ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c",
450       fabs(p.getLongitudeDeg()), ew, fabs(p.getLatitudeDeg()), ns);
451   } else if (rowIndex > 0) {
452     double courseDeg;
453     double distanceM;
454     Waypt* prev = _model->waypointAt(rowIndex - 1);
455     boost::tie(courseDeg, distanceM) = wp->courseAndDistanceFrom(prev->position());
456   
457     ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm",
458       courseDeg, distanceM * SG_METER_TO_NM);
459   }
460
461   f->drawString(buffer, x, yy);
462   x += 100 + PUSTR_LGAP;
463   
464   if (wp->altitudeRestriction() != RESTRICT_NONE) {
465     int altHundredFt = (wp->altitudeFt() + 50) / 100; // round to nearest 100ft
466     if (altHundredFt < 100) {
467       count = ::snprintf(buffer, 128, "%d'", altHundredFt * 100);
468     } else { // display as a flight-level
469       count = ::snprintf(buffer, 128, "FL%d", altHundredFt);
470     }
471     
472     f->drawString(buffer, x, yy);
473   } // of valid wp altitude
474   x += 60 + PUSTR_LGAP;
475   
476   if (wp->speedRestriction() != RESTRICT_NONE) {
477     count = ::snprintf(buffer, 126, "%dKts", (int) wp->speedKts());
478     f->drawString(buffer, x, yy);
479   }
480   
481   if (isDragSource) {
482     puSetColor(col, 1.0, 0.5, 0.0, 0.5);
483     bkgBox.draw(dx, dy, PUSTYLE_PLAIN, &col, false, 0);
484   }
485 }
486
487 const double SCROLL_PX_SEC = 200.0;
488
489 void WaypointList::doDragScroll()
490 {
491   double dt = (SGTimeStamp::now() - _dragScrollTime).toSecs();
492   _dragScrollTime.stamp();
493   int deltaPx = (int)(dt * SCROLL_PX_SEC);
494   
495   if (_dragScroll == SCROLL_UP) {
496     _scrollPx = _scrollPx - deltaPx;
497     SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
498     _dragTargetRow = firstVisibleRow();
499   } else {
500     _scrollPx = _scrollPx + deltaPx;
501     SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx());
502     _dragTargetRow = lastFullyVisibleRow() + 1;
503   }
504   
505   if (_scrollCallback) {
506     (*_scrollCallback)();
507   }
508 }
509
510 int WaypointList::getSelected()
511 {
512   return getIntegerValue();
513 }
514
515 void WaypointList::setSelected(int rowIndex)
516 {
517   if (rowIndex == getSelected()) {
518     return;
519   }
520   
521   setValue(rowIndex);
522   invokeCallback();
523   if (rowIndex == -1) {
524     return;
525   }
526
527   ensureRowVisible(rowIndex);
528 }
529
530 void WaypointList::ensureRowVisible(int rowIndex)
531 {
532   if ((rowIndex >= firstFullyVisibleRow()) && (rowIndex <= lastFullyVisibleRow())) {
533     return; // already visible, fine
534   }
535   
536   // ideal position would place the desired row in the middle of the
537   // visible section - hence subtract half the visible height.
538   int targetScrollPx = (rowIndex * rowHeightPx()) - (_heightPx / 2);
539   
540   // clamp the scroll value to something valid
541   SG_CLAMP_RANGE(targetScrollPx, 0, scrollRangePx());
542   _scrollPx = targetScrollPx;
543   
544   puPostRefresh();
545   if (_scrollCallback) { // keep scroll observers in sync
546     (*_scrollCallback)();
547   }
548 }
549
550 unsigned int WaypointList::numWaypoints() const
551 {
552   if (!_model) {
553     return 0;
554   }
555   
556   return _model->numWaypoints();
557 }
558
559 bool WaypointList::wantsVScroll() const
560 {
561   return totalHeightPx() > _heightPx;
562 }
563
564 float WaypointList::getVScrollPercent() const
565 {
566   float scrollRange = scrollRangePx();
567   if (scrollRange < 1.0f) {
568     return 0.0;
569   }
570   
571   return _scrollPx / scrollRange;
572 }
573
574 float WaypointList::getVScrollThumbPercent() const
575 {
576   return _heightPx / (float) totalHeightPx();
577 }
578
579 void WaypointList::setVScrollPercent(float perc)
580 {
581   float scrollRange = scrollRangePx();
582   _scrollPx = (int)(scrollRange * perc);
583 }
584
585 int WaypointList::firstVisibleRow() const
586 {
587   return _scrollPx / rowHeightPx();
588 }
589
590 int WaypointList::firstFullyVisibleRow() const
591 {
592   int rh = rowHeightPx();
593   return (_scrollPx + rh - 1) / rh;
594 }
595   
596 int WaypointList::numVisibleRows() const
597 {
598   int rh = rowHeightPx();
599   int topOffset = _scrollPx % rh; // pixels of first visible row
600   return (_heightPx - topOffset + rh - 1) / rh;
601
602 }
603
604 int WaypointList::numFullyVisibleRows() const
605 {
606   int rh = rowHeightPx();
607   int topOffset = _scrollPx % rh; // pixels of first visible row
608   return (_heightPx - topOffset) / rh;
609 }
610
611 int WaypointList::rowHeightPx() const
612 {
613   return legendFont.getStringHeight() + PUSTR_BGAP;
614 }
615
616 int WaypointList::scrollRangePx() const
617 {
618   return std::max(0, totalHeightPx() - _heightPx);
619 }
620
621 int WaypointList::totalHeightPx() const
622 {
623   if (!_model) {
624     return 0;
625   }
626   
627   return (int) _model->numWaypoints() * rowHeightPx();
628 }
629
630 int WaypointList::lastFullyVisibleRow() const
631 {
632   int row = firstFullyVisibleRow() + numFullyVisibleRows();
633   return std::min(row, (int) _model->numWaypoints() - 1);
634 }
635
636 int WaypointList::lastVisibleRow() const
637 {
638   int row = firstVisibleRow() + numVisibleRows();
639   return std::min(row, (int) _model->numWaypoints() - 1);
640 }
641
642 void WaypointList::setModel(Model* model)
643 {
644   if (_model) {
645     delete _model;
646   }
647   
648   _model = model;
649   _model->setUpdateCallback(make_callback(this, &WaypointList::modelUpdateCallback));
650   
651   puPostRefresh();
652 }
653
654 int WaypointList::checkKey (int key, int updown )
655 {
656   if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) {
657     return FALSE ;
658   }
659   
660   switch (key)
661   {
662   case PU_KEY_HOME:
663     setSelected(0);
664     break;
665
666   case PU_KEY_END:
667     setSelected(_model->numWaypoints() - 1);
668     break ;
669
670   case PU_KEY_UP        :
671   case PU_KEY_PAGE_UP   :
672     if (getSelected() >= 0) {
673       setSelected(getSelected() - 1);
674     }
675     break ;
676
677   case PU_KEY_DOWN      :
678   case PU_KEY_PAGE_DOWN : {
679     int newSel = getSelected() + 1;
680     if (newSel >= (int) _model->numWaypoints()) {
681       setSelected(-1);
682     } else {
683       setSelected(newSel);
684     }
685     break ;
686   }
687   
688   case '-':
689     if (getSelected() >= 0) {
690       Waypt* wp = _model->waypointAt(getSelected());
691       if (wp->flag(WPT_GENERATED)) {
692         break;
693       }
694       
695       if (wp->altitudeRestriction() != RESTRICT_NONE) {
696         int curAlt = (static_cast<int>(wp->altitudeFt()) + 50) / 100;
697         if (curAlt <= 0) {
698           wp->setAltitude(0, RESTRICT_NONE);
699         } else {
700           wp->setAltitude((curAlt - 10) * 100, wp->altitudeRestriction());
701         }
702       }
703     }
704     break;
705     
706   case '=':
707     if (getSelected() >= 0) {
708       flightgear::Waypt* wp = _model->waypointAt(getSelected());
709       if (wp->flag(WPT_GENERATED)) {
710         break;
711       }
712         
713       if (wp->altitudeRestriction() == RESTRICT_NONE) {
714         wp->setAltitude(1000, RESTRICT_AT);
715       } else {
716         int curAlt = (static_cast<int>(wp->altitudeFt()) + 50) / 100;
717         wp->setAltitude((curAlt + 10) * 100, wp->altitudeRestriction());
718       }
719     }
720     break;
721   
722   case 0x7f: // delete
723     if (getSelected() >= 0) {
724       int index = getSelected();
725       Waypt* wp = _model->waypointAt(getSelected());
726       if (wp->flag(WPT_GENERATED)) {
727         break;
728       }
729       
730       _model->deleteAt(index);
731       setSelected(index - 1);
732     }
733     break;
734
735   default :
736     return FALSE;
737   }
738
739   return TRUE ;
740 }
741
742 void WaypointList::modelUpdateCallback()
743 {
744   // local stuff
745   
746   if (_updateCallback) {
747     (*_updateCallback)();
748   }
749 }
750
751 //////////////////////////////////////////////////////////////////////////////
752
753
754 static void handle_scrollbar(puObject* scrollbar)
755 {
756   ScrolledWaypointList* self = (ScrolledWaypointList*)scrollbar->getUserData();
757   self->setScrollPercent(scrollbar->getFloatValue());
758 }
759
760 static void waypointListCb(puObject* wpl)
761 {
762   ScrolledWaypointList* self = (ScrolledWaypointList*)wpl->getUserData();
763   self->setValue(wpl->getIntegerValue());
764   self->invokeCallback();
765 }
766
767 ScrolledWaypointList::ScrolledWaypointList(int x, int y, int width, int height) :
768   puGroup(x,y),
769   _scrollWidth(16)
770 {
771   // ensure our type is compound, so fgPopup::applySize doesn't descend into
772   // us, and try to cast our children's user-data to GUIInfo*.
773   type |= PUCLASS_LIST;
774   
775   init(width, height);
776 }
777
778 void ScrolledWaypointList::setValue(float v)
779 {
780   puGroup::setValue(v);
781   _list->setValue(v);
782 }
783
784 void ScrolledWaypointList::setValue(int v)
785 {
786   puGroup::setValue(v);
787   _list->setValue(v);
788 }
789
790 void ScrolledWaypointList::init(int w, int h)
791 {
792   _list = new WaypointList(0, 0, w, h);
793   _list->setUpdateCallback(make_callback(this, &ScrolledWaypointList::modelUpdated));
794   _hasVScroll = _list->wantsVScroll();
795   _list->setUserData(this);
796   _list->setCallback(waypointListCb);
797   
798   _list->setScrollCallback(make_callback(this, &ScrolledWaypointList::updateScroll));
799   
800   _scrollbar = new puaScrollBar(w - _scrollWidth, 0, h, 
801     1 /*arrow*/, 1 /* vertical */, _scrollWidth);
802   _scrollbar->setMinValue(0.0);
803   _scrollbar->setMaxValue(1.0);
804   _scrollbar->setUserData(this);
805   _scrollbar->setCallback(handle_scrollbar);
806   close(); // close the group
807   
808   setSize(w, h);
809 }
810
811 void ScrolledWaypointList::modelUpdated()
812 {  
813   int w, h;
814   getSize(&w, &h);
815   updateWantsScroll(w, h);
816 }
817
818 void ScrolledWaypointList::setScrollPercent(float v)
819 {
820   // slider's min is the bottom, so invert the value
821   _list->setVScrollPercent(1.0f - v); 
822 }
823
824 void ScrolledWaypointList::setSize(int w, int h)
825 {
826   updateWantsScroll(w, h);
827   puGroup::setSize(w, h);
828 }
829
830 void ScrolledWaypointList::updateWantsScroll(int w, int h)
831 {
832   _hasVScroll = _list->wantsVScroll();
833   
834   if (_hasVScroll) {
835     _scrollbar->reveal();
836     _scrollbar->setPosition(w - _scrollWidth, 0);
837     _scrollbar->setSize(_scrollWidth, h);
838     _list->setSize(w - _scrollWidth, h);
839     updateScroll();
840   } else {
841     _scrollbar->hide();
842     _list->setSize(w, h);
843   }
844 }
845
846 void ScrolledWaypointList::updateScroll()
847 {
848  // _scrollbar->setMaxValue(_list->numWaypoints());
849   _scrollbar->setValue(1.0f - _list->getVScrollPercent());
850   _scrollbar->setSliderFraction(_list->getVScrollThumbPercent());
851 }
852