1// Frame.C 2// Some modifications by Michael A. Losh, tagged "ML" below, 3// or bracketed by TOPSIDE manifest constant 4// Last update: 2009-09-27 5 6#include "config.h" 7#include "Frame.H" 8#include "Desktop.H" 9#include <string.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <FL/fl_draw.H> 13#include "Rotated.H" 14 15 16static Atom wm_state = 0; 17static Atom wm_change_state; 18static Atom wm_protocols; 19static Atom wm_delete_window; 20static Atom wm_take_focus; 21static Atom wm_save_yourself; 22static Atom wm_colormap_windows; 23static Atom _motif_wm_hints; 24static Atom kwm_win_decoration; 25#if DESKTOPS 26static Atom kwm_win_desktop; 27static Atom kwm_win_sticky; 28#endif 29//static Atom wm_client_leader; 30static Atom _wm_quit_app; 31 32// these are set by initialize in main.C: 33Atom _win_hints; 34Atom _win_state; 35#if DESKTOPS 36extern Atom _win_workspace; 37#endif 38 39#ifdef SHOW_CLOCK 40extern char clock_buf[]; 41extern int clock_alarm_on; 42#endif 43 44static const int XEventMask = 45ExposureMask|StructureNotifyMask 46|KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask 47|ButtonPressMask|ButtonReleaseMask 48|EnterWindowMask|LeaveWindowMask 49|PointerMotionMask|SubstructureRedirectMask|SubstructureNotifyMask; 50 51extern Fl_Window* Root; 52 53Frame* Frame::active_; 54Frame* Frame::first; 55 56static inline int max(int a, int b) {return a > b ? a : b;} 57static inline int min(int a, int b) {return a < b ? a : b;} 58 59//////////////////////////////////////////////////////////////// 60// The constructor is by far the most complex part, as it collects 61// all the scattered pieces of information about the window that 62// X has and uses them to initialize the structure, position the 63// window, and then finally create it. 64 65int dont_set_event_mask = 0; // used by FrameWindow 66 67// "existing" is a pointer to an XWindowAttributes structure that is 68// passed for an already-existing window when the window manager is 69// starting up. If so we don't want to alter the state, size, or 70// position. If null than this is a MapRequest of a new window. 71Frame::Frame(XWindow window, XWindowAttributes* existing) : 72 Fl_Window(0,0), 73 window_(window), 74 state_flags_(0), 75 flags_(0), 76 transient_for_xid(None), 77 transient_for_(0), 78 revert_to(active_), 79 colormapWinCount(0), 80 close_button(BUTTON_LEFT,BUTTON_TOP,BUTTON_W,BUTTON_H,"X"), 81 iconize_button(BUTTON_LEFT,BUTTON_TOP,BUTTON_W,BUTTON_H,"i"), 82 max_h_button(BUTTON_LEFT,BUTTON_TOP+3*BUTTON_H,BUTTON_W,BUTTON_H,"h"), 83 max_w_button(BUTTON_LEFT,BUTTON_TOP+BUTTON_H,BUTTON_W,BUTTON_H,"w"), 84 min_w_button(BUTTON_LEFT,BUTTON_TOP+2*BUTTON_H,BUTTON_W,BUTTON_H,"W") 85{ 86#if FL_MAJOR_VERSION > 1 87 clear_double_buffer(); 88#endif 89 close_button.callback(button_cb_static); 90 iconize_button.callback(button_cb_static); 91 max_h_button.type(FL_TOGGLE_BUTTON); 92 max_h_button.callback(button_cb_static); 93 max_w_button.type(FL_TOGGLE_BUTTON); 94 max_w_button.callback(button_cb_static); 95 min_w_button.type(FL_TOGGLE_BUTTON); 96 min_w_button.callback(button_cb_static); 97 end(); 98 box(FL_NO_BOX); // relies on background color erasing interior 99 // ML ------------------------------- 100#ifdef ML_TITLEBAR_COLOR 101 char * titlebar_color_str = getenv("FLWM_TITLEBAR_COLOR"); 102 int r = 0x90, g = 0x90, b = 0x90; 103 int fields; 104 if (titlebar_color_str) { 105 fields = sscanf(titlebar_color_str, "%02X:%02X:%02X", &r, &g, &b); 106 } 107 if (titlebar_color_str && fields == 3) { 108 Fl::set_color(FL_BACKGROUND2_COLOR, r, g, b); 109 } 110 else { 111 Fl::set_color(FL_BACKGROUND2_COLOR, 0x90, 0x90, 0x90); 112 } 113#else 114 Fl::set_color(FL_BACKGROUND2_COLOR, 0x90, 0x90, 0x90); 115#endif 116 // --------------------------------ML 117 labelcolor(FL_FOREGROUND_COLOR); 118 next = first; 119 first = this; 120 121 // do this asap so we don't miss any events... 122 if (!dont_set_event_mask) 123 XSelectInput(fl_display, window_, 124 ColormapChangeMask | PropertyChangeMask | FocusChangeMask 125 ); 126 127 if (!wm_state) { 128 // allocate all the atoms if this is the first time 129 wm_state = XInternAtom(fl_display, "WM_STATE", 0); 130 wm_change_state = XInternAtom(fl_display, "WM_CHANGE_STATE", 0); 131 wm_protocols = XInternAtom(fl_display, "WM_PROTOCOLS", 0); 132 wm_delete_window = XInternAtom(fl_display, "WM_DELETE_WINDOW", 0); 133 wm_take_focus = XInternAtom(fl_display, "WM_TAKE_FOCUS", 0); 134 wm_save_yourself = XInternAtom(fl_display, "WM_SAVE_YOURSELF", 0); 135 wm_colormap_windows = XInternAtom(fl_display, "WM_COLORMAP_WINDOWS",0); 136 _motif_wm_hints = XInternAtom(fl_display, "_MOTIF_WM_HINTS", 0); 137 kwm_win_decoration = XInternAtom(fl_display, "KWM_WIN_DECORATION", 0); 138#if DESKTOPS 139 kwm_win_desktop = XInternAtom(fl_display, "KWM_WIN_DESKTOP", 0); 140 kwm_win_sticky = XInternAtom(fl_display, "KWM_WIN_STICKY", 0); 141#endif 142// wm_client_leader = XInternAtom(fl_display, "WM_CLIENT_LEADER", 0); 143 _wm_quit_app = XInternAtom(fl_display, "_WM_QUIT_APP", 0); 144 } 145 146#ifdef TOPSIDE 147 label_x = label_h = label_w = 0; 148#else 149 label_y = label_h = label_w = 0; 150#endif 151 getLabel(); 152 // getIconLabel(); 153 154 {XWindowAttributes attr; 155 if (existing) attr = *existing; 156 else { 157 // put in some legal values in case XGetWindowAttributes fails: 158 attr.x = attr.y = 0; attr.width = attr.height = 100; 159 attr.colormap = fl_colormap; 160 attr.border_width = 0; 161 XGetWindowAttributes(fl_display, window, &attr); 162 } 163 left = top = dwidth = dheight = 0; // pretend border is zero-width for now 164 app_border_width = attr.border_width; 165 x(attr.x+app_border_width); restore_x = x(); 166 y(attr.y+app_border_width); restore_y = y(); 167 w(attr.width); restore_w = w(); 168 h(attr.height); restore_h = h(); 169 colormap = attr.colormap;} 170 171 getColormaps(); 172 173 //group_ = 0; 174 {XWMHints* hints = XGetWMHints(fl_display, window_); 175 if (hints) { 176 if ((hints->flags & InputHint) && !hints->input) set_flag(NO_FOCUS); 177 //if (hints && hints->flags&WindowGroupHint) group_ = hints->window_group; 178 } 179 switch (getIntProperty(wm_state, wm_state, 0)) { 180 case NormalState: 181 state_ = NORMAL; break; 182 case IconicState: 183 state_ = ICONIC; break; 184 // X also defines obsolete values ZoomState and InactiveState 185 default: 186 if (hints && (hints->flags&StateHint) && hints->initial_state==IconicState) 187 state_ = ICONIC; 188 else 189 state_ = NORMAL; 190 } 191 if (hints) XFree(hints);} 192 // Maya sets this, seems to mean the same as group: 193 // if (!group_) group_ = getIntProperty(wm_client_leader, XA_WINDOW); 194 195 XGetTransientForHint(fl_display, window_, &transient_for_xid); 196 197 getProtocols(); 198 199 getMotifHints(); 200 201 // get Gnome hints: 202 int p = getIntProperty(_win_hints, XA_CARDINAL); 203 if (p&1) set_flag(NO_FOCUS); // WIN_HINTS_SKIP_FOCUS 204 // if (p&2) // WIN_HINTS_SKIP_WINLIST 205 // if (p&4) // WIN_HINTS_SKIP_TASKBAR 206 // if (p&8) ... // WIN_HINTS_GROUP_TRANSIENT 207 if (p&16) set_flag(CLICK_TO_FOCUS); // WIN_HINTS_FOCUS_ON_CLICK 208 209 // get KDE hints: 210 p = getIntProperty(kwm_win_decoration, kwm_win_decoration, 1); 211 if (!(p&3)) set_flag(NO_BORDER); 212 else if (p & 2) set_flag(THIN_BORDER); 213 if (p & 256) set_flag(NO_FOCUS); 214 215 fix_transient_for(); 216 217 if (transient_for()) { 218 if (state_ == NORMAL) state_ = transient_for()->state_; 219#if DESKTOPS 220 desktop_ = transient_for()->desktop_; 221#endif 222 } 223#if DESKTOPS 224 // see if anybody thinks window is "sticky:" 225 else if ((getIntProperty(_win_state, XA_CARDINAL) & 1) // WIN_STATE_STICKY 226 || getIntProperty(kwm_win_sticky, kwm_win_sticky)) { 227 desktop_ = 0; 228 } else { 229 // get the desktop from either Gnome or KDE (Gnome takes precedence): 230 p = getIntProperty(_win_workspace, XA_CARDINAL, -1) + 1; // Gnome desktop 231 if (p <= 0) p = getIntProperty(kwm_win_desktop, kwm_win_desktop); 232 if (p > 0 && p < 25) 233 desktop_ = Desktop::number(p, 1); 234 else 235 desktop_ = Desktop::current(); 236 } 237 if (desktop_ && desktop_ != Desktop::current()) 238 if (state_ == NORMAL) state_ = OTHER_DESKTOP; 239#endif 240 241 int autoplace = getSizes(); 242 // some Motif programs assumme this will force the size to conform :-( 243 if (w() < min_w || h() < min_h) { 244 if (w() < min_w) w(min_w); 245 if (h() < min_h) h(min_h); 246 XResizeWindow(fl_display, window_, w(), h()); 247 } 248 249 // try to detect programs that think "transient_for" means "no border": 250 if (transient_for_xid && !label() && !flag(NO_BORDER)) { 251 set_flag(THIN_BORDER); 252 } 253 updateBorder(); 254 show_hide_buttons(); 255 256 if (autoplace && !existing && !(transient_for() && (x() || y()))) { 257 place_window(); 258 } 259 260 // move window so contents and border are visible: 261 x(force_x_onscreen(x(), w())); 262 y(force_y_onscreen(y(), h())); 263 264 // guess some values for the "restore" fields, if already maximized: 265 if (max_w_button.value()) { 266 restore_w = min_w + ((w()-dwidth-min_w)/2/inc_w) * inc_w; 267 restore_x = x()+left + (w()-dwidth-restore_w)/2; 268 } 269 if (max_h_button.value()) { 270 restore_h = min_h + ((h()-dheight-min_h)/2/inc_h) * inc_h; 271 restore_y = y()+top + (h()-dheight-restore_h)/2; 272 } 273 274 const int mask = CWBorderPixel | CWColormap | CWEventMask | CWBitGravity 275 | CWBackPixel | CWOverrideRedirect; 276 XSetWindowAttributes sattr; 277 sattr.event_mask = XEventMask; 278 sattr.colormap = fl_colormap; 279 sattr.border_pixel = fl_xpixel(FL_GRAY0); 280 sattr.bit_gravity = NorthWestGravity; 281 sattr.override_redirect = 1; 282 sattr.background_pixel = fl_xpixel(FL_GRAY); 283 Fl_X::set_xid(this, XCreateWindow(fl_display, 284 RootWindow(fl_display,fl_screen), 285 x(), y(), w(), h(), 0, 286 fl_visual->depth, 287 InputOutput, 288 fl_visual->visual, 289 mask, &sattr)); 290 291 setStateProperty(); 292 293 if (!dont_set_event_mask) XAddToSaveSet(fl_display, window_); 294 if (existing) set_state_flag(IGNORE_UNMAP); 295 XReparentWindow(fl_display, window_, fl_xid(this), left, top); 296 XSetWindowBorderWidth(fl_display, window_, 0); 297 if (state_ == NORMAL) XMapWindow(fl_display, window_); 298 sendConfigureNotify(); // many apps expect this even if window size unchanged 299 300#if CLICK_RAISES || CLICK_TO_TYPE 301 if (!dont_set_event_mask) 302 XGrabButton(fl_display, AnyButton, AnyModifier, window, False, 303 ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); 304#endif 305 306 if (state_ == NORMAL) { 307 XMapWindow(fl_display, fl_xid(this)); 308 if (!existing) activate_if_transient(); 309 } 310 set_visible(); 311} 312 313#if SMART_PLACEMENT 314// Helper functions for "smart" window placement. 315int overlap1(int p1, int l1, int p2, int l2) { 316 int ret = 0; 317 if(p1 <= p2 && p2 <= p1 + l1) { 318 ret = min(p1 + l1 - p2, l2); 319 } else if (p2 <= p1 && p1 <= p2 + l2) { 320 ret = min(p2 + l2 - p1, l1); 321 } 322 return ret; 323} 324 325int overlap(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { 326 return (overlap1(x1, w1, x2, w2) * overlap1(y1, h1, y2, h2)); 327} 328 329// Compute the overlap with existing windows. 330// For normal windows the overlapping area is taken into account plus a 331// constant value for every overlapping window. 332// The active window counts twice. 333// For iconic windows half the overlapping area is taken into account. 334int getOverlap(int x, int y, int w, int h, Frame *first, Frame *self) { 335 int ret = 0; 336 short state; 337 for (Frame* f = first; f; f = f->next) { 338 if (f != self) { 339 state = f->state(); 340 if (state == NORMAL || state == ICONIC) { 341 int o = overlap(x, y, w, h, f->x(), f->y(), f->w(), f->h()); 342 if (state == NORMAL) { 343 ret = ret + o + (o>0?40000:0) + (o * f->active()); 344 } else if (state == ICONIC) { 345 ret = ret + o/2; 346 } 347 } 348 } 349 } 350 return ret; 351} 352 353// autoplacement (brute force version for now) 354void Frame::place_window() { 355 int min_overlap = -1; 356 int tmp_x, tmp_y, tmp_o; 357 int best_x = 0; 358 int best_y = 0; 359 int _w = w(); 360 int _h = h(); 361 int max_x = Root->x() + Root->w(); 362 int max_y = Root->y() + Root->h(); 363 364 Frame *f1 = Frame::first; 365 for(int i=0;; i++) { 366 if (i==0) { 367 tmp_x = 0; 368 } else if (i==1) { 369 tmp_x = max_x - _w; 370 } else { 371 if (f1 == this) { 372 f1 = f1->next; 373 } 374 if (!f1) { 375 break; 376 } 377 tmp_x = f1->x() + f1->w(); 378 f1 = f1->next; 379 } 380 Frame *f2 = Frame::first; 381 for(int j=0;; j++) { 382 if (j==0) { 383 tmp_y = 0; 384 } else if (j==1) { 385 tmp_y = max_y - _h; 386 } else { 387 if (f2 == this) { 388 f2 = f2->next; 389 } 390 if (!f2) { 391 break; 392 } 393 tmp_y = f2->y() + f2->h(); 394 f2 = f2->next; 395 } 396 397 if ((tmp_x + _w <= max_x) && (tmp_y + _h <= max_y)) { 398 tmp_o = getOverlap(tmp_x, tmp_y, _w, _h, Frame::first, this); 399 if(tmp_o < min_overlap || min_overlap < 0) { 400 best_x = tmp_x; 401 best_y = tmp_y; 402 min_overlap = tmp_o; 403 if (min_overlap == 0) { 404 break; 405 } 406 } 407 } 408 } 409 if (min_overlap == 0) { 410 break; 411 } 412 } 413 x(best_x); 414 y(best_y); 415} 416 417#else 418 419// autoplacement (stupid version for now) 420void Frame::place_window() { 421 x(Root->x()+(Root->w()-w())/2); 422 y(Root->y()+(Root->h()-h())/2); 423 // move it until it does not hide any existing windows: 424 const int delta = TITLE_WIDTH+LEFT; 425 for (Frame* f = next; f; f = f->next) { 426 if (f->x()+delta > x() && f->y()+delta > y() && 427 f->x()+f->w()-delta < x()+w() && f->y()+f->h()-delta < y()+h()) { 428 x(max(x(),f->x()+delta)); 429 y(max(y(),f->y()+delta)); 430 f = this; 431 } 432 } 433} 434#endif 435 436// modify the passed X & W to a legal horizontal window position 437int Frame::force_x_onscreen(int X, int W) { 438 // force all except the black border on-screen: 439 X = min(X, Root->x()+Root->w()+1-W); 440 X = max(X, Root->x()-1); 441 // force the contents on-screen: 442 X = min(X, Root->x()+Root->w()-W+dwidth-left); 443 if (W-dwidth > Root->w() || h()-dheight > Root->h()) 444 // windows bigger than the screen need title bar so they can move 445 X = max(X, Root->x()-LEFT); 446 else 447 X = max(X, Root->x()-left); 448 return X; 449} 450 451// modify the passed Y & H to a legal vertical window position: 452int Frame::force_y_onscreen(int Y, int H) { 453 // force border (except black edge) to be on-screen: 454 Y = min(Y, Root->y()+Root->h()+1-H); 455 Y = max(Y, Root->y()-1); 456 // force contents to be on-screen: 457 Y = min(Y, Root->y()+Root->h()-H+dheight-top); 458 Y = max(Y, Root->y()-top); 459 return Y; 460} 461 462//////////////////////////////////////////////////////////////// 463// destructor 464// The destructor is called on DestroyNotify, so I don't have to do anything 465// to the contained window, which is already been destroyed. 466 467// fltk bug: it does not clear these pointers when window is deleted, 468// causing flwm to crash on window close sometimes: 469extern Fl_Window *fl_xfocus; 470extern Fl_Window *fl_xmousewin; 471 472Frame::~Frame() { 473 474 // It is possible for the frame to be destroyed while the menu is 475 // popped-up, and the menu will still contain a pointer to it. To 476 // fix this the menu checks the state_ location for a legal and 477 // non-withdrawn state value before doing anything. This should 478 // be reliable unless something reallocates the memory and writes 479 // a legal state value to this location: 480 state_ = UNMAPPED; 481 482#if FL_MAJOR_VERSION < 2 483 // fix fltk bug: 484 fl_xfocus = 0; 485 fl_xmousewin = 0; 486 Fl::focus_ = 0; 487#endif 488 489 // remove any pointers to this: 490 Frame** cp; for (cp = &first; *cp; cp = &((*cp)->next)) 491 if (*cp == this) {*cp = next; break;} 492 for (Frame* f = first; f; f = f->next) { 493 if (f->transient_for_ == this) f->transient_for_ = transient_for_; 494 if (f->revert_to == this) f->revert_to = revert_to; 495 } 496 throw_focus(1); 497 498 if (colormapWinCount) { 499 XFree((char *)colormapWindows); 500 delete[] window_Colormaps; 501 } 502 //if (iconlabel()) XFree((char*)iconlabel()); 503 if (label()) XFree((char*)label()); 504} 505 506//////////////////////////////////////////////////////////////// 507 508void Frame::getLabel(int del) { 509 char* old = (char*)label(); 510 char* nu = del ? 0 : (char*)getProperty(XA_WM_NAME); 511 if (nu) { 512 // since many window managers print a default label when none is 513 // given, many programs send spaces to make a blank label. Detect 514 // this and make it really be blank: 515 char* c = nu; while (*c == ' ') c++; 516 if (!*c) {XFree(nu); nu = 0;} 517 } 518 if (old) { 519 if (nu && !strcmp(old,nu)) {XFree(nu); return;} 520 XFree(old); 521 } else { 522 if (!nu) return; 523 } 524#ifdef TOPSIDE 525 Fl_Widget::label(nu); 526 if (nu) { 527 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE); 528 label_h = int(fl_size())+6; 529 } else 530 label_h = 0; 531 if (shown())// && label_w > 3 && top > 3) 532 XClearArea(fl_display, fl_xid(this), label_x, BUTTON_TOP, label_w, label_h, 1); 533#else 534 Fl_Widget::label(nu); 535 if (nu) { 536 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE); 537 label_w = int(fl_width(nu))+6; 538 } else 539 label_w = 0; 540 if (shown() && label_h > 3 && left > 3) 541 XClearArea(fl_display, fl_xid(this), 1, label_y+3, left-1, label_h-3, 1); 542#endif 543} 544 545//////////////////////////////////////////////////////////////// 546 547int Frame::getGnomeState(int &) { 548// values for _WIN_STATE property are from Gnome WM compliance docs: 549#define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/ 550#define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/ 551#define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/ 552#define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/ 553#define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/ 554#define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/ 555#define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/ 556#define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/ 557#define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/ 558#define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/ 559 // nyi 560 return 0; 561} 562 563//////////////////////////////////////////////////////////////// 564 565// Read the sizeHints, and try to remove the vast number of mistakes 566// that some applications seem to do writing them. 567// Returns true if autoplace should be done. 568 569int Frame::getSizes() { 570 571 XSizeHints sizeHints; 572 long junk; 573 if (!XGetWMNormalHints(fl_display, window_, &sizeHints, &junk)) 574 sizeHints.flags = 0; 575 576 // get the increment, use 1 if none or illegal values: 577 if (sizeHints.flags & PResizeInc) { 578 inc_w = sizeHints.width_inc; if (inc_w < 1) inc_w = 1; 579 inc_h = sizeHints.height_inc; if (inc_h < 1) inc_h = 1; 580 } else { 581 inc_w = inc_h = 1; 582 } 583 584 // get the current size of the window: 585 int W = w()-dwidth; 586 int H = h()-dheight; 587 // I try a lot of places to get a good minimum size value. Lots of 588 // programs set illegal or junk values, so getting this correct is 589 // difficult: 590 min_w = W; 591 min_h = H; 592 593 // guess a value for minimum size in case it is not set anywhere: 594 min_w = min(min_w, 4*BUTTON_H); 595 min_w = ((min_w+inc_w-1)/inc_w) * inc_w; 596 min_h = min(min_h, 4*BUTTON_H); 597 min_h = ((min_h+inc_h-1)/inc_h) * inc_h; 598 // some programs put the minimum size here: 599 if (sizeHints.flags & PBaseSize) { 600 junk = sizeHints.base_width; if (junk > 0) min_w = junk; 601 junk = sizeHints.base_height; if (junk > 0) min_h = junk; 602 } 603 // finally, try the actual place the minimum size should be: 604 if (sizeHints.flags & PMinSize) { 605 junk = sizeHints.min_width; if (junk > 0) min_w = junk; 606 junk = sizeHints.min_height; if (junk > 0) min_h = junk; 607 } 608 609 max_w = max_h = 0; // default maximum size is "infinity" 610 if (sizeHints.flags & PMaxSize) { 611 // Though not defined by ICCCM standard, I interpret any maximum 612 // size that is less than the minimum to mean "infinity". This 613 // allows the maximum to be set in one direction only: 614 junk = sizeHints.max_width; 615 if (junk >= min_w && junk <= W) max_w = junk; 616 junk = sizeHints.max_height; 617 if (junk >= min_h && junk <= H) max_h = junk; 618 } 619 620 // set the maximize buttons according to current size: 621 max_w_button.value(W == maximize_width()); 622 max_h_button.value(H == maximize_height()); 623 624 // Currently only 1x1 aspect works: 625 if (sizeHints.flags & PAspect 626 && sizeHints.min_aspect.x == sizeHints.min_aspect.y) 627 set_flag(KEEP_ASPECT); 628 629 // another fix for gimp, which sets PPosition to 0,0: 630 if (x() <= 0 && y() <= 0) sizeHints.flags &= ~PPosition; 631 632 return !(sizeHints.flags & (USPosition|PPosition)); 633} 634 635int max_w_switch; 636// return width of contents when maximize button pressed: 637int Frame::maximize_width() { 638 int W = max_w_switch; if (!W) W = Root->w(); 639#ifdef TOPSIDE 640 return ((W-min_w)/inc_w) * inc_w + min_w; 641#else 642 return ((W-TITLE_WIDTH-min_w)/inc_w) * inc_w + min_w; 643#endif 644} 645 646int max_h_switch; 647int Frame::maximize_height() { 648 int H = max_h_switch; if (!H) H = Root->h(); 649#ifdef TOPSIDE 650 return ((H-TITLE_HEIGHT-min_h)/inc_h) * inc_h + min_h; 651#else 652 return ((H-min_h)/inc_h) * inc_h + min_h; 653#endif 654} 655 656//////////////////////////////////////////////////////////////// 657 658void Frame::getProtocols() { 659 int n; Atom* p = (Atom*)getProperty(wm_protocols, XA_ATOM, &n); 660 if (p) { 661 clear_flag(DELETE_WINDOW_PROTOCOL|TAKE_FOCUS_PROTOCOL|QUIT_PROTOCOL); 662 for (int i = 0; i < n; ++i) { 663 if (p[i] == wm_delete_window) { 664 set_flag(DELETE_WINDOW_PROTOCOL); 665 } else if (p[i] == wm_take_focus) { 666 set_flag(TAKE_FOCUS_PROTOCOL); 667 } else if (p[i] == wm_save_yourself) { 668 set_flag(SAVE_PROTOCOL); 669 } else if (p[i] == _wm_quit_app) { 670 set_flag(QUIT_PROTOCOL); 671 } 672 } 673 } 674 XFree((char*)p); 675} 676 677//////////////////////////////////////////////////////////////// 678 679int Frame::getMotifHints() { 680 long* prop = (long*)getProperty(_motif_wm_hints, _motif_wm_hints); 681 if (!prop) return 0; 682 683 // see /usr/include/X11/Xm/MwmUtil.h for meaning of these bits... 684 // prop[0] = flags (what props are specified) 685 // prop[1] = functions (all, resize, move, minimize, maximize, close, quit) 686 // prop[2] = decorations (all, border, resize, title, menu, minimize, 687 // maximize) 688 // prop[3] = input_mode (modeless, primary application modal, system modal, 689 // full application modal) 690 // prop[4] = status (tear-off window) 691 692 // Fill in the default value for missing fields: 693 if (!(prop[0]&1)) prop[1] = 1; 694 if (!(prop[0]&2)) prop[2] = 1; 695 696 // The low bit means "turn the marked items off", invert this. 697 // Transient windows already have size & iconize buttons turned off: 698 if (prop[1]&1) prop[1] = ~prop[1] & (transient_for_xid ? ~0x58 : -1); 699 if (prop[2]&1) prop[2] = ~prop[2] & (transient_for_xid ? ~0x60 : -1); 700 701 int old_flags = flags(); 702 703 // see if they are trying to turn off border: 704 if (!(prop[2])) set_flag(NO_BORDER); else clear_flag(NO_BORDER); 705 706 // see if they are trying to turn off title & close box: 707 if (!(prop[2]&0x18)) set_flag(THIN_BORDER); else clear_flag(THIN_BORDER); 708 709 // some Motif programs use this to disable resize :-( 710 // and some programs change this after the window is shown (*&%$#%) 711 if (!(prop[1]&2) || !(prop[2]&4)) 712 set_flag(NO_RESIZE); else clear_flag(NO_RESIZE); 713 714 // and some use this to disable the Close function. The commented 715 // out test is it trying to turn off the mwm menu button: it appears 716 // programs that do that still expect Alt+F4 to close them, so I 717 // leave the close on then: 718 if (!(prop[1]&0x20) /*|| !(prop[2]&0x10)*/) 719 set_flag(NO_CLOSE); else clear_flag(NO_CLOSE); 720 721 // see if they set "input hint" to non-zero: 722 // prop[3] should be nonzero but the only example of this I have 723 // found is Netscape 3.0 and it sets it to zero... 724 if (!shown() && (prop[0]&4) /*&& prop[3]*/) set_flag(::MODAL); 725 726 // see if it is forcing the iconize button back on. This makes 727 // transient_for act like group instead... 728 if ((prop[1]&0x8) || (prop[2]&0x20)) set_flag(ICONIZE); 729 730 // Silly 'ol Amazon paint ignores WM_DELETE_WINDOW and expects to 731 // get the SGI-specific "_WM_QUIT_APP". It indicates this by trying 732 // to turn off the close box. SIGH!!! 733 if (flag(QUIT_PROTOCOL) && !(prop[1]&0x20)) 734 clear_flag(DELETE_WINDOW_PROTOCOL); 735 736 XFree((char*)prop); 737 return (flags() ^ old_flags); 738} 739 740//////////////////////////////////////////////////////////////// 741 742void Frame::getColormaps(void) { 743 if (colormapWinCount) { 744 XFree((char *)colormapWindows); 745 delete[] window_Colormaps; 746 } 747 int n; 748 XWindow* cw = (XWindow*)getProperty(wm_colormap_windows, XA_WINDOW, &n); 749 if (cw) { 750 colormapWinCount = n; 751 colormapWindows = cw; 752 window_Colormaps = new Colormap[n]; 753 for (int i = 0; i < n; ++i) { 754 if (cw[i] == window_) { 755 window_Colormaps[i] = colormap; 756 } else { 757 XWindowAttributes attr; 758 XSelectInput(fl_display, cw[i], ColormapChangeMask); 759 XGetWindowAttributes(fl_display, cw[i], &attr); 760 window_Colormaps[i] = attr.colormap; 761 } 762 } 763 } else { 764 colormapWinCount = 0; 765 } 766} 767 768void Frame::installColormap() const { 769 for (int i = colormapWinCount; i--;) 770 if (colormapWindows[i] != window_ && window_Colormaps[i]) 771 XInstallColormap(fl_display, window_Colormaps[i]); 772 if (colormap) 773 XInstallColormap(fl_display, colormap); 774} 775 776//////////////////////////////////////////////////////////////// 777 778// figure out transient_for(), based on the windows that exist, the 779// transient_for and group attributes, etc: 780void Frame::fix_transient_for() { 781 Frame* p = 0; 782 if (transient_for_xid && !flag(ICONIZE)) { 783 for (Frame* f = first; f; f = f->next) { 784 if (f != this && f->window_ == transient_for_xid) {p = f; break;} 785 } 786 // loops are illegal: 787 for (Frame* q = p; q; q = q->transient_for_) if (q == this) {p = 0; break;} 788 } 789 transient_for_ = p; 790} 791 792int Frame::is_transient_for(const Frame* f) const { 793 if (f) 794 for (Frame* p = transient_for(); p; p = p->transient_for()) 795 if (p == f) return 1; 796 return 0; 797} 798 799// When a program maps or raises a window, this is called. It guesses 800// if this window is in fact a modal window for the currently active 801// window and if so transfers the active state to this: 802// This also activates new main windows automatically 803int Frame::activate_if_transient() { 804 if (!Fl::pushed()) 805 if (!transient_for() || is_transient_for(active_)) return activate(1); 806 return 0; 807} 808 809//////////////////////////////////////////////////////////////// 810 811int Frame::activate(int warp) { 812 // see if a modal & newer window is up: 813 for (Frame* c = first; c && c != this; c = c->next) 814 if (c->flag(::MODAL) && c->transient_for() == this) 815 if (c->activate(warp)) return 1; 816 // ignore invisible windows: 817 if (state() != NORMAL || w() <= dwidth) return 0; 818 // always put in the colormap: 819 installColormap(); 820 // move the pointer if desired: 821 // (note that moving the pointer is pretty much required for point-to-type 822 // unless you know the pointer is already in the window): 823 if (!warp || Fl::event_state() & (FL_BUTTON1|FL_BUTTON2|FL_BUTTON3)) { 824 ; 825 } else if (warp==2) { 826 // warp to point at title: 827#ifdef TOPSIDE 828 XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, 829 min(label_x+label_w/2+1, label_h/2)); 830#else 831 XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, 832 min(label_y+label_w/2+1,h()/2)); 833#endif 834 } else { 835 warp_pointer(); 836 } 837 // skip windows that don't want focus: 838 if (flag(NO_FOCUS)) return 0; 839 // set this even if we think it already has it, this seems to fix 840 // bugs with Motif popups: 841 XSetInputFocus(fl_display, window_, RevertToPointerRoot, fl_event_time); 842 if (active_ != this) { 843 if (active_) active_->deactivate(); 844 active_ = this; 845//#ifdef ACTIVE_COLOR 846// XSetWindowAttributes a; 847// a.background_pixel = fl_xpixel(FL_SELECTION_COLOR); 848// XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 849// labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR)); // ML changed color comparison 850// XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 851//#else 852 XSetWindowAttributes a; 853 a.background_pixel = fl_xpixel(FL_BACKGROUND2_COLOR); 854 XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 855 labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); // ML changed color comparison 856 XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 857#ifdef SHOW_CLOCK 858 redraw(); 859#endif 860//#endif 861 if (flag(TAKE_FOCUS_PROTOCOL)) 862 sendMessage(wm_protocols, wm_take_focus); 863 } 864 return 1; 865} 866 867// this private function should only be called by constructor and if 868// the window is active(): 869void Frame::deactivate() { 870#ifdef ACTIVE_COLOR 871 XSetWindowAttributes a; 872 a.background_pixel = fl_xpixel(FL_GRAY); 873 XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 874 labelcolor(FL_FOREGROUND_COLOR); 875 XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 876#else 877#ifdef SHOW_CLOCK 878 redraw(); 879#endif 880#endif 881} 882 883#if CLICK_RAISES || CLICK_TO_TYPE 884// After the XGrabButton, the main loop will get the mouse clicks, and 885// it will call here when it gets them: 886void click_raise(Frame* f) { 887 f->activate(); 888#if CLICK_RAISES 889 if (fl_xevent->xbutton.button <= 1) f->raise(); 890#endif 891 XAllowEvents(fl_display, ReplayPointer, CurrentTime); 892} 893#endif 894 895// get rid of the focus by giving it to somebody, if possible: 896void Frame::throw_focus(int destructor) { 897 if (!active()) return; 898 if (!destructor) deactivate(); 899 active_ = 0; 900 if (revert_to && revert_to->activate()) return; 901 for (Frame* f = first; f; f = f->next) 902 if (f != this && f->activate()) return; 903} 904 905//////////////////////////////////////////////////////////////// 906 907// change the state of the window (this is a private function and 908// it ignores the transient-for or desktop information): 909 910void Frame::state(short newstate) { 911 short oldstate = state(); 912 if (newstate == oldstate) return; 913 state_ = newstate; 914 switch (newstate) { 915 case UNMAPPED: 916 throw_focus(); 917 XUnmapWindow(fl_display, fl_xid(this)); 918 //set_state_flag(IGNORE_UNMAP); 919 //XUnmapWindow(fl_display, window_); 920 XRemoveFromSaveSet(fl_display, window_); 921 break; 922 case NORMAL: 923 if (oldstate == UNMAPPED) XAddToSaveSet(fl_display, window_); 924 if (w() > dwidth) XMapWindow(fl_display, window_); 925 XMapWindow(fl_display, fl_xid(this)); 926 clear_state_flag(IGNORE_UNMAP); 927 break; 928 default: 929 if (oldstate == UNMAPPED) { 930 XAddToSaveSet(fl_display, window_); 931 } else if (oldstate == NORMAL) { 932 throw_focus(); 933 XUnmapWindow(fl_display, fl_xid(this)); 934 //set_state_flag(IGNORE_UNMAP); 935 //XUnmapWindow(fl_display, window_); 936 } else { 937 return; // don't setStateProperty IconicState multiple times 938 } 939 break; 940 } 941 setStateProperty(); 942} 943 944void Frame::setStateProperty() const { 945 long data[2]; 946 switch (state()) { 947 case UNMAPPED : 948 data[0] = WithdrawnState; break; 949 case NORMAL : 950 case OTHER_DESKTOP : 951 data[0] = NormalState; break; 952 default : 953 data[0] = IconicState; break; 954 } 955 data[1] = (long)None; 956 XChangeProperty(fl_display, window_, wm_state, wm_state, 957 32, PropModeReplace, (unsigned char *)data, 2); 958} 959 960//////////////////////////////////////////////////////////////// 961// Public state modifiers that move all transient_for(this) children 962// with the frame and do the desktops right: 963 964void Frame::raise() { 965 Frame* newtop = 0; 966 Frame* previous = 0; 967 int previous_state = state_; 968 Frame** p; 969 // Find all the transient-for windows and this one, and raise them, 970 // preserving stacking order: 971 for (p = &first; *p;) { 972 Frame* f = *p; 973 if (f == this || f->is_transient_for(this) && f->state() != UNMAPPED) { 974 *p = f->next; // remove it from list 975 if (previous) { 976 XWindowChanges w; 977 w.sibling = fl_xid(previous); 978 w.stack_mode = Below; 979 XConfigureWindow(fl_display, fl_xid(f), CWSibling|CWStackMode, &w); 980 previous->next = f; 981 } else { 982 XRaiseWindow(fl_display, fl_xid(f)); 983 newtop = f; 984 } 985#if DESKTOPS 986 if (f->desktop_ && f->desktop_ != Desktop::current()) 987 f->state(OTHER_DESKTOP); 988 else 989#endif 990 f->state(NORMAL); 991 previous = f; 992 } else { 993 p = &((*p)->next); 994 } 995 } 996 previous->next = first; 997 first = newtop; 998#if DESKTOPS 999 if (!transient_for() && desktop_ && desktop_ != Desktop::current()) { 1000 // for main windows we also must move to the current desktop 1001 desktop(Desktop::current()); 1002 } 1003#endif 1004 if (previous_state != NORMAL && newtop->state_==NORMAL) 1005 newtop->activate_if_transient(); 1006} 1007 1008void Frame::lower() { 1009 Frame* t = transient_for(); if (t) t->lower(); 1010 if (!next || next == t) return; // already on bottom 1011 // pull it out of the list: 1012 Frame** p = &first; 1013 for (; *p != this; p = &((*p)->next)) {} 1014 *p = next; 1015 // find end of list: 1016 Frame* f = next; while (f->next != t) f = f->next; 1017 // insert it after that: 1018 f->next = this; next = t; 1019 // and move the X window: 1020 XWindowChanges w; 1021 w.sibling = fl_xid(f); 1022 w.stack_mode = Below; 1023 XConfigureWindow(fl_display, fl_xid(this), CWSibling|CWStackMode, &w); 1024} 1025 1026void Frame::iconize() { 1027 for (Frame* c = first; c; c = c->next) { 1028 if (c == this || c->is_transient_for(this) && c->state() != UNMAPPED) 1029 c->state(ICONIC); 1030 } 1031} 1032 1033#if DESKTOPS 1034void Frame::desktop(Desktop* d) { 1035 if (d == desktop_) return; 1036 // Put all the relatives onto the desktop as well: 1037 for (Frame* c = first; c; c = c->next) { 1038 if (c == this || c->is_transient_for(this)) { 1039 c->desktop_ = d; 1040 c->setProperty(_win_state, XA_CARDINAL, !d); 1041 c->setProperty(kwm_win_sticky, kwm_win_sticky, !d); 1042 if (d) { 1043 c->setProperty(kwm_win_desktop, kwm_win_desktop, d->number()); 1044 c->setProperty(_win_workspace, XA_CARDINAL, d->number()-1); 1045 } 1046 if (!d || d == Desktop::current()) { 1047 if (c->state() == OTHER_DESKTOP) c->state(NORMAL); 1048 } else { 1049 if (c->state() == NORMAL) c->state(OTHER_DESKTOP); 1050 } 1051 } 1052 } 1053} 1054#endif 1055 1056//////////////////////////////////////////////////////////////// 1057 1058// Resize and/or move the window. The size is given for the frame, not 1059// the contents. This also sets the buttons on/off as needed: 1060 1061void Frame::set_size(int nx, int ny, int nw, int nh, int warp) { 1062 int dx = nx-x(); x(nx); 1063 int dy = ny-y(); y(ny); 1064 if (!dx && !dy && nw == w() && nh == h()) return; 1065 int unmap = 0; 1066 int remap = 0; 1067 // use XClearArea to cause correct damage events: 1068 if (nw != w()) { 1069 max_w_button.value(nw-dwidth == maximize_width()); 1070 min_w_button.value(nw <= dwidth); 1071 if (nw <= dwidth) { 1072 unmap = 1; 1073 } else { 1074 if (w() <= dwidth) remap = 1; 1075 } 1076 int minw = (nw < w()) ? nw : w(); 1077 XClearArea(fl_display, fl_xid(this), minw-RIGHT, 0, RIGHT, nh, 1); 1078 w(nw); 1079#ifdef TOPSIDE 1080 show_hide_buttons(); 1081#endif 1082 } 1083 if (nh != h()) { 1084 max_h_button.value(nh-dheight == maximize_height()); 1085 int minh = (nh < h()) ? nh : h(); 1086 XClearArea(fl_display, fl_xid(this), 0, minh-BOTTOM, w(), BOTTOM, 1); 1087 // see if label or close box moved, erase the minimum area: 1088// int old_label_y = label_y; 1089// int old_label_h = label_h; 1090 h(nh); 1091#if 1 //def SHOW_CLOCK 1092#ifdef TOPSIDE 1093 //int t = label_x + 3; // we have to clear the entire label area 1094 XClearArea(fl_display,fl_xid(this), label_x, BUTTON_TOP, label_x + label_w, 1095 BUTTON_H, 1); // ML 1096#else 1097 show_hide_buttons(); 1098 //int t = label_y + 3; // we have to clear the entire label area 1099 XClearArea(fl_display,fl_xid(this), 1, label_y, left-1, nh-label_y, 1); // ML 1100#endif 1101#else 1102 int t = nh; 1103 if (label_y != old_label_y) { 1104 t = label_y; if (old_label_y < t) t = old_label_y; 1105 } else if (label_y+label_h != old_label_y+old_label_h) { 1106 t = label_y+label_h; 1107 if (old_label_y+old_label_h < t) t = old_label_y+old_label_h; 1108 } 1109#endif 1110//ML if (t < nh && left>LEFT) 1111//ML XClearArea(fl_display,fl_xid(this), 1, t, left-1, nh-t, 1); 1112 } 1113 // for maximize button move the cursor first if window gets smaller 1114 if (warp == 1 && (dx || dy)) 1115 XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy); 1116 // for configure request, move the cursor first 1117 if (warp == 2 && active() && !Fl::pushed()) warp_pointer(); 1118 XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh); 1119 if (nw <= dwidth) { 1120 if (unmap) { 1121 set_state_flag(IGNORE_UNMAP); 1122 XUnmapWindow(fl_display, window_); 1123 } 1124 } else { 1125 XResizeWindow(fl_display, window_, nw-dwidth, nh-dheight); 1126 if (remap) { 1127 XMapWindow(fl_display, window_); 1128#if CLICK_TO_TYPE 1129 if (active()) activate(); 1130#else 1131 activate(); 1132#endif 1133 } 1134 } 1135 // for maximize button move the cursor second if window gets bigger: 1136 if (warp == 3 && (dx || dy)) 1137 XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy); 1138 if (nw > dwidth) sendConfigureNotify(); 1139 XSync(fl_display,0); 1140} 1141 1142void Frame::sendConfigureNotify() const { 1143 XConfigureEvent ce; 1144 ce.type = ConfigureNotify; 1145 ce.event = window_; 1146 ce.window = window_; 1147 ce.x = x()+left-app_border_width; 1148 ce.y = y()+top-app_border_width; 1149 ce.width = w()-dwidth; 1150 ce.height = h()-dheight; 1151 ce.border_width = app_border_width; 1152 ce.above = None; 1153 ce.override_redirect = 0; 1154 XSendEvent(fl_display, window_, False, StructureNotifyMask, (XEvent*)&ce); 1155} 1156 1157// move the pointer inside the window: 1158void Frame::warp_pointer() { 1159 int X,Y; Fl::get_mouse(X,Y); 1160 X -= x(); 1161 int Xi = X; 1162 if (X <= 0) X = left/2+1; 1163 if (X >= w()) X = w()-(RIGHT/2+1); 1164 Y -= y(); 1165 int Yi = Y; 1166 if (Y < 0) Y = TOP/2+1; 1167 if (Y >= h()) Y = h()-(BOTTOM/2+1); 1168 if (X != Xi || Y != Yi) 1169 XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, X, Y); 1170} 1171 1172// Resize the frame to match the current border type: 1173void Frame::updateBorder() { 1174 int nx = x()+left; 1175 int ny = y()+top; 1176 int nw = w()-dwidth; 1177 int nh = h()-dheight; 1178 if (flag(NO_BORDER)) { 1179 left = top = dwidth = dheight = 0; 1180 } else { 1181#ifdef TOPSIDE 1182 left = LEFT; 1183 dwidth = left+RIGHT; 1184 top = flag(THIN_BORDER) ? TOP : TOP+TITLE_HEIGHT; 1185 dheight = top+BOTTOM; 1186#else 1187 left = flag(THIN_BORDER) ? LEFT : LEFT+TITLE_WIDTH; 1188 dwidth = left+RIGHT; 1189 top = TOP; 1190 dheight = TOP+BOTTOM; 1191#endif 1192 } 1193 nx -= left; 1194 ny -= top; 1195 nw += dwidth; 1196 nh += dheight; 1197 if (x()==nx && y()==ny && w()==nw && h()==nh) return; 1198 x(nx); y(ny); w(nw); h(nh); 1199 if (!shown()) return; // this is so constructor can call this 1200 // try to make the contents not move while the border changes around it: 1201 XSetWindowAttributes a; 1202 a.win_gravity = StaticGravity; 1203 XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a); 1204 XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh); 1205 a.win_gravity = NorthWestGravity; 1206 XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a); 1207 // fix the window position if the X server didn't do the gravity: 1208 XMoveWindow(fl_display, window_, left, top); 1209} 1210 1211// position and show the buttons according to current border, size, 1212// and other state information: 1213#ifdef TOPSIDE 1214 // FIRST, the TOPSIDE VERSION of the function 1215void Frame::show_hide_buttons() { 1216 if (flag(THIN_BORDER|NO_BORDER)) { 1217 iconize_button.hide(); 1218 max_w_button.hide(); 1219 min_w_button.hide(); 1220 max_h_button.hide(); 1221 close_button.hide(); 1222 return; 1223 } 1224 int bx = BUTTON_LEFT; 1225 1226// ML CLOSE BUTTON IS TOP LEFT -- 1227#if CLOSE_BOX 1228 if (flag(NO_CLOSE)) { 1229#endif 1230 close_button.hide(); 1231#if CLOSE_BOX 1232 } else { 1233 close_button.show(); 1234 close_button.position(BUTTON_LEFT,BUTTON_TOP); 1235 } 1236#endif 1237// ML: OTHER BUTTONS ARE PLACED IN UPPER RIGHT 1238 bx = w() - BUTTON_RIGHT - BUTTON_W; 1239 1240 // ML: FIRST PLACE ICONIZE/HIDE BUTTON FARTHEST INTO UPPER RIGHT 1241 if (transient_for()) { 1242 // don't show resize and iconize buttons for "transient" (e.g. dialog box) windows 1243 iconize_button.hide(); 1244 max_w_button.hide(); 1245 max_h_button.hide(); 1246 } else { 1247 iconize_button.show(); 1248 iconize_button.position(bx, BUTTON_TOP); 1249 bx -= BUTTON_W; 1250 1251 max_h_button.position(bx, BUTTON_TOP); 1252 max_h_button.show(); 1253 bx -= BUTTON_W; 1254 max_w_button.position(bx, BUTTON_TOP); 1255 max_w_button.show(); 1256 bx -= BUTTON_W; 1257 } 1258 1259#if MINIMIZE_BOX 1260 if (!transient_for()) { 1261 min_w_button.position(bx, BUTTON_TOP); 1262 min_w_button.show(); 1263 bx -= BUTTON_W; 1264 } 1265 else { 1266#else 1267 min_w_button.hide(); 1268#endif 1269 1270#if MINIMIZE_BOX 1271 } 1272#endif 1273 if (label_x != bx && shown()) 1274//ML Buttons look garbled after expanding, so let's just clear the whole area 1275 XClearArea(fl_display,fl_xid(this), LEFT, TOP, w() - LEFT, TITLE_HEIGHT, 1); 1276 label_x = BUTTON_LEFT + BUTTON_W + left; 1277 label_w = bx - label_x; 1278 1279} 1280 1281#else // have a left-side titlebar version 1282 1283void Frame::show_hide_buttons() { 1284 if (flag(THIN_BORDER|NO_BORDER)) { 1285 iconize_button.hide(); 1286 max_w_button.hide(); 1287 min_w_button.hide(); 1288 max_h_button.hide(); 1289 close_button.hide(); 1290 return; 1291 } 1292 int by = BUTTON_TOP; 1293//ML MOVED THESE BUTTONS TO BOTTOM -- 1294// if (transient_for()) { 1295// iconize_button.hide(); 1296// min_w_button.hide(); 1297// } else { 1298// iconize_button.position(BUTTON_LEFT,by); 1299// iconize_button.show(); 1300// by += BUTTON_H; 1301//#if MINIMIZE_BOX 1302// min_w_button.position(BUTTON_LEFT,by); 1303// min_w_button.show(); 1304// by += BUTTON_H; 1305//#else 1306// min_w_button.hide(); 1307//#endif 1308// } 1309// -- END ML 1310 1311// ML CLOSE BUTTON IS NOW TOP LEFT -- 1312#if CLOSE_BOX 1313 if (flag(NO_CLOSE)) { 1314#endif 1315 close_button.hide(); 1316#if CLOSE_BOX 1317 } else { 1318 close_button.show(); 1319 close_button.position(BUTTON_LEFT,by); 1320 by += BUTTON_H; 1321 } 1322#endif 1323// ML: And minimize button is next, if it is enabled (off in current config.h) 1324#if MINIMIZE_BOX 1325 if (!transient_for()) { 1326 min_w_button.position(BUTTON_LEFT,by); 1327 min_w_button.show(); 1328 by += BUTTON_H; 1329 } 1330 else { 1331#else 1332 min_w_button.hide(); 1333#endif 1334#if MINIMIZE_BOX 1335 } 1336#endif 1337// -- END ML 1338 if (min_h == max_h || flag(KEEP_ASPECT|NO_RESIZE) || 1339 !max_h_button.value() && by+label_w+2*BUTTON_H > h()-BUTTON_BOTTOM) { 1340 max_h_button.hide(); 1341 } else { 1342 max_h_button.position(BUTTON_LEFT,by); 1343 max_h_button.show(); 1344 by += BUTTON_H; 1345 } 1346 if (min_w == max_w || flag(KEEP_ASPECT|NO_RESIZE) || 1347 !max_w_button.value() && by+label_w+2*BUTTON_H > h()-BUTTON_BOTTOM) { 1348 max_w_button.hide(); 1349 } else { 1350 max_w_button.position(BUTTON_LEFT,by); 1351 max_w_button.show(); 1352 by += BUTTON_H; 1353 } 1354 if (label_y != by && shown()) 1355//ML XClearArea(fl_display,fl_xid(this), 1, by, left-1, label_h+label_y-by, 1); 1356//ML Buttons look garbled after expanding, so let's just clear the whole area 1357 XClearArea(fl_display,fl_xid(this), 1, 1, left-1, h()-1, 1); 1358 label_y = by; 1359 1360//ML MOVED CLOSE BUTTON TO TOP -- 1361//#if CLOSE_BOX 1362// if (by+BUTTON_H > h()-BUTTON_BOTTOM || flag(NO_CLOSE)) { 1363//#endif 1364// label_h = h()-BOTTOM-by; 1365// close_button.hide(); 1366//#if CLOSE_BOX 1367// } else { 1368// close_button.show(); 1369// close_button.position(BUTTON_LEFT,h()-(BUTTON_BOTTOM+BUTTON_H)); 1370// label_h = close_button.y()-by; 1371// } 1372//#endif 1373// -- END ML 1374 1375//ML MOVED ICONIZE BUTTON TO BOTTOM -- 1376 if (by+BUTTON_H > h()-BUTTON_BOTTOM || transient_for()) { 1377 label_h = h()-BOTTOM-by; 1378 iconize_button.hide(); 1379 } else { 1380 iconize_button.show(); 1381 iconize_button.position(BUTTON_LEFT,h()-(BUTTON_BOTTOM+BUTTON_H)); 1382 label_h = iconize_button.y()-by; 1383 } 1384// -- END ML 1385} 1386#endif 1387 1388// make sure fltk does not try to set the window size: 1389void Frame::resize(int, int, int, int) {} 1390// For fltk2.0: 1391void Frame::layout() { 1392#if FL_MAJOR_VERSION>1 1393 layout_damage(0); // actually this line is not needed in newest cvs fltk2.0 1394#endif 1395} 1396 1397//////////////////////////////////////////////////////////////// 1398 1399void Frame::close() { 1400 if (flag(DELETE_WINDOW_PROTOCOL)) 1401 sendMessage(wm_protocols, wm_delete_window); 1402 else if (flag(QUIT_PROTOCOL)) 1403 sendMessage(wm_protocols, _wm_quit_app); 1404 else 1405 kill(); 1406} 1407 1408void Frame::kill() { 1409 XKillClient(fl_display, window_); 1410} 1411 1412// this is called when window manager exits: 1413void Frame::save_protocol() { 1414 Frame* f; 1415 for (f = first; f; f = f->next) if (f->flag(SAVE_PROTOCOL)) { 1416 f->set_state_flag(SAVE_PROTOCOL_WAIT); 1417 f->sendMessage(wm_protocols, wm_save_yourself); 1418 } 1419 double t = 10.0; // number of seconds to wait before giving up 1420 while (t > 0.0) { 1421 for (f = first; ; f = f->next) { 1422 if (!f) return; 1423 if (f->flag(SAVE_PROTOCOL) && f->state_flags_&SAVE_PROTOCOL_WAIT) break; 1424 } 1425 t = Fl::wait(t); 1426 } 1427} 1428 1429//////////////////////////////////////////////////////////////// 1430// Drawing code: 1431#if FL_MAJOR_VERSION>1 1432# include <fltk/Box.h> 1433#endif 1434 1435#ifdef TOPSIDE 1436void Frame::draw() { 1437 if (flag(NO_BORDER)) return; 1438 //ML--------------------- Paint opaque titlebar background 1439 labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); 1440 if (active()) { 1441 fl_rectf(2, 2, w() - 4, h()-4, 1442 fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6) 1443// (ACTIVE_COLOR >> 16) & 0xFF, (ACTIVE_COLOR >> 8) & 0xFF, ACTIVE_COLOR & 0xFF 1444 ); 1445 } 1446 else { 1447 fl_rectf(2, 2, w() - 4, h()-4, 1448 FL_BACKGROUND2_COLOR 1449 //FL_GRAY 1450 ); 1451 } 1452 // ------------------ML 1453 1454 if (!flag(THIN_BORDER)) Fl_Window::draw(); 1455 if (damage() != FL_DAMAGE_CHILD) { 1456 1457#ifdef ACTIVE_COLOR 1458 fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h()); 1459 if (active()) { 1460 fl_color(FL_GRAY_RAMP+('N'-'A')); 1461 fl_xyline(2, h()-3, w()-3, 2); 1462 } 1463#else 1464 fl_frame("AAAAWWJJTTNN",0,0,w(),h()); 1465#endif 1466 if (!flag(THIN_BORDER) && label_h > 3) { 1467 fl_color(labelcolor()); 1468 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE); 1469 fl_draw(label(), label_x, BUTTON_TOP, label_w, BUTTON_H, 1470 Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_CLIP), 0, 0); 1471 } 1472 } 1473} 1474#else 1475void Frame::draw() { 1476 if (flag(NO_BORDER)) return; 1477 //ML--------------------- Paint opaque titlebar background 1478 labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); 1479 if (active()) { 1480 fl_rectf(2, 2, w() - 4, h()-4, 1481 fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6) ); // ML 1482 //(ACTIVE_COLOR >> 16) & 0xFF, (ACTIVE_COLOR >> 8) & 0xFF, ACTIVE_COLOR & 0xFF); 1483 } 1484 else { 1485 fl_rectf(2, 2, w() - 4, h()-4, FL_BACKGROUND2_COLOR); // ML 1486 } 1487 // ------------------ML 1488 1489 if (!flag(THIN_BORDER)) Fl_Window::draw(); 1490 if (damage() != FL_DAMAGE_CHILD) { 1491#ifdef ACTIVE_COLOR 1492 fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h()); 1493 if (active()) { 1494 fl_color(FL_GRAY_RAMP+('N'-'A')); 1495 fl_xyline(2, h()-3, w()-3, 2); 1496 } 1497#else 1498# if FL_MAJOR_VERSION>1 1499 static fltk::FrameBox framebox(0,"AAAAJJWWNNTT"); 1500 drawstyle(style(),fltk::INVISIBLE); // INVISIBLE = draw edge only 1501 framebox.draw(Rectangle(w(),h())); 1502# else 1503 fl_frame("AAAAWWJJTTNN",0,0,w(),h()); 1504# endif 1505#endif 1506 if (!flag(THIN_BORDER) && label_h > 3) { 1507#ifdef SHOW_CLOCK 1508 if (active()) { 1509 int clkw = int(fl_width(clock_buf)); 1510 if (clock_alarm_on) { 1511 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE); 1512 fl_rectf(LEFT-1, label_y + label_h - 3 - clkw, TITLE_WIDTH, clkw, 1513 (ALARM_BG_COLOR>>16)&0xff, 1514 (ALARM_BG_COLOR>>8)&0xff, 1515 ALARM_BG_COLOR&0xff); 1516 fl_color((ALARM_FG_COLOR>>16)&0xff, 1517 (ALARM_FG_COLOR>>8)&0xff, 1518 ALARM_FG_COLOR&0xff); 1519 } else 1520 fl_font(MENU_FONT_SLOT, TITLE_FONT_SIZE); 1521 // This might overlay the label if the label is long enough 1522 // and the window height is short enough. For now, we'll 1523 // assume this is not enough of a problem to be concerned 1524 // about. 1525 draw_rotated90(clock_buf, 1, label_y+3, left-1, label_h-6, 1526 Fl_Align(FL_ALIGN_BOTTOM|FL_ALIGN_CLIP)); 1527 } else 1528 // Only show the clock on the active frame. 1529 XClearArea(fl_display, fl_xid(this), 1, label_y+3, 1530 left-1, label_h-3, 0); 1531#endif 1532 fl_color(labelcolor()); 1533 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE); 1534 draw_rotated90(label(), 1, label_y+3, left-1, label_h-3, 1535 Fl_Align(FL_ALIGN_TOP|FL_ALIGN_CLIP)); 1536 } 1537 } 1538} 1539#endif 1540 1541#ifdef SHOW_CLOCK 1542void Frame::redraw_clock() { 1543 double clkw = fl_width(clock_buf); 1544 XClearArea(fl_display, fl_xid(this), 1545 1, label_y+label_h-3-(int)clkw, 1546 left-1, (int)clkw, 1); 1547} 1548#endif 1549 1550void FrameButton::draw() { 1551#if FL_MAJOR_VERSION>1 1552 const int x = value()?1:0; 1553 const int y = x; 1554 drawstyle(style(),flags()|fltk::OUTPUT); 1555 FL_UP_BOX->draw(Rectangle(w(),h())); 1556#else 1557 const int x = this->x(); 1558 const int y = this->y(); 1559 Fl_Widget::draw_box(value() ? FL_DOWN_FRAME : FL_UP_FRAME, 1560 value() ? fl_darker(FL_BACKGROUND2_COLOR) 1561 : fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6)); // ML 1562// Fl_Widget::draw_box(value() ? FL_DOWN_FRAME : FL_UP_FRAME, FL_GRAY); 1563#endif 1564 fl_color(parent()->labelcolor()); 1565 switch (label()[0]) { 1566 case 'W': 1567#if MINIMIZE_ARROW 1568 fl_line (x+2,y+(h())/2,x+w()-4,y+h()/2); 1569 fl_line (x+2,y+(h())/2,x+2+4,y+h()/2+4); 1570 fl_line (x+2,y+(h())/2,x+2+4,y+h()/2-4); 1571#else 1572 fl_rect(x+(h()-7)/2,y+3,2,h()-6); 1573#endif 1574 return; 1575 case 'w': 1576 fl_rect(x+2,y+(h()-7)/2,w()-4,7); 1577 return; 1578 case 'h': 1579 fl_rect(x+(h()-7)/2,y+2,7,h()-4); 1580 return; 1581 case 'X': 1582#if CLOSE_X 1583 fl_line(x+2,y+3,x+w()-5,y+h()-4); 1584 fl_line(x+3,y+3,x+w()-4,y+h()-4); 1585 fl_line(x+2,y+h()-4,x+w()-5,y+3); 1586 fl_line(x+3,y+h()-4,x+w()-4,y+3); 1587#endif 1588#if CLOSE_HITTITE_LIGHTNING 1589 fl_arc(x+3,y+3,w()-6,h()-6,0,360); 1590 fl_line(x+7,y+3, x+7,y+11); 1591#endif 1592 return; 1593 case 'i': 1594#if ICONIZE_BOX 1595 fl_rect(x+w()/2-1,y+h()/2-1,3,3); 1596#endif 1597 return; 1598 } 1599} 1600 1601//////////////////////////////////////////////////////////////// 1602// User interface code: 1603 1604// this is called when user clicks the buttons: 1605void Frame::button_cb(Fl_Button* b) { 1606 switch (b->label()[0]) { 1607 case 'W': // minimize button 1608 if (b->value()) { 1609 if (!max_w_button.value()) { 1610 restore_x = x()+left; 1611 restore_y = y()+top; 1612#if MINIMIZE_HEIGHT 1613 restore_w=w()-dwidth; 1614 restore_h = h()-dwidth; 1615#endif 1616 } 1617#if MINIMIZE_HEIGHT 1618#ifdef TOPSIDE 1619 set_size(x(), y(), dwidth-1, 350,1);// <-- crude hack for now 1620#else 1621 set_size(x(), y(), dwidth-1, 1622 min(h(),min(350,label_w+3*BUTTON_H+BUTTON_TOP+BUTTON_BOTTOM)), 1623 1); 1624#endif // not topside 1625#else 1626 set_size(x(), y(), dwidth-1, h(), 1); 1627#endif 1628 } else { 1629#if MINIMIZE_HEIGHT 1630 set_size(x(), y(), restore_w+dwidth, restore_h+dwidth, 1); 1631#else 1632 set_size(x(), y(), restore_w+dwidth, h(), 1); 1633#endif 1634 } 1635 show_hide_buttons(); 1636 break; 1637 case 'w': // max-width button 1638 if (b->value()) { 1639 if (!min_w_button.value()) {restore_x=x()+left; restore_w=w()-dwidth;} 1640 int W = maximize_width()+dwidth; 1641 int X = force_x_onscreen(x() + (w()-W)/2, W); 1642 set_size(X, y(), W, h(), 3); 1643 } else { 1644 set_size(restore_x-left, y(), restore_w+dwidth, h(), 1); 1645 } 1646 show_hide_buttons(); 1647 break; 1648 case 'h': // max-height button 1649 if (b->value()) { 1650 restore_y = y()+top; 1651 restore_h = h()-dwidth; 1652 int H = maximize_height()+dheight; 1653 int Y = force_y_onscreen(y() + (h()-H)/2, H); 1654 set_size(x(), Y, w(), H, 3); 1655 } else { 1656 set_size(x(), restore_y-top, w(), restore_h+dwidth, 1); 1657 } 1658 break; 1659 case 'X': 1660 close(); 1661 break; 1662 default: // iconize button 1663 iconize(); 1664 break; 1665 } 1666} 1667 1668// static callback for fltk: 1669void Frame::button_cb_static(Fl_Widget* w, void*) { 1670 ((Frame*)(w->parent()))->button_cb((Fl_Button*)w); 1671} 1672 1673// This method figures out what way the mouse will resize the window. 1674// It is used to set the cursor and to actually control what you grab. 1675// If the window cannot be resized in some direction this should not 1676// return that direction. 1677int Frame::mouse_location() { 1678 int x = Fl::event_x(); 1679 int y = Fl::event_y(); 1680 int r = 0; 1681 if (flag(NO_RESIZE)) return 0; 1682 if (min_h != max_h) { 1683 if (y < RESIZE_EDGE) r |= FL_ALIGN_TOP; 1684 else if (y >= h()-RESIZE_EDGE) r |= FL_ALIGN_BOTTOM; 1685 } 1686 if (min_w != max_w) { 1687#if RESIZE_LEFT 1688 if (x < RESIZE_EDGE) r |= FL_ALIGN_LEFT; 1689#else 1690 if (x < RESIZE_EDGE && r) r |= FL_ALIGN_LEFT; 1691#endif 1692 else if (x >= w()-RESIZE_EDGE) r |= FL_ALIGN_RIGHT; 1693 } 1694 return r; 1695} 1696 1697// set the cursor correctly for a return value from mouse_location(): 1698void Frame::set_cursor(int r) { 1699// Fl_Cursor c = r ? FL_CURSOR_ARROW : FL_CURSOR_MOVE; 1700 Fl_Cursor c = r ? FL_CURSOR_ARROW : FL_CURSOR_ARROW; 1701 switch (r) { 1702 case FL_ALIGN_TOP: 1703 case FL_ALIGN_BOTTOM: 1704 c = FL_CURSOR_NS; 1705 break; 1706 case FL_ALIGN_LEFT: 1707 case FL_ALIGN_RIGHT: 1708 c = FL_CURSOR_WE; 1709 break; 1710 case FL_ALIGN_LEFT|FL_ALIGN_TOP: 1711 case FL_ALIGN_RIGHT|FL_ALIGN_BOTTOM: 1712 c = FL_CURSOR_NWSE; 1713 break; 1714 case FL_ALIGN_LEFT|FL_ALIGN_BOTTOM: 1715 case FL_ALIGN_RIGHT|FL_ALIGN_TOP: 1716 c = FL_CURSOR_NESW; 1717 break; 1718 } 1719//#if FL_MAJOR_VERSION>1 1720 cursor(c); 1721//#else 1722// static Frame* previous_frame; 1723// static Fl_Cursor previous_cursor; 1724// if (this != previous_frame || c != previous_cursor) { 1725// previous_frame = this; 1726// previous_cursor = c; 1727// cursor(c, CURSOR_FG_SLOT, CURSOR_BG_SLOT); 1728// } 1729//#endif 1730} 1731 1732#ifdef AUTO_RAISE 1733// timeout callback to cause autoraise: 1734void auto_raise(void*) { 1735 if (Frame::activeFrame() && !Fl::grab() && !Fl::pushed()) 1736 Frame::activeFrame()->raise(); 1737} 1738#endif 1739 1740extern void ShowMenu(); 1741 1742// If cursor is in the contents of a window this is set to that window. 1743// This is only used to force the cursor to an arrow even though X keeps 1744// sending mysterious erroneous move events: 1745static Frame* cursor_inside = 0; 1746 1747// Handle an fltk event. 1748int Frame::handle(int e) { 1749 static int what, dx, dy, ix, iy, iw, ih; 1750 // see if child widget handles event: 1751#if FL_MAJOR_VERSION > 1 1752 if (fltk::Group::handle(e) && e != FL_ENTER && e != FL_MOVE) { 1753 if (e == FL_PUSH) set_cursor(-1); 1754 return 1; 1755 } 1756#else 1757 if (Fl_Group::handle(e) && e != FL_ENTER && e != FL_MOVE) { 1758 if (e == FL_PUSH) set_cursor(-1); 1759 return 1; 1760 } 1761#endif 1762 switch (e) { 1763 1764 case FL_SHOW: 1765 case FL_HIDE: 1766 return 0; // prevent fltk from messing things up 1767 1768 case FL_ENTER: 1769#if !CLICK_TO_TYPE 1770 if (Fl::pushed() || Fl::grab()) return 1; 1771 if (activate()) { 1772#ifdef AUTO_RAISE 1773 Fl::remove_timeout(auto_raise); 1774 Fl::add_timeout(AUTO_RAISE, auto_raise); 1775#endif 1776 } 1777#endif 1778 goto GET_CROSSINGS; 1779 1780 case FL_LEAVE: 1781#if !CLICK_TO_TYPE && !STICKY_FOCUS 1782 if (active()) { 1783 deactivate(); 1784 XSetInputFocus(fl_display, PointerRoot, RevertToPointerRoot, 1785 fl_event_time); 1786 active_ = 0; 1787 } 1788#endif 1789 goto GET_CROSSINGS; 1790 1791 case FL_MOVE: 1792 GET_CROSSINGS: 1793 // ML -- CLEAR ANY SPECIAL CURSOR IF JUST MOVING: 1794 set_cursor(-1); 1795/* ML --COMMENTED OUT SPECIAL CURSOR CODE... MAYBE fl_xevent->type NOT SETUP??? 1796 // set cursor_inside to true when the mouse is inside a window 1797 // set it false when mouse is on a frame or outside a window. 1798 // fltk mangles the X enter/leave events, we need the original ones: 1799 1800 switch (fl_xevent->type) { 1801 case LeaveNotify: 1802 if (fl_xevent->xcrossing.detail == NotifyInferior) { 1803 // cursor moved from frame to interior 1804 cursor_inside = this; 1805 break; 1806 } else { 1807 // cursor moved to another window 1808 return 1; 1809 } 1810 1811 case EnterNotify: 1812 // see if cursor skipped over frame and directly to interior: 1813 if (fl_xevent->xcrossing.detail == NotifyVirtual || 1814 fl_xevent->xcrossing.detail == NotifyNonlinearVirtual) 1815 cursor_inside = this; 1816 else { 1817 // cursor is now pointing at frame: 1818 cursor_inside = 0; 1819 } 1820 } 1821 if (Fl::belowmouse() != this || cursor_inside == this) 1822 set_cursor(-1); 1823 else 1824 set_cursor(mouse_location()); 1825--END ML */ 1826 return 1; 1827 1828 case FL_PUSH: 1829 if (Fl::event_button() > 2) { 1830 set_cursor(-1); 1831 ShowMenu(); 1832 return 1; 1833 } 1834 ix = x(); iy = y(); iw = w(); ih = h(); 1835 if (!max_w_button.value() && !min_w_button.value()) { 1836 restore_x = ix+left; restore_w = iw-dwidth; 1837 } 1838#if MINIMIZE_HEIGHT 1839 if (!min_w_button.value()) 1840#endif 1841 if (!max_h_button.value()) { 1842 restore_y = iy+top; restore_h = ih-dwidth; 1843 } 1844 what = mouse_location(); 1845 if (Fl::event_button() > 1) what = 0; // middle button does drag 1846 dx = Fl::event_x_root()-ix; 1847 if (what & FL_ALIGN_RIGHT) dx -= iw; 1848 dy = Fl::event_y_root()-iy; 1849 if (what & FL_ALIGN_BOTTOM) dy -= ih; 1850 set_cursor(what); 1851 return 1; 1852 case FL_DRAG: 1853 if (Fl::event_is_click()) return 1; // don't drag yet 1854 case FL_RELEASE: 1855 if (Fl::event_is_click()) { 1856 if (Fl::grab()) return 1; 1857#if CLICK_TO_TYPE 1858 if (activate()) { 1859 if (Fl::event_button() <= 1) raise(); 1860 return 1; 1861 } 1862#endif 1863 if (Fl::event_button() > 1) lower(); else raise(); 1864 } else if (!what) { 1865 int nx = Fl::event_x_root()-dx; 1866 int W = Root->x()+Root->w(); 1867 if (nx+iw > W && nx+iw < W+SCREEN_SNAP) { 1868 int t = W+1-iw; 1869 if (iw >= Root->w() || x() > t || nx+iw >= W+EDGE_SNAP) 1870 t = W+(dwidth-left)-iw; 1871 if (t >= x() && t < nx) nx = t; 1872 } 1873 int X = Root->x(); 1874 if (nx < X && nx > X-SCREEN_SNAP) { 1875 int t = X-1; 1876 if (iw >= Root->w() || x() < t || nx <= X-EDGE_SNAP) t = X-BUTTON_LEFT; 1877 if (t <= x() && t > nx) nx = t; 1878 } 1879 int ny = Fl::event_y_root()-dy; 1880 int H = Root->y()+Root->h(); 1881 if (ny+ih > H && ny+ih < H+SCREEN_SNAP) { 1882 int t = H+1-ih; 1883 if (ih >= Root->h() || y() > t || ny+ih >= H+EDGE_SNAP) 1884 t = H+(dheight-top)-ih; 1885 if (t >= y() && t < ny) ny = t; 1886 } 1887 int Y = Root->y(); 1888 if (ny < Y && ny > Y-SCREEN_SNAP) { 1889 int t = Y-1; 1890 if (ih >= H || y() < t || ny <= Y-EDGE_SNAP) t = Y-top; 1891 if (t <= y() && t > ny) ny = t; 1892 } 1893 set_size(nx, ny, iw, ih); 1894 } else { 1895 int nx = ix; 1896 int ny = iy; 1897 int nw = iw; 1898 int nh = ih; 1899 if (what & FL_ALIGN_RIGHT) 1900 nw = Fl::event_x_root()-dx-nx; 1901 else if (what & FL_ALIGN_LEFT) 1902 nw = ix+iw-(Fl::event_x_root()-dx); 1903 else {nx = x(); nw = w();} 1904 if (what & FL_ALIGN_BOTTOM) 1905 nh = Fl::event_y_root()-dy-ny; 1906 else if (what & FL_ALIGN_TOP) 1907 nh = iy+ih-(Fl::event_y_root()-dy); 1908 else {ny = y(); nh = h();} 1909 if (flag(KEEP_ASPECT)) { 1910 if (nw-dwidth > nh-dwidth 1911 && (what&(FL_ALIGN_LEFT|FL_ALIGN_RIGHT)) 1912 || !(what&(FL_ALIGN_TOP|FL_ALIGN_BOTTOM))) 1913 nh = nw-dwidth+dheight; 1914 else 1915 nw = nh-dheight+dwidth; 1916 } 1917 int MINW = min_w+dwidth; 1918 if (nw <= dwidth && dwidth > TITLE_WIDTH) { 1919 nw = dwidth-1; 1920#if MINIMIZE_HEIGHT 1921 restore_h = nh; 1922#endif 1923 } else { 1924 if (inc_w > 1) nw = ((nw-MINW+inc_w/2)/inc_w)*inc_w+MINW; 1925 if (nw < MINW) nw = MINW; 1926 else if (max_w && nw > max_w+dwidth) nw = max_w+dwidth; 1927 } 1928 int MINH = min_h+dheight; 1929 const int MINH_B = BUTTON_H+BUTTON_TOP+BOTTOM; 1930 if (MINH_B > MINH) MINH = MINH_B; 1931 if (inc_h > 1) nh = ((nh-MINH+inc_h/2)/inc_h)*inc_h+MINH; 1932 if (nh < MINH) nh = MINH; 1933 else if (max_h && nh > max_h+dheight) nh = max_h+dheight; 1934 if (what & FL_ALIGN_LEFT) nx = ix+iw-nw; 1935 if (what & FL_ALIGN_TOP) ny = iy+ih-nh; 1936 set_size(nx,ny,nw,nh); 1937 } 1938 return 1; 1939 } 1940 return 0; 1941} 1942 1943// Handle events that fltk did not recognize (mostly ones directed 1944// at the desktop): 1945 1946int Frame::handle(const XEvent* ei) { 1947 1948 switch (ei->type) { 1949 1950 case ConfigureRequest: { 1951 const XConfigureRequestEvent* e = &(ei->xconfigurerequest); 1952 unsigned long mask = e->value_mask; 1953 if (mask & CWBorderWidth) app_border_width = e->border_width; 1954 // Try to detect if the application is really trying to move the 1955 // window, or is simply echoing it's postion, possibly with some 1956 // variation (such as echoing the parent window position), and 1957 // dont' move it in that case: 1958 int X = (mask & CWX && e->x != x()) ? e->x+app_border_width-left : x(); 1959 int Y = (mask & CWY && e->y != y()) ? e->y+app_border_width-top : y(); 1960 int W = (mask & CWWidth) ? e->width+dwidth : w(); 1961 int H = (mask & CWHeight) ? e->height+dheight : h(); 1962 // Generally we want to obey any application positioning of the 1963 // window, except when it appears the app is trying to position 1964 // the window "at the edge". 1965 if (!(mask & CWX) || (X >= -2*left && X < 0)) X = force_x_onscreen(X,W); 1966 if (!(mask & CWY) || (Y >= -2*top && Y < 0)) Y = force_y_onscreen(Y,H); 1967 // Fix Rick Sayre's program that resizes it's windows bigger than the 1968 // maximum size: 1969 if (W > max_w+dwidth) max_w = 0; 1970 if (H > max_h+dheight) max_h = 0; 1971 set_size(X, Y, W, H, 2); 1972 if (e->value_mask & CWStackMode && e->detail == Above && state()==NORMAL) 1973 raise(); 1974 return 1;} 1975 1976 case MapRequest: { 1977 //const XMapRequestEvent* e = &(ei->xmaprequest); 1978 raise(); 1979 return 1;} 1980 1981 case UnmapNotify: { 1982 const XUnmapEvent* e = &(ei->xunmap); 1983 if (e->window == window_ && !e->from_configure) { 1984 if (state_flags_&IGNORE_UNMAP) clear_state_flag(IGNORE_UNMAP); 1985 else state(UNMAPPED); 1986 } 1987 return 1;} 1988 1989 case DestroyNotify: { 1990 //const XDestroyWindowEvent* e = &(ei->xdestroywindow); 1991 delete this; 1992 return 1;} 1993 1994 case ReparentNotify: { 1995 const XReparentEvent* e = &(ei->xreparent); 1996 if (e->parent==fl_xid(this)) return 1; // echo 1997 if (e->parent==fl_xid(Root)) return 1; // app is trying to tear-off again? 1998 delete this; // guess they are trying to paste tear-off thing back? 1999 return 1;} 2000 2001 case ClientMessage: { 2002 const XClientMessageEvent* e = &(ei->xclient); 2003 if (e->message_type == wm_change_state && e->format == 32) { 2004 if (e->data.l[0] == NormalState) raise(); 2005 else if (e->data.l[0] == IconicState) iconize(); 2006 } else 2007 // we may want to ignore _WIN_LAYER from xmms? 2008 Fl::warning("flwm: unexpected XClientMessageEvent, type 0x%lx, " 2009 "window 0x%lx\n", e->message_type, e->window); 2010 return 1;} 2011 2012 case ColormapNotify: { 2013 const XColormapEvent* e = &(ei->xcolormap); 2014 if (e->c_new) { // this field is called "new" in the old C++-unaware Xlib 2015 colormap = e->colormap; 2016 if (active()) installColormap(); 2017 } 2018 return 1;} 2019 2020 case PropertyNotify: { 2021 const XPropertyEvent* e = &(ei->xproperty); 2022 Atom a = e->atom; 2023 2024 // case XA_WM_ICON_NAME: (do something similar to name) 2025 if (a == XA_WM_NAME) { 2026 getLabel(e->state == PropertyDelete); 2027 2028 } else if (a == wm_state) { 2029 // it's not clear if I really need to look at this. Need to make 2030 // sure it is not seeing the state echoed by the application by 2031 // checking for it being different... 2032 switch (getIntProperty(wm_state, wm_state, state())) { 2033 case IconicState: 2034 if (state() == NORMAL || state() == OTHER_DESKTOP) iconize(); break; 2035 case NormalState: 2036 if (state() != NORMAL && state() != OTHER_DESKTOP) raise(); break; 2037 } 2038 2039 } else if (a == wm_colormap_windows) { 2040 getColormaps(); 2041 if (active()) installColormap(); 2042 2043 } else if (a == _motif_wm_hints) { 2044 // some #%&%$# SGI Motif programs change this after mapping the window! 2045 // :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( 2046 if (getMotifHints()) { // returns true if any flags changed 2047 fix_transient_for(); 2048 updateBorder(); 2049 show_hide_buttons(); 2050 } 2051 2052 } else if (a == wm_protocols) { 2053 getProtocols(); 2054 // get Motif hints since they may do something with QUIT: 2055 getMotifHints(); 2056 2057 } else if (a == XA_WM_NORMAL_HINTS || a == XA_WM_SIZE_HINTS) { 2058 getSizes(); 2059 show_hide_buttons(); 2060 2061 } else if (a == XA_WM_TRANSIENT_FOR) { 2062 XGetTransientForHint(fl_display, window_, &transient_for_xid); 2063 fix_transient_for(); 2064 show_hide_buttons(); 2065 2066 } else if (a == XA_WM_COMMAND) { 2067 clear_state_flag(SAVE_PROTOCOL_WAIT); 2068 2069 } 2070 return 1;} 2071 2072 } 2073 return 0; 2074} 2075 2076//////////////////////////////////////////////////////////////// 2077// X utility routines: 2078 2079void* Frame::getProperty(Atom a, Atom type, int* np) const { 2080 return ::getProperty(window_, a, type, np); 2081} 2082 2083void* getProperty(XWindow w, Atom a, Atom type, int* np) { 2084 Atom realType; 2085 int format; 2086 unsigned long n, extra; 2087 int status; 2088 uchar* prop; 2089 status = XGetWindowProperty(fl_display, w, 2090 a, 0L, 256L, False, type, &realType, 2091 &format, &n, &extra, &prop); 2092 if (status != Success) return 0; 2093 if (!prop) return 0; 2094 if (!n) {XFree(prop); return 0;} 2095 if (np) *np = (int)n; 2096 return (void*)prop; 2097} 2098 2099int Frame::getIntProperty(Atom a, Atom type, int deflt) const { 2100 return ::getIntProperty(window_, a, type, deflt); 2101} 2102 2103int getIntProperty(XWindow w, Atom a, Atom type, int deflt) { 2104 void* prop = getProperty(w, a, type); 2105 if (!prop) return deflt; 2106 int r = int(*(long*)prop); 2107 XFree(prop); 2108 return r; 2109} 2110 2111void setProperty(XWindow w, Atom a, Atom type, int v) { 2112 long prop = v; 2113 XChangeProperty(fl_display, w, a, type, 32, PropModeReplace, (uchar*)&prop,1); 2114} 2115 2116void Frame::setProperty(Atom a, Atom type, int v) const { 2117 ::setProperty(window_, a, type, v); 2118} 2119 2120void Frame::sendMessage(Atom a, Atom l) const { 2121 XEvent ev; 2122 long mask; 2123 memset(&ev, 0, sizeof(ev)); 2124 ev.xclient.type = ClientMessage; 2125 ev.xclient.window = window_; 2126 ev.xclient.message_type = a; 2127 ev.xclient.format = 32; 2128 ev.xclient.data.l[0] = long(l); 2129 ev.xclient.data.l[1] = long(fl_event_time); 2130 mask = 0L; 2131 XSendEvent(fl_display, window_, False, mask, &ev); 2132} 2133