DOSBox-X
|
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 */