1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //    * Redistributions of source code must retain the above copyright notice,
11 //      this list of conditions and the following disclaimer.
12 //
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the following disclaimer in the
15 //      documentation and/or other materials provided with the distribution.
16 //
17 //    * Neither the name of the author nor the names of other contributors may
18 //      be used to endorse or promote products derived from this software
19 //      without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34 
35 #include <stdafx.h>
36 #include <cursespp/ScrollAdapterBase.h>
37 #include <cursespp/ScrollableWindow.h>
38 #include <cursespp/MultiLineEntry.h>
39 #include <cursespp/ListWindow.h>
40 
41 using namespace cursespp;
42 
43 typedef IScrollAdapter::EntryPtr EntryPtr;
44 
ScrollAdapterBase()45 ScrollAdapterBase::ScrollAdapterBase() {
46     this->height = 0;
47     this->width = 0;
48 }
49 
~ScrollAdapterBase()50 ScrollAdapterBase::~ScrollAdapterBase() {
51 
52 }
53 
SetDisplaySize(size_t width,size_t height)54 void ScrollAdapterBase::SetDisplaySize(size_t width, size_t height) {
55     this->width = width;
56     this->height = height;
57 }
58 
GetLineCount()59 size_t ScrollAdapterBase::GetLineCount() {
60     return -1;
61 }
62 
GetVisibleItems(cursespp::ScrollableWindow * window,size_t desiredTopIndex,std::deque<EntryPtr> & target)63 size_t ScrollAdapterBase::GetVisibleItems(
64     cursespp::ScrollableWindow* window,
65     size_t desiredTopIndex,
66     std::deque<EntryPtr>& target)
67 {
68     size_t actualTopIndex = desiredTopIndex;
69 
70     /* ensure we have enough data to draw from the specified position
71     to the end. if we don't try to back up a bit until we can fill
72     the buffer */
73     int totalHeight = (int) this->height;
74     int entryCount = (int) this->GetEntryCount();
75 
76     /* we assume the general case -- we're some where in the middle of the
77     list. we'll start from the specified first item and work our way down */
78     for (int i = (int) desiredTopIndex; i < entryCount && totalHeight > 0; i++) {
79         EntryPtr entry = this->GetEntry(window, i);
80         entry->SetWidth(this->width);
81         totalHeight -= entry->GetLineCount();
82         target.push_back(entry);
83     }
84 
85     /* however, if the list is short, we can actually draw more items above
86     the specified one. let's work our way backwards! */
87     if (totalHeight > 0) {
88         target.clear();
89 
90         totalHeight = this->height;
91         int i = GetEntryCount() - 1;
92         while (i >= 0 && totalHeight >= 0) {
93             EntryPtr entry = this->GetEntry(window, i);
94             entry->SetWidth(this->width);
95 
96             int lines = entry->GetLineCount();
97             if (lines > totalHeight) {
98                 break; /* this Entry won't fit. bail. */
99             }
100 
101             totalHeight -= lines;
102             target.push_front(entry);
103             --i;
104         }
105 
106         actualTopIndex = i + 1;
107     }
108 
109     return actualTopIndex;
110 }
111 
DrawPage(ScrollableWindow * scrollable,size_t index,ScrollPosition & result)112 void ScrollAdapterBase::DrawPage(ScrollableWindow* scrollable, size_t index, ScrollPosition& result) {
113     WINDOW* window = scrollable->GetContent();
114 
115     if (!scrollable->IsVisible() || !window || this->height == 0 || this->width == 0) {
116         return;
117     }
118 
119     werase(window);
120 
121     std::deque<EntryPtr> visible;
122     size_t drawnLines = 0;
123     size_t topIndex = 0;
124     size_t count = GetEntryCount();
125 
126     if (count > 0) {
127         if (index >= count) {
128             index = count - 1;
129         }
130 
131         topIndex = GetVisibleItems(scrollable, index, visible);
132 
133         for (size_t e = 0; e < visible.size(); e++) {
134             EntryPtr entry = visible.at(e);
135             size_t count = entry->GetLineCount();
136 
137             for (size_t i = 0; i < count && drawnLines < this->height; i++) {
138                 Color attrs = Color::Default;
139 
140                 if (this->decorator) {
141                     attrs = this->decorator(scrollable, topIndex + e, i, entry);
142                 }
143 
144                 if (attrs == -1) {
145                     attrs = entry->GetAttrs(i);
146                 }
147 
148                 if (attrs != -1) {
149                     wattron(window, attrs);
150                 }
151 
152                 std::string line = entry->GetLine(i);
153                 size_t len = u8cols(line);
154 
155                 /* pad with empty spaces to the end of the line. this allows us to
156                 do highlight rows. this should probably be configurable. */
157 
158                 int remain = this->width - len;
159                 if (remain > 0) {
160                     line += std::string(remain, ' ');
161                 }
162 
163                 /* string is padded above, we don't need a \n */
164 
165                 checked_wprintw(window, "%s", line.c_str());
166 
167                 if (attrs != -1) {
168                     wattroff(window, attrs);
169                 }
170 
171                 ++drawnLines;
172             }
173         }
174     }
175 
176     result.visibleEntryCount = visible.size();
177     result.firstVisibleEntryIndex = topIndex;
178     result.lineCount = drawnLines;
179     result.totalEntries = count;
180 }
181