]> Shamusworld >> Repos - apple2/blob - src/gui/element.cpp
c9401cf8ecd7c5a24972395426052bb04c626ba1
[apple2] / src / gui / element.cpp
1 //
2 // ELEMENT.CPP
3 //
4 // Graphical User Interface base class
5 // by James L. Hammons
6 //
7 // JLH = James L. 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 x/*= 0*/, uint32 y/*= 0*/, uint32 w/*= 0*/, uint32 h/*= 0*/,
44         Element * parentElement/*= NULL*/):     parent(parentElement), backstore(NULL)
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 x, uint32 y, uint32 w, uint32 h,
54         uint8 fgR/*= 0xFF*/, uint8 fgG/*= 0xFF*/, uint8 fgB/*= 0xFF*/, uint8 fgA/*= 0xFF*/,
55         uint8 bgR/*= 0x00*/, uint8 bgG/*= 0x00*/, uint8 bgB/*= 0x00*/, uint8 bgA/*= 0xFF*/,
56         Element * parentElement/*= NULL*/): parent(parentElement), backstore(NULL)
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 * c = (uint8 *)&fgColor;
67         c[0] = fgR, c[1] = fgG, c[2] = fgB, c[3] = fgA;
68         c = (uint8 *)&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 x, uint32 y)
87 {
88         return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
89                 && y >= (uint32)extents.y && y < (uint32)(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 void Element::CreateBackstore(void)
141 {
142         backstore = SDL_CreateRGBSurface(SDL_SWSURFACE, extents.w, extents.h, 32,
143                 MASK_R, MASK_G, MASK_B, 0x00);
144         SDL_BlitSurface(screen, &extents, backstore, NULL);
145 }
146
147 void Element::RestoreScreenFromBackstore(void)
148 {
149         SDL_Rect r;
150
151         r.x = extents.x;
152         r.y = extents.y;
153         SDL_BlitSurface(backstore, NULL, screen, &r);
154 }
155
156 void Element::SaveScreenToBackstore(void)
157 {
158         SDL_BlitSurface(screen, &extents, backstore, NULL);
159 }
160
161 void Element::ResetCoverageList(void)
162 {
163         // Setup our coverage list with the entire window area
164         coverList.empty();
165         coverList.push_back(extents);
166 }
167
168 void Element::AdjustCoverageList(SDL_Rect r)
169 {
170 //Prolly should have a bool here to set whether or not to do this crap, since it
171 //takes a little time...
172
173         // Here's where we do the coverage list voodoo... :-)
174
175 /*
176 Steps:
177   o Check for intersection. If no intersection, then no need to divide rects.
178   o Loop through current rects. If rect is completely inside passed in rect, remove from list.
179   o Loop through remaining rects. If rect intersects, decompose to four rects and
180     exclude degenerate rects, push rest into the coverage list.
181
182 */
183 //      std::list<Element *>::reverse_iterator ri;
184 //      std::list<SDL_Rect>::iterator i;
185
186         // Loop through rects and remove those completely covered by passed in rect.
187 /*      for(i=coverList.begin(); i!=coverList.end(); i++)
188         {
189 //              if (RectanglesIntersect(r, *i))
190                 if (RectangleFirstInsideSecond(*i, r))
191                 {
192 //This is not right--do a while loop instead of a for loop?
193                         // Remove it from the list...
194                         std::list<SDL_Rect>::iterator next = coverList.erase(i);
195                 }
196         }
197 */
198         // Loop through rects and remove those completely covered by passed in rect.
199         std::list<SDL_Rect>::iterator i = coverList.begin();
200
201         while (i != coverList.end())
202         {
203                 if (RectangleFirstInsideSecond(*i, r))
204                         i = coverList.erase(i);                         // This will also advance i to the next item!
205                 else
206                         i++;
207         }
208
209 //This may not be needed if nothing follows the loop below...!
210 //      if (coverList.empty())
211 //              return;
212
213         // Check for intersection. If no intersection, then no need to divide rects.
214         i = coverList.begin();
215
216         while (i != coverList.end())
217         {
218                 if (RectanglesIntersect(r, *i))
219                 {
220                         // Do the decomposition here. There will always be at least *one* rectangle
221                         // generated by this algorithm, so we know we're OK in removing the original
222                         // from the list. The general pattern looks like this:
223                         //
224                         // +------+
225                         // |1     |
226                         // +-+--+-+
227                         // |2|//|3|  <- Rectangle "r" is in the center
228                         // +-+--+-+
229                         // |4     |
230                         // +------+
231                         //
232                         // Even if r extends beyond the bounds of the rectangle under consideration,
233                         // that's OK because we test to see that the rectangle isn't degenerate
234                         // before adding it to the list.
235
236 //Should probably use a separate list here and splice it in when we're done here...
237 //Or, could use push_front() to avoid the problem... Neat! Doesn't require a separate list!
238 //But, we need to remove the currently referenced rect... Another while loop!
239
240 //This approach won't work--if no rect1 then we're screwed! [FIXED]
241 //Now *that* will work...
242                         SDL_Rect current = *i;
243                         uint32 bottomOfRect1 = current.y;
244 //                      uint32 rightOfRect2 = current.x;
245 //                      uint32 leftOfRect3 = current.x + current.w;
246                         uint32 topOfRect4 = current.y + current.h;
247
248                         // Rectangle #1 (top)
249                         if (r.y > current.y)                            // Simple rectangle degeneracy test...
250                         {
251                                 bottomOfRect1 = r.y;
252                                 SDL_Rect rect = current;
253                                 rect.h = r.y - current.y;
254                                 coverList.push_front(rect);
255                         }
256
257                         // Rectangle #4 (bottom)
258                         if (r.y + r.h < current.y + current.h)
259                         {
260                                 topOfRect4 = r.y + r.h;
261                                 SDL_Rect rect = current;
262                                 rect.y = r.y + r.h;
263                                 rect.h = (current.y + current.h) - (r.y + r.h);
264                                 coverList.push_front(rect);
265                         }
266
267                         // Rectangle #2 (left side)
268                         if (r.x > current.x)
269                         {
270                                 SDL_Rect rect = current;
271                                 rect.w = r.x - current.x;
272                                 rect.y = bottomOfRect1;
273                                 rect.h = topOfRect4 - bottomOfRect1;
274                                 coverList.push_front(rect);
275                         }
276
277                         // Rectangle #3 (right side)
278                         if (r.x + r.w < current.x + current.w)
279                         {
280                                 SDL_Rect rect;
281                                 rect.x = r.x + r.w;
282                                 rect.w = (current.x + current.w) - (r.x + r.w);
283                                 rect.y = bottomOfRect1;
284                                 rect.h = topOfRect4 - bottomOfRect1;
285                                 coverList.push_front(rect);
286                         }
287
288                         i = coverList.erase(i);                         // This will also advance i to the next item!
289                 }
290                 else
291                         i++;
292         }
293 }
294
295 //
296 // Class methods
297 //
298
299 void Element::SetScreen(SDL_Surface * s)
300 {
301         screen = s;
302 }
303
304 bool Element::ScreenNeedsRefreshing(void)
305 {
306         return needToRefreshScreen;
307 }
308
309 void Element::ScreenWasRefreshed(void)
310 {
311         needToRefreshScreen = false;
312 }