DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/libs/gui_tk/gui_tk.cpp
Go to the documentation of this file.
00001 /*
00002  *  gui_tk - framework-agnostic GUI toolkit
00003  *  Copyright (C) 2005-2013 Jörg Walter
00004  *
00005  *  gui_tk is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 3 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  gui_tk is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program.  If not, see <http://www.gnu.org/licenses/>
00017  */
00018 
00019 /* TODO:
00020   - make menu a bufferedwindow with shadow
00021 */
00022 
00032 #include "config.h"
00033 #include "dosbox.h"
00034 
00035 #include <SDL.h>
00036 #include "gui_tk.h"
00037 
00038 namespace GUI {
00039 
00040 /* start <= y < stop, region reserved for top level window title bar */
00041 int titlebar_y_start = 5;
00042 int titlebar_y_stop = 25;
00043 
00044 /* region where title bar is drawn */
00045 int titlebox_y_start = 4;
00046 int titlebox_y_height = 20;
00047 
00048 /* width of the system menu */
00049 int titlebox_sysmenu_width = 20; // includes black divider line
00050 
00051 namespace Color {
00052         RGB Background3D =              0xffc0c0c0;
00053         RGB Light3D =                   0xfffcfcfc;
00054         RGB Shadow3D =                  0xff808080;
00055         RGB Border =                    0xff000000;
00056         RGB Text =                      0xff000000;
00057         RGB Background =                0xffc0c0c0;
00058         RGB SelectionBackground =       0xff000080;
00059         RGB SelectionForeground =       0xffffffff;
00060         RGB EditableBackground =        0xffffffff;
00061         RGB Titlebar =                  0xffa4c8f0;
00062         RGB TitlebarText =              0xff000000;
00063         RGB TitlebarInactive =                  0xffffffff;
00064         RGB TitlebarInactiveText =              0xff000000;
00065 }
00066 
00067 std::map<const char *,Font *,Font::ltstr> Font::registry;
00068 
00069 bool ToplevelWindow::mouseDoubleClicked(int x, int y, MouseButton button) {
00070         if (button == Left && x < (6+titlebox_sysmenu_width) && x > 6 && y >= titlebar_y_start && y < titlebar_y_stop) {
00071                 systemMenu->executeAction("Close");
00072                 return true;
00073         }
00074         BorderedWindow::mouseClicked(x,y,button);
00075         return true;
00076 }
00077 
00078 void Drawable::drawText(const String& text, bool interpret, Size start, Size len) {
00079         if (interpret) {
00080                 if (len > text.size()-start) len = (Size)(text.size()-start);
00081                 len += start;
00082 
00083                 Size wordstart = start;
00084                 int width = 0;
00085 
00086                 while (start < len) {
00087                         switch (font->toSpecial(text[start])) {
00088                         case Font::CR:
00089                                 if (wordstart != start) {
00090                                         drawText(text,false,wordstart,start-wordstart);
00091                                         wordstart = start;
00092                                         width = 0;
00093                                 }
00094                                 wordstart++;
00095                                 gotoXY(0,y);
00096                                 break;
00097                         case Font::LF:
00098                                 if (wordstart != start) {
00099                                         drawText(text,false,wordstart,start-wordstart);
00100                                         wordstart = start;
00101                                         width = 0;
00102                                 }
00103                                 wordstart++;
00104                                 gotoXY(0,y+font->getHeight());
00105                                 break;
00106                         case Font::BS:
00107                                 if (wordstart != start) {
00108                                         drawText(text,false,wordstart,start-wordstart);
00109                                         wordstart = start;
00110                                         width = 0;
00111                                 }
00112                                 wordstart++;
00113                                 gotoXY(imax(0,x-font->getWidth()),y);
00114                                 break;
00115                         case Font::Tab:
00116                                 if (wordstart != start) {
00117                                         drawText(text,false,wordstart,start-wordstart);
00118                                         wordstart = start;
00119                                         width = 0;
00120                                 }
00121                                 wordstart++;
00122                                 gotoXY((((int)(x/font->getWidth()/8))+1)*8*font->getWidth(),y);
00123                                 break;
00124                         case Font::Space:
00125                                 if (wordstart != start) {
00126                                         drawText(text,false,wordstart,start-wordstart);
00127                                         wordstart = start;
00128                                         width = 0;
00129                                 }
00130                                 wordstart++;
00131                                 font->drawString(this,text,start,1);
00132                                 break;
00133                         case Font::ESC: // ignore ANSI sequences except for colors
00134                                 if (wordstart != start) {
00135                                         drawText(text,false,wordstart,start-wordstart);
00136                                         wordstart = start;
00137                                         width = 0;
00138                                 }
00139                                 wordstart++;
00140                                 do {
00141                                         Size seqstart = start+1u;
00142                                         Char c;
00143                                         do {
00144                                                 start++;
00145                                                 wordstart++;
00146                                                 c = font->toSpecial(text[start]);
00147                                         } while (start < len && ((c >= '0' && c <= '9') || c == ';' || c == '['));
00148                                         if (c == 'm' && start < len) {
00149                                                 if (text[seqstart++] != '[') break;
00150                                                 c = font->toSpecial(text[seqstart++]);
00151                                                 while (c != 'm') {
00152                                                         unsigned int param = 0;
00153                                                         if (c == ';') c = '0';
00154                                                         while (c != 'm' && c != ';') {
00155                                                                 param = param * 10u + (unsigned int)c - '0';
00156                                                                 c = font->toSpecial(text[seqstart++]);
00157                                                         }
00158                                                         const RGB bright = 0x00808080u;
00159                                                         const RGB intensity = (color&bright?~0u:~bright);
00160                                                         switch (param) {
00161                                                         case 0: setColor(Color::Black); break;
00162                                                         case 1: setColor(color | 0x00808080u); break;
00163                                                         case 30: setColor((Color::Black|bright) & intensity); break;
00164                                                         case 31: setColor(Color::Red & intensity); break;
00165                                                         case 32: setColor(Color::Green & intensity); break;
00166                                                         case 33: setColor(Color::Yellow & intensity); break;
00167                                                         case 34: setColor(Color::Blue & intensity); break;
00168                                                         case 35: setColor(Color::Magenta & intensity); break;
00169                                                         case 36: setColor(Color::Cyan & intensity); break;
00170                                                         case 37: setColor(Color::White & intensity); break;
00171                                                         default: break;
00172                                                         }
00173                                                 }
00174                                         }
00175                                 } while (0);
00176                         default:
00177                                 width += font->getWidth(text[start]);
00178                                 if (x > 0 && x+width > (this->fw)) gotoXY(0,y+font->getHeight());
00179                         }
00180                         start++;
00181                 }
00182                 if (wordstart != start) drawText(text,false,wordstart,start-wordstart);
00183                 return;
00184         }
00185 
00186         font->drawString(this,text,start,len);
00187 }
00188 
00189 bool ToplevelWindow::mouseDown(int x, int y, MouseButton button) {
00190         if (button == Left && x >= (6+titlebox_sysmenu_width) && x < width-6 && y >= titlebar_y_start && y < titlebar_y_stop) {
00191                 dragx = x;
00192                 dragy = y;
00193                 mouseChild = NULL;
00194                 systemMenu->setVisible(false);
00195                 return true;
00196         } else if (button == Left && x < (6+titlebox_sysmenu_width) && x >= 6 && y >= titlebar_y_start && y < titlebar_y_stop) {
00197                 mouseChild = NULL;
00198                 raise();
00199                 systemMenu->setVisible(!systemMenu->isVisible());
00200                 return true;
00201         }
00202         systemMenu->setVisible(false);
00203         BorderedWindow::mouseDown(x,y,button);
00204         return true;
00205 }
00206 
00207 Drawable::Drawable(int w, int h, RGB clear) :
00208         buffer(new RGB[w*h]),
00209         width(w), height(h),
00210         owner(true),
00211         color(Color::Black),
00212         font(NULL),
00213         lineWidth(1),
00214         tx(0), ty(0),
00215         cx(0), cy(0),
00216         cw(w), ch(h),
00217     fw(w), fh(h),
00218         x(0), y(0)
00219 {
00220         this->clear(clear);
00221 }
00222 
00223 Drawable::Drawable(Drawable &src, RGB clear) :
00224         buffer(new RGB[src.cw*src.ch]),
00225         width(src.cw), height(src.ch),
00226         owner(true),
00227         color(src.color),
00228         font(src.font),
00229         lineWidth(src.lineWidth),
00230         tx(0), ty(0),
00231         cx(0), cy(0),
00232         cw(src.cw), ch(src.ch),
00233     fw(src.fw), fh(src.fh),
00234         x(src.x), y(src.y)
00235 {
00236         if (clear != 0) {
00237                 this->clear(clear);
00238         } else {
00239                 for (unsigned int h = 0; (int)h < src.ch; h++) {
00240                         memcpy(buffer+(size_t)src.cw*h,src.buffer+src.width*(h+(size_t)src.ty)+src.tx,4u*(size_t)src.cw);
00241                 }
00242         }
00243 }
00244 
00245 Drawable::Drawable(Drawable &src, int x, int y, int w, int h) :
00246         buffer(src.buffer),
00247         width(src.width), height(src.height),
00248         owner(false),
00249         color(src.color),
00250         font(src.font),
00251         lineWidth(src.lineWidth),
00252         tx(src.tx+x), ty(src.ty+y),
00253         cx(imax(imax(-x,src.cx-x),0)), cy(imax(imax(-y,src.cy-y),0)),
00254         cw(imax(0,imin(src.cw-x,w))), ch(imax(0,imin(src.ch-y,h))),
00255         fw(w), fh(h),
00256         x(imax(0,imin(src.tx-tx,cw))), y(imax(0,imin(src.ty-ty,cw)))
00257 {
00258 }
00259 
00260 Drawable::~Drawable()
00261 {
00262         if (owner) delete[] buffer;
00263 }
00264 
00265 void Drawable::clear(RGB clear)
00266 {
00267         for (int y = cy; y < ch; y++) {
00268                 for (int x = cx; x < cw; x++) {
00269                         buffer[(y+ty)*width+x+tx] = clear;
00270                 }
00271         }
00272 }
00273 
00274 void Drawable::drawDotLine(int x2, int y2)
00275 {
00276         int x0 = x2, x1 = x, y0 = y2, y1 = y;
00277         int dx = x2-x1, dy = y2-y1;
00278         drawPixel();
00279 
00280         if (abs(dx) > abs(dy)) {
00281                 if (x1 > x2) {
00282                         x = x2; x2 = x1; x1 = x;
00283                         y = y2; y2 = y1; y1 = y;
00284                 }
00285                 for (x = x1; x <= x2; x++) {
00286                         y = y1+(x-x1)*dy/dx-lineWidth/2;
00287                         for (int i = 0; i < lineWidth; i++, y++) {
00288                 if (((x^y)&1) == 0)
00289                     drawPixel();
00290             }
00291         }
00292     } else if (y1 != y2) {
00293         if (y1 > y2) {
00294             x = x2; x2 = x1; x1 = x;
00295             y = y2; y2 = y1; y1 = y;
00296         }
00297         for (y = y1; y <= y2; y ++) {
00298             x = x1+(y-y1)*dx/dy-lineWidth/2;
00299             for (int i = 0; i < lineWidth; i++, x++) {
00300                 if (((x^y)&1) == 0)
00301                     drawPixel();
00302             }
00303                 }
00304         }
00305 
00306         drawPixel(x0,y0);
00307 }
00308 
00309 void Drawable::drawLine(int x2, int y2)
00310 {
00311         int x0 = x2, x1 = x, y0 = y2, y1 = y;
00312         int dx = x2-x1, dy = y2-y1;
00313         drawPixel();
00314 
00315         if (abs(dx) > abs(dy)) {
00316                 if (x1 > x2) {
00317                         x = x2; x2 = x1; x1 = x;
00318                         y = y2; y2 = y1; y1 = y;
00319                 }
00320                 for (x = x1; x <= x2; x++) {
00321                         y = y1+(x-x1)*dy/dx-lineWidth/2;
00322                         for (int i = 0; i < lineWidth; i++, y++) {
00323                                 drawPixel();
00324                         }
00325                 }
00326         } else if (y1 != y2) {
00327                 if (y1 > y2) {
00328                         x = x2; x2 = x1; x1 = x;
00329                         y = y2; y2 = y1; y1 = y;
00330                 }
00331                 for (y = y1; y <= y2; y ++) {
00332                         x = x1+(y-y1)*dx/dy-lineWidth/2;
00333                         for (int i = 0; i < lineWidth; i++, x++) {
00334                                 drawPixel();
00335                         }
00336                 }
00337         }
00338 
00339         drawPixel(x0,y0);
00340 }
00341 
00342 void Drawable::drawCircle(int d) {
00343         int xo = 0, yo = d/2, rest = (d+1)/2-yo, x0 = x, y0 = y, rsq = d*d/4, lwo = lineWidth/2;
00344 
00345         while (xo <= yo) {
00346                 while (xo*xo+(2*yo-1)*(2*yo-1)/4 > rsq) yo--;
00347                 for (int i = 0, yow = yo+lwo; i < lineWidth; i++, yow--) {
00348                         drawPixel(x0+xo,y0-yow-rest);
00349                         drawPixel(x0+yow,y0-xo-rest);
00350                         drawPixel(x0+yow,y0+xo);
00351                         drawPixel(x0+xo,y0+yow);
00352 
00353                         drawPixel(x0-xo-rest,y0-yow-rest);
00354                         drawPixel(x0-yow-rest,y0-xo-rest);
00355                         drawPixel(x0-yow-rest,y0+xo);
00356                         drawPixel(x0-xo-rest,y0+yow);
00357                 }
00358 
00359                 xo++;
00360         }
00361         gotoXY(x0,y0);
00362 }
00363 
00364 void Drawable::drawRect(int w, int h)
00365 {
00366         gotoXY(x-lineWidth/2,y);
00367         drawLine(x+w+lineWidth-1,y);
00368         gotoXY(x-(lineWidth-1)/2,y);
00369         drawLine(x,y+h);
00370         gotoXY(x+(lineWidth-1)/2,y);
00371         drawLine(x-w-lineWidth+1,y);
00372         gotoXY(x+lineWidth/2,y);
00373         drawLine(x,y-h);
00374 }
00375 
00376 void Drawable::drawDotRect(int w, int h)
00377 {
00378         gotoXY(x-lineWidth/2,y);
00379         drawDotLine(x+w+lineWidth-1,y);
00380         gotoXY(x-(lineWidth-1)/2,y);
00381         drawDotLine(x,y+h);
00382         gotoXY(x+(lineWidth-1)/2,y);
00383         drawDotLine(x-w-lineWidth+1,y);
00384         gotoXY(x+lineWidth/2,y);
00385         drawDotLine(x,y-h);
00386 }
00387 
00388 void Drawable::fill()
00389 {
00390         int x0 = x, xmin;
00391         RGB color = getPixel();
00392 
00393         if (color == this->color) return;
00394 
00395         for (x--; x >= 0 && getPixel() == color; x--) drawPixel();
00396         xmin = ++x;
00397         for (x = x0; x < cw && getPixel() == color; x++) drawPixel();
00398         y++;
00399         for (x--; x >= xmin; x--) {
00400                 if (getPixel() == color) fill();
00401                 y -= 2;
00402                 if (getPixel() == color) fill();
00403                 y += 2;
00404         }
00405         y--;
00406         x = x0;
00407 }
00408 
00409 void Drawable::fillCircle(int d)
00410 {
00411         int xo = 0, yo = d/2, rest = (d+1)/2-yo, x0 = x, y0 = y, rsq = d*d/4;
00412 
00413         while (xo <= yo) {
00414                 while (xo*xo+(2*yo-1)*(2*yo-1)/4 > rsq) yo--;
00415                 x = x0+xo;
00416                 for (y = y0-yo-rest; y <= y0+yo; y++) drawPixel();
00417                 x = x0-xo-rest;
00418                 for (y = y0-yo-rest; y <= y0+yo; y++) drawPixel();
00419 
00420                 y = y0-xo-rest;
00421                 for (x = x0-yo-rest; x <= x0+yo; x++) drawPixel();
00422                 y = y0+xo;
00423                 for (x = x0-yo-rest; x <= x0+yo; x++) drawPixel();
00424 
00425                 xo++;
00426         }
00427         gotoXY(x0,y0);
00428 }
00429 
00430 void Drawable::fillRect(int w, int h)
00431 {
00432         int x0 = x, y0 = y, w0 = w;
00433         for (; h > 0; h--, y++) {
00434                 for (x = x0, w = w0; w > 0; w--, x++) {
00435                         drawPixel();
00436                 }
00437         }
00438         gotoXY(x0,y0);
00439 }
00440 
00441 void Drawable::drawDrawable(Drawable &d, unsigned char alpha)
00442 {
00443         int scw = d.cw, sch = d.ch, w, h;
00444         RGB *src, *dest;
00445 
00446         for (h = imax(d.cy,-ty-y); h < sch && y+h < ch; h++) {
00447                 src = d.buffer+d.width*((size_t)h+d.ty)+d.tx;
00448                 dest = buffer+width*((size_t)y+ty+h)+tx+x;
00449                 for (w = imax(d.cx,-tx-x); w < scw && x+w < cw; w++) {
00450                         RGB srcb = src[w], destb = dest[w];
00451                         unsigned int sop = Color::A(srcb)*((unsigned int)alpha)/255;
00452                         unsigned int rop = Color::A(destb) + sop - Color::A(destb)*sop/255;
00453                         if (rop == 0) {
00454                                 dest[w] = Color::Transparent;
00455                         } else {
00456                                 unsigned int dop = Color::A(destb)*(255-sop)/255;
00457                                 unsigned int magval = ((destb&Color::MagentaMask)*dop+(srcb&Color::MagentaMask)*sop);
00458                                 dest[w] = (((magval&0xffff)/rop)&Color::BlueMask) |
00459                                         (((magval&0xffff0000)/rop)&Color::RedMask) |
00460                                         ((((destb&Color::GreenMask)*dop+(srcb&Color::GreenMask)*sop)/rop)&Color::GreenMask) |
00461                                         (rop<<Color::AlphaShift);
00462                         }
00463                 }
00464         }
00465 }
00466 
00467 void Drawable::drawText(const Char c, bool interpret)
00468 {
00469         if (interpret) {
00470                 switch (font->toSpecial(c)) {
00471                 case Font::CR: gotoXY(0,y); return;
00472                 case Font::LF: gotoXY(0,y+font->getHeight()); return;
00473                 case Font::BS: gotoXY(imax(0,x-font->getWidth()),y); return;
00474                 case Font::Tab: gotoXY((((int)(x/font->getWidth()/8))+1)*8*font->getWidth(),y); return;
00475                 default: break;
00476                 }
00477                 if (font->getWidth(c)+x > (this->fw)) gotoXY(0,y+font->getHeight());
00478         }
00479         font->drawChar(this,c);
00480 }
00481 
00482 void BitmapFont::drawChar(Drawable *d, const Char c) const {
00483 #define move(x) (ptr += ((x)+bit)/8-(((x)+bit)<0), bit = ((x)+bit+(((x)+bit)<0?8:0))%8)
00484         const unsigned char *ptr = bitmap;
00485         int bit = 0;
00486 
00487         if (c > last) return;
00488 
00489         if (char_position != NULL) {
00490                 ptr = char_position[c];
00491                 bit = 0;
00492         } else {
00493                 move(character_step*((int)c));
00494         }
00495 
00496         int rs = row_step;
00497         int w = (widths != NULL?widths[c]:width);
00498         int h = (ascents != NULL?ascents[c]:height);
00499         Drawable out(*d,d->getX(),d->getY()-ascent,w,h);
00500 
00501         if (rs == 0) rs = isign(col_step)*w;
00502         if (rs < 0) move(-rs*(h-1));
00503         if (col_step < 0) move(abs(rs)-1);
00504 
00505         for (int row = height-h; row < height; row++, move(rs-w*col_step)) {
00506                 for (int col = 0; col < w; col++, move(col_step)) {
00507                         if (!background_set != !(*ptr&(1<<bit)))
00508                                 out.drawPixel(col,row);
00509                 }
00510         }
00511         d->gotoXY(d->getX()+w,d->getY());
00512 #undef move
00513 }
00514 
00515 void Timer::check_to(unsigned int ticks) {
00516         if (ticks >= Timer::ticks) check(ticks - Timer::ticks);
00517 }
00518 
00519 void Timer::check(unsigned int ticks)
00520 {
00521         if (timers.empty()) {
00522                 Timer::ticks += ticks;
00523                 return;
00524         }
00525 
00526         if (Timer::ticks > (Timer::ticks+ticks)) {
00527                 ticks -= (unsigned int)(-(int)Timer::ticks) - 1u;
00528                 check((unsigned int)(-(int)Timer::ticks) - 1u);
00529         }
00530 
00531         std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator old, i = timers.lower_bound(Timer::ticks+1);
00532         Timer::ticks += ticks;
00533 
00534         while (i != timers.end() && (*i).first <= Timer::ticks) {
00535                 Timer_Callback *c = (*i).second;
00536                 unsigned int time = (*i).first;
00537                 old = i;
00538                 ++i;
00539                 timers.erase(old);
00540                 unsigned int next = c->timerExpired(time);
00541                 if (next) add(c, time+next-Timer::ticks);
00542         }
00543 }
00544 
00545 void Timer::remove(const Timer_Callback *const timer)
00546 {
00547         if (timers.empty()) return;
00548 
00549         std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator old, i = timers.begin();
00550 
00551         while (i != timers.end()) {
00552                 old = i;
00553                 ++i;
00554                 if ((*old).second == timer) timers.erase(old);
00555         }
00556 }
00557 
00558 unsigned int Timer::next()
00559 {
00560         if (timers.empty()) return 0;
00561 
00562         std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator i = timers.upper_bound(ticks);
00563 
00564         if (i == timers.end()) return 0;
00565         return (*i).first-Timer::ticks;
00566 }
00567 
00568 std::multimap<unsigned int,Timer_Callback*,Timer::ltuint> Timer::timers;
00569 unsigned int Timer::ticks = 0;
00570 
00571 BitmapFont::BitmapFont(const unsigned char *data, int height, int ascent, bool owner,
00572                 int width, bool background_set,
00573                 int col_step, int row_step, int character_step, Char last,
00574                 const int *widths, const int *ascents, const unsigned char *const* char_position,
00575                 const Font::SpecialChar *special) :
00576                 bitmap(data),
00577                 width(width), height(height), ascent(ascent), widths(widths), ascents(ascents),
00578                 background_set(background_set), col_step(col_step), row_step(row_step),
00579                 character_step(character_step?character_step:abs((row_step?row_step:width*col_step)*height)),
00580                 char_position(char_position), special(special), owner(owner), last(last)
00581 {
00582 }
00583 
00584 BitmapFont::~BitmapFont() {
00585         if (owner) {
00586                 if (bitmap != NULL) delete bitmap;
00587                 if (ascents != NULL) delete ascents;
00588                 if (widths != NULL) delete widths;
00589                 if (special != NULL) delete special;
00590         }
00591 }
00592 
00593 Window::Window(Window *parent, int x, int y, int w, int h) :
00594         width(w), height(h),
00595         x(x), y(y),
00596         dirty(true),
00597         visible(true),
00598     tabbable(true),
00599         parent(parent),
00600         mouseChild(NULL),
00601     transient(false),
00602     toplevel(false),
00603     mouse_in_window(false),
00604     first_tabbable(false),
00605     last_tabbable(false)
00606 {
00607         parent->addChild(this);
00608 }
00609 
00610 Window::Window() :
00611         width(0), height(0),
00612         x(0), y(0),
00613         dirty(false),
00614         visible(true),
00615     tabbable(true),
00616         parent(NULL),
00617         mouseChild(NULL),
00618     transient(false),
00619     toplevel(false),
00620     mouse_in_window(false)
00621 {
00622 }
00623 
00624 
00625 Window::~Window()
00626 {
00627     // WARNING: Child windows will call parent->removeChild() each child we delete here.
00628     //          That means the list is modified per call, therefore it's not safe to
00629     //          blindly iterate over the children list.
00630     //
00631     //          Furthermore, modifying the code so that removeChild() does nothing during
00632     //          the destructor, and then iterating over the children list normally and
00633     //          deleting the child windows, works OK except for a segfault when the user
00634     //          uses the "Close" menu item from the system menu.
00635     //
00636     //          This code is a good framework for the GUI but a knotted mess when it comes
00637     //          to pointer ownership and when things are valid.
00638         while (!children.empty()) delete children.front();
00639         if (parent) parent->removeChild(this);
00640         if (parent && parent->mouseChild == this) parent->mouseChild = NULL;
00641 }
00642 
00643 void Window::addChild(Window *child)
00644 {
00645         children.push_back(child);
00646         setDirty();
00647 }
00648 
00649 void Window::removeChild(Window *child)
00650 {
00651         children.remove(child);
00652         setDirty();
00653 }
00654 
00655 void Window::move(int x, int y)
00656 {
00657         this->x = x;
00658         this->y = y;
00659         std::list<Window_Callback*>::iterator i = movehandlers.begin();
00660         bool end = (i == movehandlers.end());
00661         while (!end) {
00662                 Window_Callback *c = *i;
00663                 ++i;
00664                 end = (i == movehandlers.end());
00665                 c->windowMoved(this,x,y);
00666         }
00667         parent->setDirty();
00668 }
00669 
00670 void Window::resize(int w, int h)
00671 {
00672         this->width = w;
00673         this->height = h;
00674         setDirty();
00675 }
00676 
00677 void Window::paintAll(Drawable &d) const
00678 {
00679         paint(d);
00680         std::list<Window *>::const_iterator i = children.begin();
00681         while (i != children.end()) {
00682                 Window *child = *i;
00683                 ++i;
00684                 if (child->visible) {
00685                         Drawable *cd = new Drawable(d,child->x,child->y,child->width,child->height);
00686                         child->paintAll(*cd);
00687                         delete cd;
00688                 }
00689         }
00690 }
00691 
00692 bool Window::keyDown(const Key &key)
00693 {
00694         if (children.empty()) return false;
00695         if ((*children.rbegin())->keyDown(key)) return true;
00696         if (key.ctrl || key.alt || key.windows || key.special != Key::Tab) return false;
00697 
00698         if (key.shift) {
00699                 std::list<Window *>::reverse_iterator i = children.rbegin(), e = children.rend();
00700                 ++i;
00701         while (i != e) {
00702             if ((*i)->tabbable) {
00703                 if ((*i)->raise())
00704                     break;
00705             }
00706 
00707             ++i;
00708         }
00709         return (i != e) || toplevel/*prevent TAB escape to another window*/;
00710         } else {
00711                 std::list<Window *>::iterator i = children.begin(), e = children.end();
00712         --e;
00713                 while (i != e) {
00714             if ((*i)->tabbable) {
00715                 if ((*i)->raise())
00716                     break;
00717             }
00718 
00719             ++i;
00720         }
00721                 return (i != e) || toplevel/*prevent TAB escape to another window*/;
00722         }
00723 }
00724 
00725 void WindowInWindow::scrollToWindow(Window *child) {
00726     if (child->parent != this) {
00727         fprintf(stderr,"BUG: scrollToWindow given a window not a child of this parent\n");
00728         return;
00729     }
00730 
00731     int xadj = -scroll_pos_x;
00732     int yadj = -scroll_pos_y;
00733     int bw = width;
00734     int bh = height;
00735 
00736     if (border) {
00737         bw -= 2 + (vscroll?vscroll_display_width:0);
00738         bh -= 2;
00739     }
00740 
00741     int rx = child->getX() + xadj;
00742     int ry = child->getY() + yadj;
00743 
00744     if (rx < 0)
00745         scroll_pos_x += rx;
00746     if (ry < 0)
00747         scroll_pos_y += ry;
00748 
00749     {/*UNTESTED*/
00750         int ext = (rx+child->getWidth()) - bw;
00751         if (ext > 0) scroll_pos_x += ext;
00752     }
00753 
00754     {
00755         int ext = (ry+child->getHeight()) - bh;
00756         if (ext > 0) scroll_pos_y += ext;
00757     }
00758 
00759     if (scroll_pos_x < 0) scroll_pos_x = 0;
00760     if (scroll_pos_y < 0) scroll_pos_y = 0;
00761     if (scroll_pos_x > scroll_pos_w) scroll_pos_x = scroll_pos_w;
00762     if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
00763 }
00764 
00765 bool WindowInWindow::keyDown(const Key &key)
00766 {
00767         if (children.empty()) return false;
00768         if ((*children.rbegin())->keyDown(key)) return true;
00769     if (dragging || vscroll_dragging) return true;
00770 
00771     if (key.special == Key::Up) {
00772         scroll_pos_y -= 64;
00773         if (scroll_pos_y < 0) scroll_pos_y = 0;
00774         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
00775         return true;
00776     }
00777 
00778     if (key.special == Key::Down) {
00779         scroll_pos_y += 64;
00780         if (scroll_pos_y < 0) scroll_pos_y = 0;
00781         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
00782         return true;
00783     }
00784 
00785     if (key.special == Key::PageUp) {
00786         scroll_pos_y -= height - 16;
00787         if (scroll_pos_y < 0) scroll_pos_y = 0;
00788         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
00789         return true;
00790     }
00791 
00792     if (key.special == Key::PageDown) {
00793         scroll_pos_y += height - 16;
00794         if (scroll_pos_y < 0) scroll_pos_y = 0;
00795         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
00796         return true;
00797     }
00798 
00799         if (key.ctrl || key.alt || key.windows || key.special != Key::Tab) return false;
00800 
00801     bool tab_quit = false;
00802 
00803         if (key.shift) {
00804                 std::list<Window *>::reverse_iterator i = children.rbegin(), e = children.rend();
00805                 ++i;
00806         while (i != e) {
00807             if ((*i)->last_tabbable)
00808                 tab_quit = true;
00809 
00810             if ((*i)->tabbable) {
00811                 // WARNING: remember raise() changes the order of children, therefore using
00812                 //          *i after raise() is invalid (stale reference)
00813                 scrollToWindow(*i);
00814                 if ((*i)->raise())
00815                     break;
00816             }
00817 
00818             ++i;
00819         }
00820         if (tab_quit) return false;
00821         return (i != e) || toplevel/*prevent TAB escape to another window*/;
00822     } else {
00823         std::list<Window *>::iterator i = children.begin(), e = children.end();
00824         --e;
00825         while (i != e) {
00826             if ((*i)->first_tabbable)
00827                 tab_quit = true;
00828 
00829             if ((*i)->tabbable) {
00830                 // WARNING: remember raise() changes the order of children, therefore using
00831                 //          *i after raise() is invalid (stale reference)
00832                 scrollToWindow(*i);
00833                 if ((*i)->raise())
00834                     break;
00835             }
00836 
00837             ++i;
00838         }
00839         if (tab_quit) return false;
00840                 return (i != e) || toplevel/*prevent TAB escape to another window*/;
00841         }
00842 }
00843 
00844 bool Window::keyUp(const Key &key)
00845 {
00846         if (children.empty()) return false;
00847         return (*children.rbegin())->keyUp(key);
00848 }
00849 
00850 void Window::mouseMovedOutside(void) {
00851 }
00852 
00853 bool Window::mouseMoved(int x, int y)
00854 {
00855         std::list<Window *>::reverse_iterator i = children.rbegin();
00856         bool end = (i == children.rend());
00857         while (!end) {
00858                 Window *w = *i;
00859                 ++i;
00860                 end = (i == children.rend());
00861                 if (w->visible && x >= w->x && x <= w->x+w->width
00862                         && y >= w->y && y <= w->y+w->height
00863                         && w->mouseMoved(x-w->x, y-w->y)) {
00864             w->mouse_in_window = true;
00865             return true;
00866         }
00867         else if (w->mouse_in_window) {
00868             w->mouse_in_window = false;
00869             w->mouseMovedOutside();
00870         }
00871         }
00872 
00873         return false;
00874 }
00875 
00876 bool Window::mouseDragged(int x, int y, MouseButton button)
00877 {
00878         if (mouseChild == NULL) return false;
00879         return mouseChild->mouseDragged(x-mouseChild->x, y-mouseChild->y, button);
00880 }
00881 
00882 bool Window::mouseDownOutside(MouseButton button) {
00883         std::list<Window *>::reverse_iterator i = children.rbegin();
00884     bool handled = false;
00885 
00886         while (i != children.rend()) {
00887                 Window *w = *i;
00888 
00889                 if (w->hasFocus()) {
00890                         if (w->mouseDownOutside(button))
00891                                 handled = true;
00892                 }
00893 
00894                 ++i;
00895         }
00896 
00897     return handled;
00898 }
00899 
00900 bool Window::mouseDown(int x, int y, MouseButton button)
00901 {
00902         std::list<Window *>::reverse_iterator i = children.rbegin();
00903     bool handled = false;
00904     bool doraise = !(button == GUI::WheelUp || button == GUI::WheelDown); /* do not raise if the scroll wheel */
00905         Window *last = NULL;
00906 
00907         while (i != children.rend()) {
00908                 Window *w = *i;
00909 
00910                 if (w->visible && x >= w->x && x < (w->x+w->width) && y >= w->y && y < (w->y+w->height)) {
00911             if (handled) {
00912                 mouseChild = NULL;
00913                 return true;
00914             }
00915                         mouseChild = last = w;
00916                         if (w->mouseDown(x-w->x, y-w->y, button)) {
00917                 if (doraise) w->raise();
00918                                 return true;
00919                         }
00920                 }
00921         else if (w->transient) {
00922             handled |= w->mouseDownOutside(button);
00923         }
00924 
00925                 ++i;
00926         }
00927 
00928         mouseChild = NULL;
00929         if (last != NULL && doraise) last->raise();
00930         return handled;
00931 }
00932 
00933 bool Window::mouseUp(int x, int y, MouseButton button)
00934 {
00935         if (mouseChild == NULL) return false;
00936         return mouseChild->mouseUp(x-mouseChild->x, y-mouseChild->y, button);
00937 }
00938 
00939 bool Window::mouseClicked(int x, int y, MouseButton button)
00940 {
00941         if (mouseChild == NULL) return false;
00942         return mouseChild->mouseClicked(x-mouseChild->x, y-mouseChild->y, button);
00943 }
00944 
00945 bool Window::mouseDoubleClicked(int x, int y, MouseButton button)
00946 {
00947         if (mouseChild == NULL) return false;
00948         return mouseChild->mouseDoubleClicked(x-mouseChild->x, y-mouseChild->y, button);
00949 }
00950 
00951 bool BorderedWindow::mouseDown(int x, int y, MouseButton button)
00952 {
00953         mouseChild = NULL;
00954         if (x > width-border_right || y > height-border_bottom) return false;
00955         x -= border_left; y -= border_top;
00956         if (x < 0 || y < 0) return false;
00957         return Window::mouseDown(x,y,button);
00958 }
00959 
00960 bool BorderedWindow::mouseUp(int x, int y, MouseButton button) {
00961         if (mouseChild == NULL && (x > width-border_right || y > height-border_bottom)) return false;
00962         x -= border_left; y -= border_top;
00963     if (mouseChild == NULL && (x < 0 || y < 0)) return false;
00964         return Window::mouseUp(x,y,button);
00965 }
00966 
00967 bool BorderedWindow::mouseMoved(int x, int y)
00968 {
00969         if (x > width-border_right || y > height-border_bottom) return false;
00970         x -= border_left; y -= border_top;
00971         if (x < 0 || y < 0) return false;
00972         return Window::mouseMoved(x,y);
00973 }
00974 
00975 bool BorderedWindow::mouseDragged(int x, int y, MouseButton button)
00976 {
00977         if (mouseChild == NULL && (x > width-border_right || y > height-border_bottom)) return false;
00978         x -= border_left; y -= border_top;
00979     if (mouseChild == NULL && (x < 0 || y < 0)) return false;
00980         return Window::mouseDragged(x,y,button);
00981 }
00982 
00983 void ToplevelWindow::paint(Drawable &d) const
00984 {
00985         unsigned int mask = (systemMenu->isVisible()?Color::RedMask|Color::GreenMask|Color::BlueMask:0);
00986         d.clear(Color::Background);
00987 
00988         d.setColor(Color::Border);
00989         d.drawLine(0,height-1,width-1,height-1);
00990         d.drawLine(width-1,0,width-1,height-1);
00991 
00992         d.setColor(Color::Shadow3D);
00993         d.drawLine(0,0,width-2,0);
00994         d.drawLine(0,0,0,height-2);
00995         d.drawLine(0,height-2,width-2,height-2);
00996         d.drawLine(width-2,0,width-2,height-2);
00997 
00998         d.drawLine(5,titlebox_y_start,width-7,titlebox_y_start);
00999         d.drawLine(5,titlebox_y_start,5,titlebox_y_start+titlebox_y_height-2);
01000 
01001         d.setColor(Color::Light3D);
01002         d.drawLine(1,1,width-3,1);
01003         d.drawLine(1,1,1,height-3);
01004 
01005         d.drawLine(5,titlebox_y_start+titlebox_y_height-1,width-6,titlebox_y_start+titlebox_y_height-1);
01006         d.drawLine(width-6,5,width-6,titlebox_y_start+titlebox_y_height-1);
01007 
01008         d.setColor(Color::Background3D^mask);
01009         d.fillRect(6,titlebox_y_start+1,titlebox_sysmenu_width-1,titlebox_y_height-2);
01010     {
01011         int y = titlebox_y_start+((titlebox_y_height-4)/2);
01012         int x = 8;
01013         int w = (titlebox_sysmenu_width * 20) / 27;
01014         int h = 4;
01015 
01016         d.setColor(Color::Grey50^mask);
01017         d.fillRect(x+1,y+1,w,  h);
01018         d.setColor(Color::Black^mask);
01019         d.fillRect(x,  y,  w,  h);
01020         d.setColor(Color::White^mask);
01021         d.fillRect(x+1,y+1,w-2,h-2);
01022     }
01023 
01024         d.setColor(Color::Border);
01025         d.drawLine(6+titlebox_sysmenu_width-1,titlebox_y_start+1,6+titlebox_sysmenu_width-1,titlebox_y_start+titlebox_y_height-2);
01026 
01027     bool active = hasFocus();
01028 
01029     // FIX: "has focus" is defined as "being at the back of the child list".
01030     //      Transient windows such as menus will steal focus, but we don't
01031     //      want the title to flash inactive every time a menu comes up.
01032     //      Avoid that by searching backwards past transient windows above
01033     //      us to check if we'd be at the top anyway without the transient windows.
01034     if (!active) {
01035         auto i = parent->children.rbegin();
01036         while (i != parent->children.rend()) {
01037             Window *w = *i;
01038 
01039             if (w->transient) {
01040                 i++;
01041             }
01042             else if (w == this) {
01043                 active = true;
01044                 break;
01045             }
01046             else {
01047                 break;
01048             }
01049         }
01050     }
01051 
01052         d.setColor(active ? Color::Titlebar : Color::TitlebarInactive);
01053         d.fillRect(6+titlebox_sysmenu_width,titlebox_y_start+1,width-(6+titlebox_sysmenu_width+6),titlebox_y_height-2);
01054 
01055         const Font *font = Font::getFont("title");
01056         d.setColor(active ? Color::TitlebarText : Color::TitlebarInactiveText);
01057         d.setFont(font);
01058         d.drawText(31+(width-39-font->getWidth(title))/2,titlebox_y_start+(titlebox_y_height-font->getHeight())/2+font->getAscent(),title,false,0);
01059 }
01060 
01061 void Input::posToEnd(void) {
01062         pos = (GUI::Size)text.size();
01063         checkOffset();
01064 }
01065 
01066 void Input::paint(Drawable &d) const
01067 {
01068         d.clear(Color::EditableBackground);
01069 
01070         d.setColor(Color::Shadow3D);
01071         d.drawLine(0,0,width-2,0);
01072         d.drawLine(0,0,0,height-2);
01073 
01074         d.setColor(Color::Background3D);
01075         d.drawLine(1,height-2,width-2,height-2);
01076         d.drawLine(width-2,1,width-2,height-2);
01077 
01078         d.setColor(Color::Text);
01079         d.drawLine(1,1,width-3,1);
01080         d.drawLine(1,1,1,height-3);
01081 
01082         const Font *f = Font::getFont("input");
01083         d.setFont(f);
01084 
01085         Drawable d1(d,3,4,width-6,height-8);
01086         Drawable dr(d1,(multi?0:-offset),(multi?-offset:0),width-6+(multi?0:offset),height-8+(multi?offset:0));
01087 
01088         const Size start = imin(start_sel, end_sel), end = imax(start_sel, end_sel);
01089         dr.drawText(0,f->getAscent()+1,text,multi,0,start);
01090 
01091         int sx = dr.getX(), sy = dr.getY();
01092         dr.drawText(text, multi, start, end-start);
01093         int ex = dr.getX(), ey = dr.getY();
01094 
01095         if (sx != ex || sy != ey) {
01096                 dr.setColor(Color::SelectionBackground);
01097                 if (sy == ey) dr.fillRect(sx,sy-f->getAscent(),ex-sx,f->getHeight()+1);
01098                 else {
01099                         dr.fillRect(sx, sy-f->getAscent(),              width-sx+offset, f->getHeight() );
01100                         dr.fillRect(0,  sy-f->getAscent()+f->getHeight(), width+offset,    ey-sy-f->getHeight());
01101                         dr.fillRect(0,  ey-f->getAscent(),              ex,           f->getHeight()    );
01102                 }
01103                 dr.setColor(Color::SelectionForeground);
01104                 dr.drawText(sx, sy, text, multi, start, end-start);
01105         }
01106 
01107         dr.setColor(Color::Text);
01108 
01109         dr.drawText(text, multi, end);
01110 
01111         if (blink && hasFocus()) {
01112                 if (insert) dr.drawLine(posx,posy,posx,posy+f->getHeight()+1);
01113                 else dr.fillRect(posx,posy,f->getWidth(text[pos]),f->getHeight()+1);
01114         }
01115 }
01116 
01117 Size Input::findPos(int x, int y) {
01118         const Font *f = Font::getFont("input");
01119         if (multi) y += offset;
01120         else x += offset;
01121         y = (y-4) / f->getHeight();
01122         int line = 0;
01123         Size pos = 0;
01124         while (line < y && pos < text.size()) if (f->toSpecial(text[pos++]) == Font::LF) line++;
01125         Drawable d(width-6,1);
01126         d.setFont(f);
01127         while (pos <= text.size() && d.getY() == 0 && x > d.getX()) {
01128                 d.drawText(String(text), multi, pos, 1);
01129                 pos++;
01130         }
01131         if (pos > 0) pos--;
01132         return pos;
01133 }
01134 
01135 bool Input::mouseDown(int x, int y, MouseButton button)
01136 {
01137         if (button == Left || (button == Middle && start_sel == end_sel)) {
01138                 end_sel = start_sel = pos = findPos(x,y);
01139                 blink = true;
01140                 checkOffset();
01141         }
01142         if (button == Middle) keyDown(Key(0,Key::Insert,true,false,false,false));
01143         return true;
01144 }
01145 
01146 bool Input::mouseDragged(int x, int y, MouseButton button)
01147 {
01148         if (button == Left) {
01149                 end_sel = pos = findPos(x,y);
01150                 blink = true;
01151                 checkOffset();
01152         }
01153         return true;
01154 }
01155 
01156 bool Input::keyDown(const Key &key)
01157 {
01158         const Font *f = Font::getFont("input");
01159         switch (key.special) {
01160         case Key::None:
01161                 if (key.ctrl) {
01162                         switch (key.character) {
01163                         case 1:
01164                         case 'a':
01165                         case 'A':
01166                                 if (key.shift) {
01167                                         start_sel = end_sel = pos;
01168                                 } else {
01169                                         start_sel = 0;
01170                                         pos = end_sel = (Size)text.size();
01171                                 }
01172                                 break;
01173                         case 24:
01174                         case 'x':
01175                         case 'X':
01176                                 cutSelection();
01177                                 break;
01178                         case 3:
01179                         case 'c':
01180                         case 'C':
01181                                 copySelection();
01182                                 break;
01183                         case 22:
01184                         case 'v':
01185                         case 'V':
01186                                 pasteSelection();
01187                                 break;
01188                         default: printf("Ctrl-0x%x\n",key.character); break;
01189                         }
01190                         break;
01191                 }
01192                 if (start_sel != end_sel) clearSelection();
01193                 if (insert || pos >= text.size() ) text.insert(text.begin()+int(pos++),key.character);
01194                 else text[pos++] = key.character;
01195                 break;
01196         case Key::Left:
01197                 if (pos > 0) pos--;
01198                 break;
01199         case Key::Right:
01200                 if (pos < text.size()) pos++;
01201                 break;
01202         case Key::Down:
01203                 if (multi) pos = findPos(posx+3, posy-offset+f->getHeight()+4);
01204         else return false;
01205                 break;
01206         case Key::Up:
01207                 if (multi) pos = findPos(posx+3, posy-offset-f->getHeight()+4);
01208         else return false;
01209                 break;
01210         case Key::Home:
01211                 if (multi) {
01212                         while (pos > 0 && f->toSpecial(text[pos-1]) != Font::LF) pos--;
01213                 } else pos = 0;
01214                 break;
01215         case Key::End:
01216                 if (multi) {
01217                         while (pos < text.size() && f->toSpecial(text[pos]) != Font::LF) pos++;
01218                 } else pos = (Size)text.size();
01219                 break;
01220         case Key::Backspace:
01221                 if (!key.shift && start_sel != end_sel) clearSelection();
01222                 else if (pos > 0) text.erase(text.begin()+int(--pos));
01223                 break;
01224         case Key::Delete:
01225                 if (key.shift) cutSelection();
01226                 else if (start_sel != end_sel) clearSelection();
01227                 else if (pos < text.size()) text.erase(text.begin()+int(pos));
01228                 break;
01229         case Key::Insert:
01230                 if (key.ctrl) copySelection();
01231                 else if (key.shift) pasteSelection();
01232                 else insert = !insert;
01233                 break;
01234         case Key::Enter:
01235                 if (multi) {
01236                         if (start_sel != end_sel) clearSelection();
01237                         if (insert || pos >= text.size() ) text.insert(text.begin()+int(pos++),f->fromSpecial(Font::LF));
01238                         else text[pos++] = f->fromSpecial(Font::LF);
01239                 } else executeAction(text);
01240                 break;
01241         case Key::Tab:
01242                 if (multi && enable_tab_input) {
01243                         if (start_sel != end_sel) clearSelection();
01244                         if (insert || pos >= text.size() ) text.insert(text.begin()+int(pos++),f->fromSpecial(Font::Tab));
01245                         else text[pos++] = f->fromSpecial(Font::Tab);
01246                 } else return false;
01247                 break;
01248         default:
01249                 return false;
01250         }
01251         if (!key.ctrl) {
01252                 if (!key.shift || key.special == Key::None) start_sel = end_sel = pos;
01253                 else end_sel = pos;
01254         }
01255         checkOffset();
01256         blink = true;
01257         return true;
01258 }
01259 
01260 void BorderedWindow::paintAll(Drawable &d) const
01261 {
01262         this->paint(d);
01263         Drawable dchild(d,border_left,border_top,width-border_left-border_right,height-border_top-border_bottom);
01264         for (std::list<Window *>::const_iterator i = children.begin(); i != children.end(); ++i) {
01265                 Window *child = *i;
01266                 if (child->isVisible()) {
01267                         Drawable cd(dchild,child->getX(),child->getY(),child->getWidth(),child->getHeight());
01268                         child->paintAll(cd);
01269                 }
01270         }
01271 }
01272 
01273 void Button::paint(Drawable &d) const
01274 {
01275         int offset = -1;
01276 
01277         if (hasFocus()) {
01278                 offset = 0;
01279                 d.setColor(Color::Border);
01280                 d.drawLine(0,0,width,0);
01281                 d.drawLine(0,0,0,height);
01282 
01283                 d.drawLine(0,height-1,width,height-1);
01284                 d.drawLine(width-1,0,width-1,height);
01285         }
01286 
01287         d.setColor(Color::Background3D);
01288         d.fillRect(2,2,width-4,height-4);
01289 
01290         if (pressed) {
01291                 d.setColor(Color::Shadow3D);
01292 
01293                 d.drawLine(1+offset,1+offset,width-2-offset,1+offset);
01294                 d.drawLine(1+offset,1+offset,1+offset,height-2-offset);
01295         } else {
01296                 d.setColor(Color::Background3D);
01297 
01298                 d.drawLine(1+offset,1+offset,width-3-offset,1+offset);
01299                 d.drawLine(1+offset,1+offset,1+offset,height-3-offset);
01300 
01301                 d.setColor(Color::Light3D);
01302 
01303                 d.drawLine(2+offset,2+offset,width-4-offset,2+offset);
01304                 d.drawLine(2+offset,2+offset,2+offset,height-4-offset);
01305 
01306                 d.setColor(Color::Shadow3D);
01307 
01308                 d.drawLine(2+offset,height-3-offset,width-2-offset,height-3-offset);
01309                 d.drawLine(width-3-offset,2+offset,width-3-offset,height-2-offset);
01310 
01311                 d.setColor(Color::Border);
01312 
01313                 d.drawLine(width-2-offset,1+offset,width-2-offset,height-2-offset);
01314                 d.drawLine(1+offset,height-2-offset,width-2-offset,height-2-offset);
01315         }
01316 }
01317 
01318 bool Checkbox::keyDown(const Key &key)
01319 {
01320         switch (key.special) {
01321         case Key::None:
01322                 if (key.character != ' ') return false;
01323         case Key::Enter:
01324                 break;
01325         default: return false;
01326         }
01327         mouseDown(0,0,Left);
01328         return true;
01329 }
01330 
01331 bool Checkbox::keyUp(const Key &key)
01332 {
01333         if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
01334         mouseUp(0,0,Left);
01335         mouseClicked(0,0,Left);
01336         return true;
01337 }
01338 
01339 void Checkbox::paint(Drawable &d) const
01340 {
01341         d.setColor(Color::Background3D);
01342         d.fillRect(2,(height/2)-7,14,14);
01343 
01344         d.setColor(Color::Shadow3D);
01345         d.drawLine(2,(height/2)-7,13,(height/2)-7);
01346         d.drawLine(2,(height/2)-7,2,(height/2)+5);
01347 
01348         d.setColor(Color::Light3D);
01349         d.drawLine(2,(height/2)+5,14,(height/2)+5);
01350         d.drawLine(14,(height/2)-7,14,(height/2)+5);
01351 
01352         d.setColor(Color::EditableBackground);
01353         d.fillRect(4,(height/2)-5,9,9);
01354 
01355         d.setColor(Color::Border);
01356         d.drawLine(3,(height/2)-6,12,(height/2)-6);
01357         d.drawLine(3,(height/2)-6,3,(height/2)+4);
01358 
01359     if (hasFocus()) {
01360         d.setColor(Color::Black);
01361         d.drawDotRect(1,(height/2)-8,14,14);
01362     }
01363 
01364         if (checked) {
01365                 d.setColor(Color::Text);
01366                 d.drawLine(5,(height/2)-2,7,(height/2)  );
01367                 d.drawLine(11,(height/2)-4);
01368                 d.drawLine(5,(height/2)-1,7,(height/2)+1);
01369                 d.drawLine(11,(height/2)-3);
01370                 d.drawLine(5,(height/2)  ,7,(height/2)+2);
01371                 d.drawLine(11,(height/2)-2);
01372         }
01373 }
01374 
01375 Radiobox::Radiobox(Frame *parent, int x, int y, int w, int h) : BorderedWindow(static_cast<Window *>(parent),x,y,w,h,16,0,0,0), ActionEventSource("GUI::Radiobox"), checked(0)
01376 {
01377          addActionHandler(parent);
01378 }
01379 
01380 bool Radiobox::keyDown(const Key &key)
01381 {
01382         switch (key.special) {
01383         case Key::None:
01384                 if (key.character != ' ') return false;
01385         case Key::Enter:
01386                 break;
01387         default: return false;
01388         }
01389         mouseDown(0,0,Left);
01390         return true;
01391 }
01392 
01393 bool Radiobox::keyUp(const Key &key)
01394 {
01395         if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
01396         mouseUp(0,0,Left);
01397         mouseClicked(0,0,Left);
01398         return true;
01399 }
01400 
01401 void Radiobox::paint(Drawable &d) const
01402 {
01403         d.setColor(Color::Light3D);
01404         d.drawLine(6,(height/2)+6,9,(height/2)+6);
01405         d.drawLine(4,(height/2)+5,11,(height/2)+5);
01406         d.drawLine(13,(height/2)-1,13,(height/2)+2);
01407         d.drawLine(12,(height/2)-2,12,(height/2)+4);
01408 
01409         d.setColor(Color::Background3D);
01410         d.drawLine(6,(height/2)+5,9,(height/2)+5);
01411         d.drawLine(4,(height/2)+4,11,(height/2)+4);
01412         d.drawLine(12,(height/2)-1,12,(height/2)+2);
01413         d.drawLine(11,(height/2)-2,11,(height/2)+4);
01414 
01415         d.setColor(Color::Shadow3D);
01416         d.drawLine(6,(height/2)-5,9,(height/2)-5);
01417         d.drawLine(4,(height/2)-4,11,(height/2)-4);
01418         d.drawLine(2,(height/2)-1,2,(height/2)+2);
01419         d.drawLine(3,(height/2)-3,3,(height/2)+4);
01420 
01421         d.setColor(Color::Border);
01422         d.drawLine(6,(height/2)-4,9,(height/2)-4);
01423         d.drawLine(4,(height/2)-3,11,(height/2)-3);
01424         d.drawLine(3,(height/2)-1,3,(height/2)+2);
01425         d.drawLine(4,(height/2)-3,4,(height/2)+3);
01426 
01427         d.setColor(Color::EditableBackground);
01428         d.fillRect(5,(height/2)-2,6,6);
01429         d.fillRect(4,(height/2)-1,8,4);
01430         d.fillRect(6,(height/2)-3,4,8);
01431 
01432         if (checked) {
01433                 d.setColor(Color::Text);
01434                 d.fillRect(6,(height/2),4,2);
01435                 d.fillRect(7,(height/2)-1,2,4);
01436         }
01437 }
01438 
01439 void Menu::paint(Drawable &d) const
01440 {
01441         d.clear(Color::Background3D);
01442 
01443         d.setColor(Color::Border);
01444         d.drawLine(0,height-1,width-1,height-1);
01445         d.drawLine(width-1,0,width-1,height-1);
01446 
01447         d.setColor(Color::Shadow3D);
01448         d.drawLine(0,0,width-2,0);
01449         d.drawLine(0,0,0,height-2);
01450         d.drawLine(0,height-2,width-2,height-2);
01451         d.drawLine(width-2,0,width-2,height-2);
01452 
01453         d.setColor(Color::Light3D);
01454         d.drawLine(1,1,width-3,1);
01455         d.drawLine(1,1,1,height-3);
01456 
01457         d.setFont(Font::getFont("menu"));
01458         const int asc = Font::getFont("menu")->getAscent()+1;
01459         const int height = Font::getFont("menu")->getHeight()+2;
01460     int x = 3,cwidth = width-3-x;
01461         int y = asc+3;
01462         int index = 0;
01463     unsigned int coli = 0;
01464 
01465     if (coli < colx.size()) {
01466         x = colx[coli++];
01467         cwidth = width-3-x;
01468         if (coli < colx.size()) {
01469             cwidth = colx[coli] - x;
01470         }
01471     }
01472 
01473         for (std::vector<String>::const_iterator i = items.begin(); i != items.end(); ++i) {
01474                 if ((*i).empty()) {
01475                         d.setColor(Color::Shadow3D);
01476                         d.drawLine(x+1,y-asc+6,cwidth,y-asc+6);
01477                         d.setColor(Color::Light3D);
01478                         d.drawLine(x+1,y-asc+7,cwidth,y-asc+7);
01479                         y += 12;
01480         } else if (*i == "|") {
01481             y = asc+3;
01482             if (coli < colx.size()) {
01483                 x = colx[coli++];
01484                 cwidth = width-3-x;
01485                 if (coli < colx.size()) {
01486                     cwidth = colx[coli] - x;
01487                 }
01488 
01489                 d.setColor(Color::Shadow3D);
01490                 d.drawLine(x-2,2,x-2,this->height-4);
01491                 d.setColor(Color::Light3D);
01492                 d.drawLine(x-1,2,x-1,this->height-4);
01493             }
01494         } else {
01495                         if (index == selected && hasFocus()) {
01496                                 d.setColor(Color::SelectionBackground);
01497                                 d.fillRect(x,y-asc,cwidth,height);
01498                                 d.setColor(Color::SelectionForeground);
01499                         } else {
01500                                 d.setColor(Color::Text);
01501                         }
01502                         d.drawText(x+17,y,(*i),false,0);
01503                         y += height;
01504                 }
01505                 index++;
01506         }
01507 }
01508 
01509 void Menubar::paint(Drawable &d) const
01510 {
01511         const Font *f = Font::getFont("menu");
01512 
01513         d.setColor(Color::Light3D);
01514         d.drawLine(0,height-1,width-1,height-1);
01515         d.setColor(Color::Shadow3D);
01516         d.drawLine(0,height-2,width-1,height-2);
01517 
01518         d.gotoXY(7,f->getAscent()+2);
01519 
01520         int index = 0;
01521         for (std::vector<Menu*>::const_iterator i = menus.begin(); i != menus.end(); ++i, ++index) {
01522                 if (index == selected && (*i)->isVisible()) {
01523                         int w = f->getWidth((*i)->getName());
01524                         d.setColor(Color::SelectionBackground);
01525                         d.fillRect(d.getX()-7,0,w+14,height-2);
01526                         d.setColor(Color::SelectionForeground);
01527                         d.gotoXY(d.getX()+7,f->getAscent()+2);
01528                 } else {
01529                         d.setColor(Color::Text);
01530                 }
01531                 d.drawText((*i)->getName(),false);
01532                 d.gotoXY(d.getX()+14,f->getAscent()+2);
01533         }
01534 }
01535 
01536 bool Button::keyDown(const Key &key)
01537 {
01538         switch (key.special) {
01539         case Key::None:
01540                 if (key.character != ' ') return false;
01541         case Key::Enter:
01542                 break;
01543         default: return false;
01544         }
01545         mouseDown(0,0,Left);
01546         return true;
01547 }
01548 
01549 bool Button::keyUp(const Key &key)
01550 {
01551         if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
01552         mouseUp(0,0,Left);
01553         mouseClicked(0,0,Left);
01554         return true;
01555 }
01556 
01557 void Frame::paint(Drawable &d) const {
01558         const Font *f = Font::getFont("default");
01559         const int top = (label.empty()?1:f->getAscent()/2+1);
01560 
01561         d.setColor(Color::Shadow3D);
01562         d.drawLine(1,height-2,1,top);
01563         d.drawLine(8,top);
01564         d.drawLine((label.empty()?8:f->getWidth(label)+14),top,width-2,top);
01565         d.drawLine(2,height-3,width-3,height-3);
01566         d.drawLine(width-3,top+1);
01567         
01568         d.setColor(Color::Light3D);
01569         d.drawLine(2,height-3,2,top+1);
01570         d.drawLine(8,top+1);
01571         d.drawLine((label.empty()?8:f->getWidth(label)+14),top+1,width-3,top+1);
01572         d.drawLine(2,height-2,width-2,height-2);
01573         d.drawLine(width-2,top+1);
01574 
01575         d.setColor(Color::Text);
01576         d.drawText(11,f->getAscent()+1,label,false,0);
01577 }
01578 
01579 Screen *Window::getScreen() { return (parent == NULL?dynamic_cast<Screen*>(this):parent->getScreen()); }
01580 
01581 Screen::Screen(unsigned int width, unsigned int height) :
01582         Window(),
01583         buffer(new Drawable((int)width, (int)height)),
01584     buffer_i_alloc(true)
01585 {
01586         this->width = (int)width;
01587         this->height = (int)height;
01588 }
01589 
01590 Screen::Screen(Drawable *d) :
01591         Window(),
01592         buffer(d),
01593     buffer_i_alloc(true) /* our primary use is from ScreenSDL that calls new */
01594 {
01595         this->width = d->getClipWidth();
01596         this->height = d->getClipHeight();
01597 }
01598 
01599 Screen::~Screen()
01600 {
01601     if (buffer_i_alloc) delete buffer;
01602 }
01603 
01604 void Screen::paint(Drawable &d) const
01605 {
01606         d.clear(Color::Transparent);
01607 }
01608 
01609 unsigned int Screen::update(void *surface, unsigned int ticks)
01610 {
01611     (void)ticks;//UNUSED
01612         paintAll(*buffer);
01613         RGB *buf = buffer->buffer;
01614         for (y = 0; y < height; y++) {
01615                 for (x = 0; x < width; x++, buf++) {
01616                         RGB sval = surfaceToRGB(surface);
01617                         RGB bval = *buf;
01618                         unsigned int a = Color::A(bval);
01619                         bval = ((((sval&Color::MagentaMask)*a+(bval&Color::MagentaMask)*(256-a))>>8)&Color::MagentaMask)
01620                                 | ((((sval&Color::GreenMask)*a+(bval&Color::GreenMask)*(256-a))>>8)&Color::GreenMask);
01621                         rgbToSurface(bval, &surface);
01622                 }
01623         }
01624 
01625         return Timer::next();
01626 }
01627 
01628 void Screen::move(int x, int y)
01629 {
01630     (void)x;//UNUSED
01631     (void)y;//UNUSED
01632 }
01633 
01634 void Screen::resize(int w, int h)
01635 {
01636     (void)w;//UNUSED
01637     (void)h;//UNUSED
01638 }
01639 
01640 #ifdef TESTING
01641 static void test(Drawable &d) {
01642         const int width = d.getClipWidth();
01643         const int height = d.getClipHeight();
01644 
01645         d.clear(Color::rgba(0,0,255,128));
01646 
01647         d.setColor(Color::Black);
01648         for (int x = 0; x < width; x += 10) d.drawLine(x,0,x,height);
01649         for (int y = 0; y < height; y += 10) d.drawLine(0,y,width,y);
01650 
01651         d.setColor(Color::Red);
01652         for (int x = 10; x <= 130 ; x += 10) {
01653                 d.drawLine(x,10,70,70);
01654                 d.drawLine(x,130);
01655         }
01656         for (int y = 10; y <= 130 ; y += 10) {
01657                 d.drawLine(10,y,70,70);
01658                 d.drawLine(130,y);
01659         }
01660 
01661         d.setColor(Color::Yellow);
01662         d.fillRect(150,10,30,30);
01663         d.setColor(Color::Blue);
01664         d.drawRect(30,30);
01665 
01666         d.drawRect(150,60,30,30);
01667         d.setColor(Color::Yellow);
01668         d.fillRect(30,30);
01669 
01670         for (int x = 0; x <= 100 ; x += 10) {
01671                 d.setColor(Color::rgba(0xff,0x00,0xff,(255*x/100)&255));
01672                 d.fillRect(200+2*x,0,20,20);
01673         }
01674 
01675         d.setColor(Color::Yellow);
01676         d.fillCircle(210,60,40);
01677         d.setColor(Color::Blue);
01678         d.drawCircle(40);
01679 
01680         d.drawCircle(210,110,40);
01681         d.setColor(Color::Yellow);
01682         d.fillCircle(40);
01683 
01684         d.setColor(Color::rgba(0,0,255,128));
01685         d.fillRect(251,41,9,59);
01686         d.fillRect(251,41,59,9);
01687         d.fillRect(301,41,9,59);
01688         d.fillRect(291,91,19,9);
01689         d.fillRect(291,51,9,49);
01690         d.fillRect(261,51,39,9);
01691         d.fillRect(261,51,9,49);
01692         d.fillRect(261,91,29,9);
01693         d.fillRect(281,61,9,39);
01694         d.fillRect(271,61,19,9);
01695         d.fillRect(271,61,9,29);
01696         d.fillRect(241,41,9,59);
01697         d.fillRect(241,41,19,9);
01698         d.fillRect(241,91,19,9);
01699         d.setColor(Color::rgba(255,0,0,128));
01700         d.fill(255,64);
01701 
01702         d.setColor(Color::Green);
01703         Drawable(d,500,355,30,30).fillCircle(65);
01704 
01705         for (int i = 0; i <= 100; i += 10) {
01706                 Drawable(d,25,155+i*3,420,30).drawDrawable(0,0,d,255*i/100);
01707         }
01708 
01709         d.setColor(Color::White);
01710         d.setFont(Font::getFont("VGA14"));
01711         d.drawText(270,110,"GUI:: Test Program\n");
01712         d.drawText("Still testing\tTable\n");
01713         d.drawText("More of...\tTable\n");
01714         d.drawText("Overwrite\rXXXXXXXXX\n");
01715         d.drawText("Fake int'l chars: O\b/e\b\"\n");
01716         d.drawText("Real ones: \211\222\234\345\246\321");
01717 }
01718 #else
01719 static void test(Drawable &d) { (void)d; }
01720 #endif
01721 
01722 
01723 void ScreenRGB32le::paint(Drawable &d) const
01724 {
01725         parent->paint(d);
01726         test(d);
01727 }
01728 
01729 static MouseButton SDL_to_GUI(const int button)
01730 {
01731         switch (button) {
01732         case SDL_BUTTON_LEFT:      return GUI::Left;
01733         case SDL_BUTTON_RIGHT:     return GUI::Right;
01734         case SDL_BUTTON_MIDDLE:    return GUI::Middle;
01735 #if !defined(C_SDL2)
01736         case SDL_BUTTON_WHEELUP:   return GUI::WheelUp;
01737         case SDL_BUTTON_WHEELDOWN: return GUI::WheelDown;
01738 #endif /* !C_SDL2 */
01739         default: return GUI::NoButton;
01740         }
01741 }
01742 
01743 #if defined(C_SDL2)
01744 static GUI::Char SDLSymToChar(const SDL_Keysym &key) {
01745     /* SDL will not uppercase the char for us with shift, etc. */
01746     /* Additionally we have to filter out non-char values */
01747     if (key.sym == 0 || key.sym > 0x7f) return 0;
01748 
01749     GUI::Char ret = key.sym;
01750 
01751     if (key.mod & KMOD_SHIFT) {
01752         switch (ret) {
01753             case '[':
01754                 ret = (GUI::Char)('{');
01755                 break;
01756             case ']':
01757                 ret = (GUI::Char)('}');
01758                 break;
01759             case '\\':
01760                 ret = (GUI::Char)('|');
01761                 break;
01762             case ';':
01763                 ret = (GUI::Char)(':');
01764                 break;
01765             case '\'':
01766                 ret = (GUI::Char)('"');
01767                 break;
01768             case ',':
01769                 ret = (GUI::Char)('<');
01770                 break;
01771             case '.':
01772                 ret = (GUI::Char)('>');
01773                 break;
01774             case '/':
01775                 ret = (GUI::Char)('?');
01776                 break;
01777             case '-':
01778                 ret = (GUI::Char)('_');
01779                 break;
01780             case '=':
01781                 ret = (GUI::Char)('+');
01782                 break;
01783             case '1':
01784                 ret = (GUI::Char)('!');
01785                 break;
01786             case '2':
01787                 ret = (GUI::Char)('@');
01788                 break;
01789             case '3':
01790                 ret = (GUI::Char)('#');
01791                 break;
01792             case '4':
01793                 ret = (GUI::Char)('$');
01794                 break;
01795             case '5':
01796                 ret = (GUI::Char)('%');
01797                 break;
01798             case '6':
01799                 ret = (GUI::Char)('^');
01800                 break;
01801             case '7':
01802                 ret = (GUI::Char)('&');
01803                 break;
01804             case '8':
01805                 ret = (GUI::Char)('*');
01806                 break;
01807             case '9':
01808                 ret = (GUI::Char)('(');
01809                 break;
01810             case '0':
01811                 ret = (GUI::Char)(')');
01812                 break;
01813             default:
01814                 ret = (GUI::Char)toupper((int)ret);
01815                 break;
01816         }
01817     }
01818 
01819     return ret;
01820 }
01821 #endif
01822 
01823 #if defined(C_SDL2)
01824 static const Key SDL_to_GUI(const SDL_Keysym &key)
01825 #else
01826 static const Key SDL_to_GUI(const SDL_keysym &key)
01827 #endif
01828 {
01829         GUI::Key::Special ksym = GUI::Key::None;
01830         switch (key.sym) {
01831 #if !defined(C_SDL2) /* hack for SDL1 that fails to send char code for spacebar up event */
01832     case SDLK_SPACE:
01833         return Key(' ', ksym,
01834                 (key.mod&KMOD_SHIFT)>0,
01835                 (key.mod&KMOD_CTRL)>0,
01836                 (key.mod&KMOD_ALT)>0,
01837                 (key.mod&KMOD_META)>0);
01838 #endif
01839         case SDLK_ESCAPE: ksym = GUI::Key::Escape; break;
01840         case SDLK_BACKSPACE: ksym = GUI::Key::Backspace; break;
01841         case SDLK_TAB: ksym = GUI::Key::Tab; break;
01842         case SDLK_LEFT: ksym = GUI::Key::Left; break;
01843         case SDLK_RIGHT: ksym = GUI::Key::Right; break;
01844         case SDLK_UP: ksym = GUI::Key::Up; break;
01845         case SDLK_DOWN: ksym = GUI::Key::Down; break;
01846         case SDLK_HOME: ksym = GUI::Key::Home; break;
01847         case SDLK_END: ksym = GUI::Key::End; break;
01848         case SDLK_DELETE: ksym = GUI::Key::Delete; break;
01849         case SDLK_INSERT: ksym = GUI::Key::Insert; break;
01850         case SDLK_RETURN: ksym = GUI::Key::Enter; break;
01851         case SDLK_MENU: ksym = GUI::Key::Menu; break;
01852         case SDLK_PAGEUP: ksym = GUI::Key::PageUp; break;
01853         case SDLK_PAGEDOWN: ksym = GUI::Key::PageDown; break;
01854 #if !defined(C_SDL2)
01855         case SDLK_PRINT: ksym = GUI::Key::Print; break;
01856 #endif
01857         case SDLK_PAUSE: ksym = GUI::Key::Pause; break;
01858 #if !defined(C_SDL2)
01859         case SDLK_BREAK: ksym = GUI::Key::Break; break;
01860 #endif
01861         case SDLK_CAPSLOCK: ksym = GUI::Key::CapsLock; break;
01862 #if !defined(C_SDL2)
01863         case SDLK_NUMLOCK: ksym = GUI::Key::NumLock; break;
01864         case SDLK_SCROLLOCK: ksym = GUI::Key::ScrollLock; break;
01865 #endif
01866         case SDLK_F1:case SDLK_F2:case SDLK_F3:case SDLK_F4:case SDLK_F5:case SDLK_F6:
01867         case SDLK_F7:case SDLK_F8:case SDLK_F9:case SDLK_F10:case SDLK_F11:case SDLK_F12:
01868                 ksym = (GUI::Key::Special)(GUI::Key::F1 + key.sym-SDLK_F1);
01869     /* do not provide a character code for these keys */
01870     case SDLK_LSHIFT: case SDLK_RSHIFT:
01871     case SDLK_LCTRL:  case SDLK_RCTRL:
01872     case SDLK_LALT:   case SDLK_RALT:
01873 #if defined(C_SDL2)
01874         return Key(0, ksym,
01875                 (key.mod&KMOD_SHIFT)>0,
01876                 (key.mod&KMOD_CTRL)>0,
01877                 (key.mod&KMOD_ALT)>0,
01878             false);
01879 #else
01880         return Key(0, ksym,
01881                 (key.mod&KMOD_SHIFT)>0,
01882                 (key.mod&KMOD_CTRL)>0,
01883                 (key.mod&KMOD_ALT)>0,
01884                 (key.mod&KMOD_META)>0);
01885 #endif
01886     /* anything else, go ahead */
01887         default:
01888         break;
01889         }
01890 #if defined(C_SDL2)
01891         return Key(SDLSymToChar(key), ksym,
01892                 (key.mod&KMOD_SHIFT)>0,
01893                 (key.mod&KMOD_CTRL)>0,
01894                 (key.mod&KMOD_ALT)>0,
01895         false);
01896 #else
01897         return Key(key.unicode, ksym,
01898                 (key.mod&KMOD_SHIFT)>0,
01899                 (key.mod&KMOD_CTRL)>0,
01900                 (key.mod&KMOD_ALT)>0,
01901                 (key.mod&KMOD_META)>0);
01902 #endif
01903 }
01904 
01906 class SDL_Drawable : public Drawable {
01907 protected:
01908         SDL_Surface *const surface;
01909 
01910 public:
01911         SDL_Drawable(int width, int height, RGB clear = Color::Transparent) : Drawable(width, height, clear), surface(SDL_CreateRGBSurfaceFrom(buffer, width, height, 32, width*4, Color::RedMask, Color::GreenMask, Color::BlueMask, Color::AlphaMask)) {
01912 #if !defined(C_SDL2)
01913             surface->flags |= SDL_SRCALPHA;
01914 #endif
01915         }
01916 
01917         ~SDL_Drawable() {
01918             SDL_FreeSurface(surface);
01919         }
01920 
01921         void update(SDL_Surface *dest) const {
01922             SDL_BlitSurface(surface, NULL, dest, NULL);
01923         }
01924 };
01925 
01926 ScreenSDL::~ScreenSDL() {
01927 }
01928 
01929 ScreenSDL::ScreenSDL(SDL_Surface *surface) : Screen(new SDL_Drawable(surface->w, surface->h)), surface(surface), downx(0), downy(0), lastclick(0), lastdown(0) {
01930         current_abs_time = start_abs_time = SDL_GetTicks();
01931         current_time = 0;
01932 }
01933 
01934 Ticks ScreenSDL::update(Ticks ticks)
01935 {
01936         Timer::check_to(ticks);
01937         paintAll(*buffer);
01938         static_cast<SDL_Drawable*>(buffer)->update(surface);
01939 
01940         return Timer::next();
01941 }
01942 
01943 void ScreenSDL::watchTime() {
01944         current_abs_time = SDL_GetTicks();
01945         current_time = current_abs_time - start_abs_time;
01946 }
01947 
01948 void ScreenSDL::paint(Drawable &d) const {
01949         d.clear(Color::Transparent);
01950         test(d);
01951 }
01952 
01953 bool ScreenSDL::event(const SDL_Event &event) {
01954         bool rc;
01955 
01956 #if defined(C_SDL2)
01957     /* handle mouse events only if it comes from the mouse.
01958      * ignore the fake mouse events some OSes generate from the touchscreen.
01959      * Note that Windows will fake mouse events, Linux/X11 wil not */
01960     if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
01961         if (event.button.which == SDL_TOUCH_MOUSEID) /* don't handle mouse events faked by touchscreen */
01962             return true;/*eat the event or else it will just keep calling objects until processed*/
01963     }
01964 #endif
01965 
01966         switch (event.type) {
01967 #if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
01968     case SDL_FINGERUP:
01969     case SDL_FINGERDOWN:
01970     case SDL_FINGERMOTION: {
01971         SDL_Event fake;
01972         bool comb = false;
01973 
01974         memset(&fake,0,sizeof(fake));
01975         fake.type = SDL_MOUSEMOTION;
01976         fake.motion.state = SDL_BUTTON(1);
01977         fake.motion.x = (Sint32)(event.tfinger.x * surface->w);
01978         fake.motion.y = (Sint32)(event.tfinger.y * surface->h);
01979         fake.motion.xrel = (Sint32)event.tfinger.dx;
01980         fake.motion.yrel = (Sint32)event.tfinger.dy;
01981         comb |= this->event(fake);
01982 
01983         if (event.type == SDL_FINGERUP || event.type == SDL_FINGERDOWN) {
01984             memset(&fake,0,sizeof(fake));
01985             fake.button.button = SDL_BUTTON_LEFT;
01986             fake.button.x = (Sint32)(event.tfinger.x * surface->w);
01987             fake.button.y = (Sint32)(event.tfinger.y * surface->h);
01988             fake.type = (event.type == SDL_FINGERUP) ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
01989             comb |= this->event(fake);
01990         }
01991 
01992         return comb;
01993     }
01994 #endif
01995         case SDL_KEYUP: {
01996                 const Key &key = SDL_to_GUI(event.key.keysym);
01997                 if (key.special == GUI::Key::None && key.character == 0) break;
01998                 if (key.special == GUI::Key::CapsLock || key.special == GUI::Key::NumLock) keyDown(key);
01999                 return keyUp(key);
02000         }
02001         case SDL_KEYDOWN: {
02002                 const Key &key = SDL_to_GUI(event.key.keysym);
02003                 if (key.special == GUI::Key::None && key.character == 0) break;
02004                 rc = keyDown(key);
02005                 if (key.special == GUI::Key::CapsLock || key.special == GUI::Key::NumLock) keyUp(key);
02006                 return rc;
02007         }
02008         case SDL_MOUSEMOTION:
02009                 if (event.motion.state) {
02010                         if (abs(event.motion.x-downx) <= 10 && abs(event.motion.y-downy) <= 10)
02011                                 break;
02012                         downx = -11; downy = -11;
02013                         if (event.motion.state&SDL_BUTTON(1))
02014                                 return mouseDragged(event.motion.x, event.motion.y, GUI::Left);
02015                         else if (event.motion.state&SDL_BUTTON(2))
02016                                 return mouseDragged(event.motion.x, event.motion.y, GUI::Middle);
02017                         else if (event.motion.state&SDL_BUTTON(3))
02018                                 return mouseDragged(event.motion.x, event.motion.y, GUI::Right);
02019                         break;
02020                 }
02021 
02022                 return mouseMoved(event.motion.x, event.motion.y);
02023 
02024         case SDL_MOUSEBUTTONDOWN:
02025                 rc = mouseDown(event.button.x, event.button.y, SDL_to_GUI(event.button.button));
02026                 if (abs(event.button.x-downx) > 10 || abs(event.button.y-downy) > 10) lastclick = 0;
02027                 downx = event.button.x; downy = event.button.y;
02028                 lastdown = GUI::Timer::now();
02029                 return rc;
02030 
02031         case SDL_MOUSEBUTTONUP:
02032                 rc = mouseUp(event.button.x, event.button.y, SDL_to_GUI(event.button.button));
02033                 if (lastdown != 0 && abs(event.button.x-downx) < 10 && abs(event.button.y-downy) < 10) {
02034                         if (lastclick == 0 || (GUI::Timer::now()-lastclick) > 200) {
02035                                 lastclick = GUI::Timer::now();
02036                                 rc |= mouseClicked(downx, downy, SDL_to_GUI(event.button.button));
02037                         } else if (lastclick != 0) {
02038                                 rc |= mouseDoubleClicked(downx, downy, SDL_to_GUI(event.button.button));
02039                                 lastclick = 0;
02040                         } else {
02041                                 lastclick = 0;
02042                         }
02043                 } else {
02044                         lastclick = 0;
02045                 }
02046                 lastdown = 0;
02047                 return rc;
02048         }
02049 
02050         return false;
02051 }
02052 
02053 void WindowInWindow::paintAll(Drawable &d) const {
02054     int xadj = -scroll_pos_x;
02055     int yadj = -scroll_pos_y;
02056 
02057     if (border) {
02058         xadj++;
02059         yadj++;
02060     }
02061 
02062         Drawable dchild(d,0,0,width,height);
02063         for (std::list<Window *>::const_iterator i = children.begin(); i != children.end(); ++i) {
02064                 Window *child = *i;
02065                 if (child->isVisible()) {
02066                         Drawable cd(dchild,child->getX() + xadj,child->getY() + yadj,child->getWidth(),child->getHeight());
02067                         child->paintAll(cd);
02068                 }
02069         }
02070 
02071     if (border) {
02072         int w = width-1;
02073         int h = height-1;
02074 
02075         if (vscroll)
02076             w -= (vscroll?vscroll_display_width:0);
02077 
02078         dchild.setColor(Color::Shadow3D);
02079         dchild.drawLine(0,0,w,0);
02080         dchild.drawLine(0,0,0,h);
02081 
02082         dchild.setColor(Color::Light3D);
02083         dchild.drawLine(0,h,w,h);
02084         dchild.drawLine(w,0,w,h);
02085     }
02086 
02087     if (vscroll && vscroll_display_width >= 4) {
02088         // TODO: Need a vertical scrollbar window object
02089 
02090         Drawable dscroll(d,width - vscroll_display_width,0,vscroll_display_width,height);
02091 
02092         bool disabled = (scroll_pos_h == 0);
02093 
02094         /* scroll bar border, gray background */
02095         dscroll.setColor(disabled ? Color::Shadow3D : Color::Black);
02096         dscroll.drawRect(0,0,vscroll_display_width-1,height-1);
02097 
02098         dscroll.setColor(Color::Background3D);
02099         dscroll.fillRect(1,1,vscroll_display_width-2,height-2);
02100 
02101         /* the "thumb". make it fixed size, Windows 3.1 style.
02102          * this code could adapt to the more range-aware visual style of Windows 95 later. */
02103         int thumbwidth = vscroll_display_width - 2;
02104         int thumbheight = vscroll_display_width - 2;
02105         int thumbtravel = height - 2 - thumbheight;
02106         if (thumbtravel < 0) thumbtravel = 0;
02107         int ytop = 1 + ((scroll_pos_h > 0) ?
02108             ((thumbtravel * scroll_pos_y) / scroll_pos_h) :
02109             0);
02110 
02111         if (thumbheight <= (height + 2) && !disabled) {
02112             int xleft = 1;
02113             dscroll.setColor(Color::Light3D);
02114             dscroll.drawLine(xleft,ytop,xleft+thumbwidth-1,ytop);
02115             dscroll.drawLine(xleft,ytop,xleft,ytop+thumbheight-1);
02116 
02117             // Windows 3.1 renders the shadow two pixels wide
02118             dscroll.setColor(Color::Shadow3D);
02119             dscroll.drawLine(xleft,ytop+thumbheight-1,xleft+thumbwidth-1,ytop+thumbheight-1);
02120             dscroll.drawLine(xleft+thumbwidth-1,ytop,xleft+thumbwidth-1,ytop+thumbheight-1);
02121 
02122             dscroll.drawLine(xleft+1,ytop+thumbheight-2,xleft+thumbwidth-2,ytop+thumbheight-2);
02123             dscroll.drawLine(xleft+thumbwidth-2,ytop+1,xleft+thumbwidth-2,ytop+thumbheight-2);
02124 
02125             // Windows 3.1 also draws a hard black line around the thumb that can coincide with the border
02126             dscroll.setColor(Color::Black);
02127             dscroll.drawLine(xleft,ytop-1,xleft+thumbwidth-1,ytop-1);
02128             dscroll.drawLine(xleft,ytop+thumbheight,xleft+thumbwidth-1,ytop+thumbheight);
02129 
02130             // Windows 3.1 also draws an inverted dotted rectangle around the thumb where it WOULD be
02131             // before quantization to scroll position.
02132             if (vscroll_dragging) {
02133                 xleft = 0;
02134                 ytop = drag_y - ((thumbheight + 2) / 2);
02135                 if (ytop < 0) ytop = 0;
02136                 if (ytop > thumbtravel) ytop = thumbtravel;
02137                 dscroll.setColor(Color::Light3D);
02138                 dscroll.drawDotRect(xleft,ytop,thumbwidth+1,thumbheight+1);
02139             }
02140         }
02141     }
02142 }
02143 
02144 bool WindowInWindow::mouseDragged(int x, int y, MouseButton button)
02145 {
02146     if (vscroll_dragging) {
02147         int thumbheight = vscroll_display_width - 2;
02148         int thumbtravel = height - 2 - thumbheight;
02149         if (thumbtravel < 0) thumbtravel = 0;
02150 
02151         double npos = (double(y - 1 - (thumbheight / 2)) * scroll_pos_h) / thumbtravel;
02152         int nipos = int(floor(npos + 0.5));
02153         if (nipos < 0) nipos = 0;
02154         if (nipos > scroll_pos_h) nipos = scroll_pos_h;
02155         scroll_pos_y = nipos;
02156 
02157         drag_x = x;
02158         drag_y = y;
02159         return true;
02160     }
02161 
02162     if (dragging) {
02163         scroll_pos_x -= x - drag_x;
02164         scroll_pos_y -= y - drag_y;
02165         if (scroll_pos_x < 0) scroll_pos_x = 0;
02166         if (scroll_pos_y < 0) scroll_pos_y = 0;
02167         if (scroll_pos_x > scroll_pos_w) scroll_pos_x = scroll_pos_w;
02168         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
02169         drag_x = x;
02170         drag_y = y;
02171         return true;
02172     }
02173 
02174     int xadj = -scroll_pos_x;
02175     int yadj = -scroll_pos_y;
02176 
02177     if (border) {
02178         xadj++;
02179         yadj++;
02180     }
02181 
02182     return Window::mouseDragged(x-xadj,y-yadj,button);
02183 }
02184 
02185 bool WindowInWindow::mouseDown(int x, int y, MouseButton button)
02186 {
02187     if (vscroll && x >= (width - vscroll_display_width) && button == GUI::Left) {
02188         mouseChild = this;
02189         vscroll_dragging = true;
02190         drag_x = x;
02191         drag_y = y;
02192 
02193         int thumbheight = vscroll_display_width - 2;
02194         int thumbtravel = height - 2 - thumbheight;
02195         if (thumbtravel < 0) thumbtravel = 0;
02196 
02197         double npos = (double(y - 1 - (thumbheight / 2)) * scroll_pos_h) / thumbtravel;
02198         int nipos = int(floor(npos + 0.5));
02199         if (nipos < 0) nipos = 0;
02200         if (nipos > scroll_pos_h) nipos = scroll_pos_h;
02201         scroll_pos_y = nipos;
02202 
02203         return true;
02204     }
02205     if (mouseChild == NULL && button == GUI::WheelUp) {
02206         scroll_pos_y -= 50;
02207         if (scroll_pos_y < 0) scroll_pos_y = 0;
02208         mouseChild = this;
02209         dragging = true;
02210         return true;
02211     }
02212     if (mouseChild == NULL && button == GUI::WheelDown) {
02213         scroll_pos_y += 50;
02214         if (scroll_pos_y > scroll_pos_h) scroll_pos_y = scroll_pos_h;
02215         mouseChild = this;
02216         dragging = true;
02217         return true;
02218     }
02219 
02220     int xadj = -scroll_pos_x;
02221     int yadj = -scroll_pos_y;
02222 
02223     if (border) {
02224         xadj++;
02225         yadj++;
02226     }
02227 
02228     bool ret = Window::mouseDown(x-xadj,y-yadj,button);
02229 
02230     if (!ret && mouseChild == NULL && button == GUI::Left) {
02231         drag_x = x;
02232         drag_y = y;
02233         mouseChild = this;
02234         dragging = true;
02235         ret = true;
02236     }
02237 
02238     return ret;
02239 }
02240 
02241 bool WindowInWindow::mouseUp(int x, int y, MouseButton button)
02242 {
02243     if (vscroll_dragging) {
02244         int thumbheight = vscroll_display_width - 2;
02245         int thumbtravel = height - 2 - thumbheight;
02246         if (thumbtravel < 0) thumbtravel = 0;
02247 
02248         double npos = (double(y - 1 - (thumbheight / 2)) * scroll_pos_h) / thumbtravel;
02249         int nipos = int(floor(npos + 0.5));
02250         if (nipos < 0) nipos = 0;
02251         if (nipos > scroll_pos_h) nipos = scroll_pos_h;
02252         scroll_pos_y = nipos;
02253 
02254         vscroll_dragging = false;
02255         mouseChild = NULL;
02256         return true;
02257     }
02258 
02259     if (hscroll_dragging) {
02260         hscroll_dragging = false;
02261         mouseChild = NULL;
02262         return true;
02263     }
02264 
02265     if (dragging) {
02266         mouseChild = NULL;
02267         dragging = false;
02268         return true;
02269     }
02270 
02271     int xadj = -scroll_pos_x;
02272     int yadj = -scroll_pos_y;
02273 
02274     if (border) {
02275         xadj++;
02276         yadj++;
02277     }
02278 
02279     return Window::mouseUp(x-xadj,y-xadj,button);
02280 }
02281 
02282 bool WindowInWindow::mouseMoved(int x, int y) {
02283     if (dragging) return true;
02284 
02285     int xadj = -scroll_pos_x;
02286     int yadj = -scroll_pos_y;
02287 
02288     if (border) {
02289         xadj++;
02290         yadj++;
02291     }
02292 
02293     return Window::mouseMoved(x-xadj,y-xadj);
02294 }
02295 
02296 bool WindowInWindow::mouseClicked(int x, int y, MouseButton button) {
02297     if (dragging) return true;
02298 
02299     int xadj = -scroll_pos_x;
02300     int yadj = -scroll_pos_y;
02301 
02302     if (border) {
02303         xadj++;
02304         yadj++;
02305     }
02306 
02307     return Window::mouseClicked(x-xadj,y-xadj,button);
02308 }
02309 
02310 bool WindowInWindow::mouseDoubleClicked(int x, int y, MouseButton button) {
02311     if (dragging) return true;
02312 
02313     int xadj = -scroll_pos_x;
02314     int yadj = -scroll_pos_y;
02315 
02316     if (border) {
02317         xadj++;
02318         yadj++;
02319     }
02320 
02321     return Window::mouseDoubleClicked(x-xadj,y-xadj,button);
02322 }
02323 
02324 void WindowInWindow::resize(int w, int h) {
02325     int mw = 0,mh = 0;
02326     int cmpw = w;
02327     int cmph = h;
02328 
02329     if (vscroll) cmpw -= vscroll_display_width;
02330     if (border) {
02331         cmpw -= 2;
02332         cmph -= 2;
02333     }
02334 
02335         for (std::list<Window *>::const_iterator i = children.begin(); i != children.end(); ++i) {
02336                 Window *child = *i;
02337         int mx = child->getX() + child->getWidth();
02338         int my = child->getY() + child->getHeight();
02339         if (mw < mx) mw = mx;
02340         if (mh < my) mh = my;
02341         }
02342 
02343     mw -= cmpw;
02344     mh -= cmph;
02345     if (mw < 0) mw = 0;
02346     if (mh < 0) mh = 0;
02347     scroll_pos_w = mw;
02348     scroll_pos_h = mh;
02349 
02350     Window::resize(w,h);
02351 }
02352 
02353 void WindowInWindow::enableBorder(bool en) {
02354     if (border != en) {
02355         border = en;
02356         resize(width, height);
02357     }
02358 }
02359 
02360 void WindowInWindow::enableScrollBars(bool hs,bool vs) {
02361     if (hs != hscroll || vs != vscroll) {
02362         hscroll = hs;
02363         vscroll = vs;
02364         resize(width, height);
02365     }
02366 }
02367 
02368 } /* end namespace GUI */