1 /* GG is a GUI for OpenGL.
2 Copyright (C) 2003-2008 T. Zachary Laine
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public License
6 as published by the Free Software Foundation; either version 2.1
7 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA
18
19 If you do not wish to comply with the terms of the LGPL please
20 contact the author as other terms are available for a fee.
21
22 Zach Laine
23 whatwasthataddress@gmail.com */
24
25 #include <GG/Scroll.h>
26
27 #include <GG/Button.h>
28 #include <GG/DrawUtil.h>
29 #include <GG/StyleFactory.h>
30 #include <GG/WndEvent.h>
31
32
33 using namespace GG;
34
35 namespace {
36 struct ScrolledEcho
37 {
ScrolledEcho__anon6e49d1180111::ScrolledEcho38 ScrolledEcho(const std::string& name) : m_name(name) {}
operator ()__anon6e49d1180111::ScrolledEcho39 void operator()(int tab_min, int tab_max, int scroll_min, int scroll_max)
40 {
41 std::cerr << "GG SIGNAL : " << m_name
42 << "(tab_min=" << tab_min << " tab_max=" << tab_max
43 << " scroll_min=" << scroll_min << " scroll_max=" << scroll_max
44 << ")\n";
45 }
46 std::string m_name;
47 };
48
49 const unsigned int MIN_TAB_SIZE = 5;
50 }
51
52 ////////////////////////////////////////////////
53 // GG::Scroll
54 ////////////////////////////////////////////////
Scroll(Orientation orientation,Clr color,Clr interior)55 Scroll::Scroll(Orientation orientation, Clr color, Clr interior) :
56 Control(X0, Y0, X1, Y1, INTERACTIVE | REPEAT_BUTTON_DOWN),
57 m_int_color(interior),
58 m_orientation(orientation),
59 m_posn(0),
60 m_range_min(0),
61 m_range_max(99),
62 m_line_sz(5),
63 m_page_sz(25),
64 m_initial_depressed_region(SBR_NONE),
65 m_depressed_region(SBR_NONE)
66 {
67 Control::SetColor(color);
68 const auto& style = GetStyleFactory();
69 if (m_orientation == VERTICAL) {
70 m_decr = style->NewScrollUpButton(color);
71 m_incr = style->NewScrollDownButton(color);
72 m_tab = style->NewVScrollTabButton(color);
73 } else {
74 m_decr = style->NewScrollLeftButton(color);
75 m_incr = style->NewScrollRightButton(color);
76 m_tab = style->NewHScrollTabButton(color);
77 }
78 }
79
CompleteConstruction()80 void Scroll::CompleteConstruction()
81 {
82 if (m_decr) {
83 AttachChild(m_decr);
84 m_decr->LeftClickedSignal.connect(boost::bind(&Scroll::ScrollLineIncrDecrImpl, this, true, -1));
85 }
86 if (m_incr) {
87 AttachChild(m_incr);
88 m_incr->LeftClickedSignal.connect(boost::bind(&Scroll::ScrollLineIncrDecrImpl, this, true, 1));
89 }
90 AttachChild(m_tab);
91 m_tab->InstallEventFilter(shared_from_this());
92
93 if (INSTRUMENT_ALL_SIGNALS) {
94 ScrolledSignal.connect(ScrolledEcho("Scroll::ScrolledSignal"));
95 ScrolledAndStoppedSignal.connect(ScrolledEcho("Scroll::ScrolledAndStoppedSignal"));
96 }
97
98 DoLayout();
99 InitBuffer();
100 }
101
MinUsableSize() const102 Pt Scroll::MinUsableSize() const
103 {
104 Pt retval;
105 const int MIN_DRAGABLE_SIZE = 2;
106 if (m_orientation == VERTICAL) {
107 retval.x = X(MIN_DRAGABLE_SIZE);
108 Y decr_y = m_decr ? m_decr->MinUsableSize().y : Y0;
109 Y incr_y = m_incr ? m_incr->MinUsableSize().y : Y0;
110 retval.y = decr_y + incr_y + 3 * std::min(decr_y, incr_y);
111 } else {
112 X decr_x = m_decr ? m_decr->MinUsableSize().x : X0;
113 X incr_x = m_incr ? m_incr->MinUsableSize().x : X0;
114 retval.x = decr_x + incr_x + 3 * std::min(decr_x, incr_x);
115 retval.y = Y(MIN_DRAGABLE_SIZE);
116 }
117 return retval;
118 }
119
PosnRange() const120 std::pair<int, int> Scroll::PosnRange() const
121 { return std::pair<int, int>(m_posn, m_posn + m_page_sz); }
122
ScrollRange() const123 std::pair<int, int> Scroll::ScrollRange() const
124 { return std::pair<int, int>(m_range_min, m_range_max); }
125
LineSize() const126 unsigned int Scroll::LineSize() const
127 { return m_line_sz; }
128
PageSize() const129 unsigned int Scroll::PageSize() const
130 { return m_page_sz; }
131
InteriorColor() const132 Clr Scroll::InteriorColor() const
133 { return m_int_color; }
134
ScrollOrientation() const135 Orientation Scroll::ScrollOrientation() const
136 { return m_orientation; }
137
InitBuffer()138 void Scroll::InitBuffer()
139 {
140 GG::Pt sz = Size();
141 m_buffer.clear();
142 m_buffer.store(0.0f, 0.0f);
143 m_buffer.store(Value(sz.x), 0.0f);
144 m_buffer.store(Value(sz.x), Value(sz.y));
145 m_buffer.store(0.0f, Value(sz.y));
146 m_buffer.createServerBuffer();
147 }
148
Render()149 void Scroll::Render()
150 {
151 Pt ul = UpperLeft();
152
153 glPushMatrix();
154 glLoadIdentity();
155 glTranslatef(static_cast<GLfloat>(Value(ul.x)), static_cast<GLfloat>(Value(ul.y)), 0.0f);
156 glDisable(GL_TEXTURE_2D);
157 glLineWidth(2.0);
158 glEnableClientState(GL_VERTEX_ARRAY);
159
160 m_buffer.activate();
161 glColor(Disabled() ? DisabledColor(m_int_color) : m_int_color);
162 glDrawArrays(GL_TRIANGLE_FAN, 0, m_buffer.size());
163
164 glLineWidth(1.0f);
165 glEnable(GL_TEXTURE_2D);
166 glPopMatrix();
167 glDisableClientState(GL_VERTEX_ARRAY);
168 }
169
SizeMove(const Pt & ul,const Pt & lr)170 void Scroll::SizeMove(const Pt& ul, const Pt& lr)
171 {
172 Pt old_size = Size();
173
174 Wnd::SizeMove(ul, lr);
175
176 if (old_size != Size()) {
177 DoLayout();
178 InitBuffer();
179 }
180 }
181
DoLayout()182 void Scroll::DoLayout()
183 {
184 int bn_width = (m_orientation == VERTICAL) ? Value(Size().x) : Value(Size().y);
185 if(m_decr)
186 m_decr->SizeMove(Pt(), Pt(X(bn_width), Y(bn_width)));
187 if(m_incr)
188 m_incr->SizeMove(Size() - Pt(X(bn_width), Y(bn_width)), Size());
189 m_tab->SizeMove(m_tab->RelativeUpperLeft(),
190 (m_orientation == VERTICAL) ?
191 Pt(X(bn_width), m_tab->RelativeLowerRight().y) :
192 Pt(m_tab->RelativeLowerRight().x, Y(bn_width)));
193 SizeScroll(m_range_min, m_range_max, m_line_sz, m_page_sz); // update tab size and position
194 }
195
Disable(bool b)196 void Scroll::Disable(bool b/* = true*/)
197 {
198 Control::Disable(b);
199 m_tab->Disable(b);
200 if(m_incr)
201 m_incr->Disable(b);
202 if(m_decr)
203 m_decr->Disable(b);
204 }
205
SetColor(Clr c)206 void Scroll::SetColor(Clr c)
207 {
208 Control::SetColor(c);
209 m_tab->SetColor(c);
210 if(m_incr)
211 m_incr->SetColor(c);
212 if(m_decr)
213 m_decr->SetColor(c);
214 }
215
SetInteriorColor(Clr c)216 void Scroll::SetInteriorColor(Clr c)
217 { m_int_color = c; }
218
SizeScroll(int min,int max,unsigned int line,unsigned int page)219 void Scroll::SizeScroll(int min, int max, unsigned int line, unsigned int page)
220 {
221 m_line_sz = line;
222 m_range_min = std::min(min, max);
223 m_range_max = std::max(min, max);
224 m_page_sz = page;
225
226 if (m_page_sz > static_cast<unsigned int>(m_range_max - m_range_min + 1))
227 m_page_sz = (m_range_max - m_range_min + 1);
228 if (m_posn > m_range_max - static_cast<int>(m_page_sz - 1))
229 m_posn = m_range_max - (m_page_sz - 1);
230 if (m_posn < m_range_min)
231 m_posn = m_range_min;
232 Pt tab_ul = m_tab->RelativeUpperLeft();
233 Pt tab_lr = m_orientation == VERTICAL ?
234 Pt(m_tab->RelativeLowerRight().x, tab_ul.y + static_cast<int>(TabWidth())):
235 Pt(tab_ul.x + static_cast<int>(TabWidth()), m_tab->RelativeLowerRight().y);
236 m_tab->SizeMove(tab_ul, tab_lr);
237 MoveTabToPosn();
238 }
239
SetMax(int max)240 void Scroll::SetMax(int max)
241 { SizeScroll(m_range_min, max, m_line_sz, m_page_sz); }
242
SetMin(int min)243 void Scroll::SetMin(int min)
244 { SizeScroll(min, m_range_max, m_line_sz, m_page_sz); }
245
SetLineSize(unsigned int line)246 void Scroll::SetLineSize(unsigned int line)
247 { SizeScroll(m_range_min, m_range_max, line, m_page_sz); }
248
SetPageSize(unsigned int page)249 void Scroll::SetPageSize(unsigned int page)
250 { SizeScroll(m_range_min, m_range_max, m_line_sz, page); }
251
ScrollTo(int p)252 void Scroll::ScrollTo(int p)
253 {
254 if (p < m_range_min)
255 m_posn = m_range_min;
256 else if (p > static_cast<int>(m_range_max - m_page_sz))
257 m_posn = m_range_max - m_page_sz;
258 else
259 m_posn = p;
260 MoveTabToPosn();
261 }
262
ScrollLineIncr(int lines)263 void Scroll::ScrollLineIncr(int lines)
264 { ScrollLineIncrDecrImpl(false, lines); }
265
ScrollLineDecr(int lines)266 void Scroll::ScrollLineDecr(int lines)
267 { ScrollLineIncrDecrImpl(false, -lines); }
268
ScrollPageIncr()269 void Scroll::ScrollPageIncr()
270 {
271 if (static_cast<int>(m_posn + m_page_sz) <= static_cast<int>(m_range_max - m_page_sz))
272 m_posn += m_page_sz;
273 else
274 m_posn = m_range_max - (m_page_sz - 1);
275 MoveTabToPosn();
276 }
277
ScrollPageDecr()278 void Scroll::ScrollPageDecr()
279 {
280 if (static_cast<int>(m_posn - m_page_sz) >= m_range_min)
281 m_posn -= m_page_sz;
282 else
283 m_posn = m_range_min;
284 MoveTabToPosn();
285 }
286
TabSpace() const287 unsigned int Scroll::TabSpace() const
288 {
289 // tab_space is the space the tab has to move about in (the control's width less the width of the incr & decr buttons)
290 return (m_orientation == VERTICAL ?
291 Value(Size().y - (m_incr ? m_incr->Size().y : Y0) - (m_decr ? m_decr->Size().y : Y0)) :
292 Value(Size().x - (m_incr ? m_incr->Size().x : X0) - (m_decr ? m_decr->Size().x : X0)));
293 }
294
TabWidth() const295 unsigned int Scroll::TabWidth() const
296 { return std::max(static_cast<unsigned int>(TabSpace() / (m_range_max - m_range_min + 1.0) * m_page_sz + 0.5), MIN_TAB_SIZE); }
297
RegionUnder(const Pt & pt)298 Scroll::ScrollRegion Scroll::RegionUnder(const Pt& pt)
299 {
300 ScrollRegion retval;
301 Pt ul = ClientUpperLeft();
302 if (pt.x - ul.x < m_tab->RelativeUpperLeft().x || pt.y - ul.y <= m_tab->RelativeUpperLeft().y)
303 retval = SBR_PAGE_DN;
304 else
305 retval = SBR_PAGE_UP;
306 return retval;
307 }
308
TabButton() const309 Button* Scroll::TabButton() const
310 { return m_tab.get(); }
311
IncrButton() const312 Button* Scroll::IncrButton() const
313 { return m_incr.get(); }
314
DecrButton() const315 Button* Scroll::DecrButton() const
316 { return m_decr.get(); }
317
LButtonDown(const Pt & pt,Flags<ModKey> mod_keys)318 void Scroll::LButtonDown(const Pt& pt, Flags<ModKey> mod_keys)
319 {
320 if (!Disabled()) {
321 // when a button is pressed, record the region of the control the cursor is over
322 ScrollRegion region = RegionUnder(pt);
323 if (m_initial_depressed_region == SBR_NONE)
324 m_initial_depressed_region = region;
325 m_depressed_region = region;
326 if (m_depressed_region == m_initial_depressed_region) {
327 switch (m_depressed_region)
328 {
329 case SBR_PAGE_DN: {
330 int old_posn = m_posn;
331 ScrollPageDecr();
332 if (old_posn != m_posn) {
333 ScrolledSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
334 ScrolledAndStoppedSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
335 }
336 break;
337 }
338 case SBR_PAGE_UP: {
339 int old_posn = m_posn;
340 ScrollPageIncr();
341 if (old_posn != m_posn) {
342 ScrolledSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
343 ScrolledAndStoppedSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
344 }
345 break;
346 }
347 default: break;
348 }
349 }
350 }
351 }
352
LButtonUp(const Pt & pt,Flags<ModKey> mod_keys)353 void Scroll::LButtonUp(const Pt& pt, Flags<ModKey> mod_keys)
354 {
355 if (!Disabled()) {
356 if(m_decr)
357 m_decr->SetState(Button::BN_UNPRESSED);
358 if(m_incr)
359 m_incr->SetState(Button::BN_UNPRESSED);
360 m_initial_depressed_region = SBR_NONE;
361 m_depressed_region = SBR_NONE;
362 }
363 }
364
LClick(const Pt & pt,Flags<ModKey> mod_keys)365 void Scroll::LClick(const Pt& pt, Flags<ModKey> mod_keys)
366 { LButtonUp(pt, mod_keys); }
367
MouseHere(const Pt & pt,Flags<ModKey> mod_keys)368 void Scroll::MouseHere(const Pt& pt, Flags<ModKey> mod_keys)
369 { LButtonUp(pt, mod_keys); }
370
EventFilter(Wnd * w,const WndEvent & event)371 bool Scroll::EventFilter(Wnd* w, const WndEvent& event)
372 {
373 if (w == m_tab.get()) {
374 switch (event.Type()) {
375 case WndEvent::LDrag: {
376 if (!Disabled()) {
377 Pt new_ul = m_tab->RelativeUpperLeft() + event.DragMove();
378 if (m_orientation == VERTICAL) {
379 new_ul.x = m_tab->RelativeUpperLeft().x;
380 new_ul.y = std::max(0 + (m_decr ? m_decr->Height() : Y0),
381 std::min(new_ul.y, ClientHeight() - (m_incr ? m_incr->Height() : Y0) - m_tab->Height()));
382 m_tab_dragged |= bool(m_tab->RelativeUpperLeft().y - new_ul.y);
383 } else {
384 new_ul.x = std::max(0 + (m_decr ? m_decr->Width() : X0),
385 std::min(new_ul.x, ClientWidth() - (m_incr ? m_incr->Width() : X0) - m_tab->Width()));
386 new_ul.y = m_tab->RelativeUpperLeft().y;
387 m_tab_dragged |= bool(m_tab->RelativeUpperLeft().x - new_ul.x);
388 }
389 m_tab->MoveTo(new_ul);
390 UpdatePosn();
391 }
392 return true;
393 }
394 case WndEvent::LButtonDown:
395 m_dragging_tab = true;
396 break;
397 case WndEvent::LButtonUp:
398 case WndEvent::LClick:
399 if (m_tab_dragged)
400 ScrolledAndStoppedSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
401 m_dragging_tab = false;
402 m_tab_dragged = false;
403 break;
404 case WndEvent::MouseLeave:
405 return m_dragging_tab;
406 default:
407 break;
408 }
409 }
410 return false;
411 }
412
UpdatePosn()413 void Scroll::UpdatePosn()
414 {
415 int old_posn = m_posn;
416 int before_tab = (m_orientation == VERTICAL ? // the tabspace before the tab's lower-value side
417 Value(m_tab->RelativeUpperLeft().y - (m_decr ? m_decr->Size().y : Y0)) :
418 Value(m_tab->RelativeUpperLeft().x - (m_decr ? m_decr->Size().x : X0)));
419 int tab_space = TabSpace() - (m_orientation == VERTICAL ? Value(m_tab->Size().y) : Value(m_tab->Size().x));
420 int max_posn = static_cast<int>(m_range_max - m_page_sz + 1);
421 m_posn = static_cast<int>(m_range_min + static_cast<double>(before_tab) / tab_space * (max_posn - m_range_min) + 0.5);
422 m_posn = std::max(m_range_min, std::min(m_posn, max_posn));
423 if (old_posn != m_posn)
424 ScrolledSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
425 }
426
MoveTabToPosn()427 void Scroll::MoveTabToPosn()
428 {
429 int start_tabspace = 0; // the tab's lowest posible extent
430 if (m_decr)
431 start_tabspace = (m_orientation == VERTICAL ?
432 Value(m_decr->Size().y) :
433 Value(m_decr->Size().x));
434 int tab_space = TabSpace() - (m_orientation == VERTICAL ? Value(m_tab->Size().y) : Value(m_tab->Size().x));
435 int max_posn = static_cast<int>(m_range_max - m_page_sz + 1);
436 double tab_location =
437 (m_posn - m_range_min) / static_cast<double>(max_posn - m_range_min) * tab_space + start_tabspace + 0.5;
438 if (m_decr && m_posn - m_range_min == 0)
439 tab_location = m_orientation == VERTICAL ? Value(m_decr->Height()) : Value(m_decr->Width());
440
441 m_tab->MoveTo(m_orientation == VERTICAL ?
442 Pt(m_tab->RelativeUpperLeft().x, Y(static_cast<int>(tab_location))) :
443 Pt(X(static_cast<int>(tab_location)), m_tab->RelativeUpperLeft().y));
444 }
445
ScrollLineIncrDecrImpl(bool signal,int lines)446 void Scroll::ScrollLineIncrDecrImpl(bool signal, int lines)
447 {
448 int old_posn = m_posn;
449 int move = lines * m_line_sz;
450
451 if (move == 0) {
452 return;
453 } else if (move > 0) {
454 if (static_cast<int>(m_posn + move) <= static_cast<int>(m_range_max - m_page_sz))
455 m_posn += move;
456 else
457 m_posn = m_range_max - m_page_sz;
458 } else {
459 if (static_cast<int>(m_posn + move) >= m_range_min)
460 m_posn += move;
461 else
462 m_posn = m_range_min;
463 }
464
465 MoveTabToPosn();
466 if (signal && old_posn != m_posn) {
467 ScrolledSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
468 ScrolledAndStoppedSignal(m_posn, m_posn + m_page_sz, m_range_min, m_range_max);
469 }
470 }
471
472
473 ////////////////////////////////////////////////
474 // free functions
475 ////////////////////////////////////////////////
SignalScroll(const Scroll & scroll,bool stopped)476 void GG::SignalScroll(const Scroll& scroll, bool stopped)
477 {
478 std::pair<int, int> pr = scroll.PosnRange();
479 std::pair<int, int> sr = scroll.ScrollRange();
480 scroll.ScrolledSignal(pr.first, pr.second, sr.first, sr.second);
481 if (stopped)
482 scroll.ScrolledAndStoppedSignal(pr.first, pr.second, sr.first, sr.second);
483 }
484