]> Shamusworld >> Repos - apple2/blob - src/gui/element.cpp
Added floppy #2 saving, statistics to makefile.
[apple2] / src / gui / element.cpp
1 //
2 // ELEMENT.CPP
3 //
4 // Graphical User Interface base class
5 // by James Hammons
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  02/02/2006  Created this file
12 // JLH  02/13/2006  Added backbuffer and rendering functions
13 // JLH  03/02/2006  Moved backbuffer destruction to destructor, added parent
14 //                  corner discovery
15 //
16
17 #include "element.h"
18 #include "guimisc.h"                                                            // Various support functions
19
20 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
21 #define MASK_R 0xFF000000
22 #define MASK_G 0x00FF0000
23 #define MASK_B 0x0000FF00
24 #define MASK_A 0x000000FF
25 #else
26 #define MASK_R 0x000000FF
27 #define MASK_G 0x0000FF00
28 #define MASK_B 0x00FF0000
29 #define MASK_A 0xFF000000
30 #endif
31
32 //#define DEBUG_ELEMENT
33
34 #ifdef DEBUG_ELEMENT
35 #include "log.h"
36 #endif
37
38 // Initialize class variables
39
40 SDL_Surface * Element::screen = NULL;
41 bool Element::needToRefreshScreen = false;
42
43 Element::Element(uint32_t x/*= 0*/, uint32_t y/*= 0*/, uint32_t w/*= 0*/, uint32_t h/*= 0*/,
44         Element * parentElement/*= NULL*/):     parent(parentElement), backstore(NULL), visible(true)
45 {
46         extents.x = x,
47         extents.y = y,
48         extents.w = w,
49         extents.h = h;
50         coverList.push_back(extents);
51 }
52
53 Element::Element(uint32_t x, uint32_t y, uint32_t w, uint32_t h,
54         uint8_t fgR/*= 0xFF*/, uint8_t fgG/*= 0xFF*/, uint8_t fgB/*= 0xFF*/, uint8_t fgA/*= 0xFF*/,
55         uint8_t bgR/*= 0x00*/, uint8_t bgG/*= 0x00*/, uint8_t bgB/*= 0x00*/, uint8_t bgA/*= 0xFF*/,
56         Element * parentElement/*= NULL*/): parent(parentElement), backstore(NULL), visible(true)
57 {
58         extents.x = x,
59         extents.y = y,
60         extents.w = w,
61         extents.h = h;
62         coverList.push_back(extents);
63
64 #if 0
65         // This *should* allow us to store our colors in an endian safe way... :-/
66         uint8_t * c = (uint8_t *)&fgColor;
67         c[0] = fgR, c[1] = fgG, c[2] = fgB, c[3] = fgA;
68         c = (uint8_t *)&bgColor;
69         c[0] = bgR, c[1] = bgG, c[2] = bgB, c[3] = bgA;
70 #else
71         fgColor = SDL_MapRGBA(screen->format, fgR, fgG, fgB, fgA);
72         bgColor = SDL_MapRGBA(screen->format, bgR, bgG, bgB, bgA);
73 #endif
74 }
75
76 Element::~Element()
77 {
78         if (backstore)
79         {
80                 RestoreScreenFromBackstore();
81                 SDL_FreeSurface(backstore);
82                 needToRefreshScreen = true;
83         }
84 }
85
86 bool Element::Inside(uint32_t x, uint32_t y)
87 {
88         return (x >= (uint32_t)extents.x && x < (uint32_t)(extents.x + extents.w)
89                 && y >= (uint32_t)extents.y && y < (uint32_t)(extents.y + extents.h) ? true : false);
90 }
91
92 //Badly named--!!! FIX !!! [DONE]
93 //SDL_Rect Element::GetParentCorner(void)
94 SDL_Rect Element::GetScreenCoords(void)
95 {
96         SDL_Rect rect;
97         rect.x = extents.x, rect.y = extents.y;
98
99         // First, traverse the parent tree to get the absolute screen address...
100
101         Element * currentParent = parent;
102
103         while (currentParent)
104         {
105                 rect.x += currentParent->extents.x;
106                 rect.y += currentParent->extents.y;
107                 currentParent = currentParent->parent;
108         }
109
110         return rect;
111 }
112
113 #if 1
114 //May use this in the future...
115 SDL_Rect Element::GetParentRect(void)
116 {
117         // If there is no parent, then return the entire screen as the parent's
118         // rectangle.
119
120         SDL_Rect rect;
121         rect.x = 0, rect.y = 0, rect.w = screen->w, rect.h = screen->h;
122
123         if (parent)
124         {
125                 rect.x = parent->extents.x;
126                 rect.y = parent->extents.y;
127                 rect.w = parent->extents.w;
128                 rect.h = parent->extents.h;
129         }
130
131         return rect;
132 }
133 #endif
134
135 SDL_Rect Element::GetExtents(void)
136 {
137         return extents;
138 }
139
140 //kludge
141 #include "settings.h"
142 void Element::CreateBackstore(void)
143 {
144         backstore = SDL_CreateRGBSurface(SDL_SWSURFACE, extents.w, extents.h, 32,
145                 MASK_R, MASK_G, MASK_B, 0x00);
146 //#define TEST_GL
147 #ifdef TEST_GL
148 printf("Element: About to do SDL_BlitSurface...\n");
149 #endif
150 //kludge
151 if (settings.useOpenGL)
152         return;
153
154 //Since screen is the main screen surface, OpenGL doesn't like it being touched.
155 //How to fix? Dunno.
156         SDL_BlitSurface(screen, &extents, backstore, NULL);
157 #ifdef TEST_GL
158 printf("Element: SDL_BlitSurface...Done.\n");
159 #endif
160 }
161
162 void Element::RestoreScreenFromBackstore(void)
163 {
164         SDL_Rect r;
165
166         r.x = extents.x;
167         r.y = extents.y;
168         SDL_BlitSurface(backstore, NULL, screen, &r);
169 }
170
171 void Element::SaveScreenToBackstore(void)
172 {
173         SDL_BlitSurface(screen, &extents, backstore, NULL);
174 }
175
176 void Element::ResetCoverageList(void)
177 {
178         // Setup our coverage list with the entire window area
179         coverList.empty();
180         coverList.push_back(extents);
181 }
182
183 void Element::AdjustCoverageList(SDL_Rect r)
184 {
185 //Prolly should have a bool here to set whether or not to do this crap, since it
186 //takes a little time...
187
188         // Here's where we do the coverage list voodoo... :-)
189
190 /*
191 Steps:
192   o Check for intersection. If no intersection, then no need to divide rects.
193   o Loop through current rects. If rect is completely inside passed in rect, remove from list.
194   o Loop through remaining rects. If rect intersects, decompose to four rects and
195     exclude degenerate rects, push rest into the coverage list.
196
197 */
198 //      std::list<Element *>::reverse_iterator ri;
199 //      std::list<SDL_Rect>::iterator i;
200
201         // Loop through rects and remove those completely covered by passed in rect.
202 /*      for(i=coverList.begin(); i!=coverList.end(); i++)
203         {
204 //              if (RectanglesIntersect(r, *i))
205                 if (RectangleFirstInsideSecond(*i, r))
206                 {
207 //This is not right--do a while loop instead of a for loop?
208                         // Remove it from the list...
209                         std::list<SDL_Rect>::iterator next = coverList.erase(i);
210                 }
211         }
212 */
213         // Loop through rects and remove those completely covered by passed in rect.
214         std::list<SDL_Rect>::iterator i = coverList.begin();
215
216         while (i != coverList.end())
217         {
218                 if (RectangleFirstInsideSecond(*i, r))
219                         i = coverList.erase(i);                         // This will also advance i to the next item!
220                 else
221                         i++;
222         }
223
224 //This may not be needed if nothing follows the loop below...!
225 //      if (coverList.empty())
226 //              return;
227
228         // Check for intersection. If no intersection, then no need to divide rects.
229         i = coverList.begin();
230
231         while (i != coverList.end())
232         {
233                 if (RectanglesIntersect(r, *i))
234                 {
235                         // Do the decomposition here. There will always be at least *one* rectangle
236                         // generated by this algorithm, so we know we're OK in removing the original
237                         // from the list. The general pattern looks like this:
238                         //
239                         // +------+
240                         // |1     |
241                         // +-+--+-+
242                         // |2|//|3|  <- Rectangle "r" is in the center
243                         // +-+--+-+
244                         // |4     |
245                         // +------+
246                         //
247                         // Even if r extends beyond the bounds of the rectangle under consideration,
248                         // that's OK because we test to see that the rectangle isn't degenerate
249                         // before adding it to the list.
250
251 //Should probably use a separate list here and splice it in when we're done here...
252 //Or, could use push_front() to avoid the problem... Neat! Doesn't require a separate list!
253 //But, we need to remove the currently referenced rect... Another while loop!
254
255 //This approach won't work--if no rect1 then we're screwed! [FIXED]
256 //Now *that* will work...
257                         SDL_Rect current = *i;
258                         uint32_t bottomOfRect1 = current.y;
259 //                      uint32_t rightOfRect2 = current.x;
260 //                      uint32_t leftOfRect3 = current.x + current.w;
261                         uint32_t topOfRect4 = current.y + current.h;
262
263                         // Rectangle #1 (top)
264                         if (r.y > current.y)                            // Simple rectangle degeneracy test...
265                         {
266                                 bottomOfRect1 = r.y;
267                                 SDL_Rect rect = current;
268                                 rect.h = r.y - current.y;
269                                 coverList.push_front(rect);
270                         }
271
272                         // Rectangle #4 (bottom)
273                         if (r.y + r.h < current.y + current.h)
274                         {
275                                 topOfRect4 = r.y + r.h;
276                                 SDL_Rect rect = current;
277                                 rect.y = r.y + r.h;
278                                 rect.h = (current.y + current.h) - (r.y + r.h);
279                                 coverList.push_front(rect);
280                         }
281
282                         // Rectangle #2 (left side)
283                         if (r.x > current.x)
284                         {
285                                 SDL_Rect rect = current;
286                                 rect.w = r.x - current.x;
287                                 rect.y = bottomOfRect1;
288                                 rect.h = topOfRect4 - bottomOfRect1;
289                                 coverList.push_front(rect);
290                         }
291
292                         // Rectangle #3 (right side)
293                         if (r.x + r.w < current.x + current.w)
294                         {
295                                 SDL_Rect rect;
296                                 rect.x = r.x + r.w;
297                                 rect.w = (current.x + current.w) - (r.x + r.w);
298                                 rect.y = bottomOfRect1;
299                                 rect.h = topOfRect4 - bottomOfRect1;
300                                 coverList.push_front(rect);
301                         }
302
303                         i = coverList.erase(i);                         // This will also advance i to the next item!
304                 }
305                 else
306                         i++;
307         }
308 }
309
310 void Element::SetVisible(bool visibility)
311 {
312         visible = visibility;
313 }
314
315 //
316 // Class methods
317 //
318
319 void Element::SetScreen(SDL_Surface * s)
320 {
321         screen = s;
322 }
323
324 bool Element::ScreenNeedsRefreshing(void)
325 {
326         return needToRefreshScreen;
327 }
328
329 void Element::ScreenWasRefreshed(void)
330 {
331         needToRefreshScreen = false;
332 }