test not only if header frei0r.h exists, also use an item
[mlt-frei0r-support.git] / shotcut / wm / Frame.cpp
blob38112b7c5d94ffe840d99835fccdbabfc2a03303
1 // Frame.C
3 #include "config.h"
4 #include "Frame.h"
5 #include "Desktop.h"
6 #include <string.h>
7 #include <stdio.h>
8 #include <FL/fl_draw.H>
9 #include "Rotated.h"
11 static Atom wm_state = 0;
12 static Atom net_wm_state = 0;
13 static Atom wm_maximize_vert;
14 static Atom wm_maximize_horz;
15 static Atom wm_change_state;
16 static Atom wm_protocols;
17 static Atom wm_delete_window;
18 static Atom wm_take_focus;
19 static Atom wm_save_yourself;
20 static Atom wm_colormap_windows;
21 static Atom _motif_wm_hints;
22 static Atom kwm_win_decoration;
23 #if DESKTOPS
24 static Atom kwm_win_desktop;
25 static Atom kwm_win_sticky;
26 #endif
27 //static Atom wm_client_leader;
28 static Atom _wm_quit_app;
30 // these are set by initialize in main.C:
31 Atom _win_hints;
32 Atom _win_state;
33 #if DESKTOPS
34 extern Atom _win_workspace;
35 #endif
37 #ifdef SHOW_CLOCK
38 extern char clock_buf[];
39 extern int clock_alarm_on;
40 #endif
42 static const int XEventMask =
43 ExposureMask|StructureNotifyMask
44 |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
45 |ButtonPressMask|ButtonReleaseMask
46 |EnterWindowMask|LeaveWindowMask
47 |PointerMotionMask|SubstructureRedirectMask|SubstructureNotifyMask;
49 extern Fl_Window* Root;
51 Frame* Frame::active_;
52 Frame* Frame::first;
54 static inline int max(int a, int b) {return a > b ? a : b;}
55 static inline int min(int a, int b) {return a < b ? a : b;}
57 ////////////////////////////////////////////////////////////////
58 // The constructor is by far the most complex part, as it collects
59 // all the scattered pieces of information about the window that
60 // X has and uses them to initialize the structure, position the
61 // window, and then finally create it.
63 int dont_set_event_mask = 0; // used by FrameWindow
65 // "existing" is a pointer to an XWindowAttributes structure that is
66 // passed for an already-existing window when the window manager is
67 // starting up. If so we don't want to alter the state, size, or
68 // position. If null than this is a MapRequest of a new window.
69 Frame::Frame(Window window, XWindowAttributes* existing) :
70 Fl_Window(0,0),
71 window_(window),
72 state_flags_(0),
73 flags_(0),
74 transient_for_xid(None),
75 transient_for_(0),
76 revert_to(active_),
77 colormapWinCount(0),
78 close_button(BUTTON_LEFT,BUTTON_TOP,BUTTON_W,BUTTON_H,"X"),
79 iconize_button(BUTTON_LEFT,BUTTON_TOP+BUTTON_H,BUTTON_W,BUTTON_H,"i"),
80 max_w_button(BUTTON_LEFT,BUTTON_TOP+2*BUTTON_H,BUTTON_W,BUTTON_H,"w"),
81 min_w_button(BUTTON_LEFT,BUTTON_TOP+3*BUTTON_H,BUTTON_W,BUTTON_H,"W")
83 close_button.callback(button_cb_static);
84 iconize_button.callback(button_cb_static);
85 max_w_button.type(FL_TOGGLE_BUTTON);
86 max_w_button.callback(button_cb_static);
87 min_w_button.type(FL_TOGGLE_BUTTON);
88 min_w_button.callback(button_cb_static);
89 end();
90 box(FL_NO_BOX); // relies on background color erasing interior
91 next = first;
92 first = this;
94 // do this asap so we don't miss any events...
95 if (!dont_set_event_mask)
96 XSelectInput(fl_display, window_,
97 ColormapChangeMask | PropertyChangeMask | FocusChangeMask
100 if (!wm_state) {
101 // allocate all the atoms if this is the first time
102 wm_state = XInternAtom(fl_display, "WM_STATE", 0);
103 net_wm_state = XInternAtom(fl_display, "_NET_WM_STATE", 0);
104 wm_maximize_vert = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
105 wm_maximize_horz = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
106 wm_change_state = XInternAtom(fl_display, "WM_CHANGE_STATE", 0);
107 wm_protocols = XInternAtom(fl_display, "WM_PROTOCOLS", 0);
108 wm_delete_window = XInternAtom(fl_display, "WM_DELETE_WINDOW", 0);
109 wm_take_focus = XInternAtom(fl_display, "WM_TAKE_FOCUS", 0);
110 wm_save_yourself = XInternAtom(fl_display, "WM_SAVE_YOURSELF", 0);
111 wm_colormap_windows = XInternAtom(fl_display, "WM_COLORMAP_WINDOWS",0);
112 _motif_wm_hints = XInternAtom(fl_display, "_MOTIF_WM_HINTS", 0);
113 kwm_win_decoration = XInternAtom(fl_display, "KWM_WIN_DECORATION", 0);
114 #if DESKTOPS
115 kwm_win_desktop = XInternAtom(fl_display, "KWM_WIN_DESKTOP", 0);
116 kwm_win_sticky = XInternAtom(fl_display, "KWM_WIN_STICKY", 0);
117 #endif
118 // wm_client_leader = XInternAtom(fl_display, "WM_CLIENT_LEADER", 0);
119 _wm_quit_app = XInternAtom(fl_display, "_WM_QUIT_APP", 0);
122 label_y = label_h = label_w = 0;
123 getLabel();
124 // getIconLabel();
126 {XWindowAttributes attr;
127 if (existing) attr = *existing;
128 else {
129 // put in some legal values in case XGetWindowAttributes fails:
130 attr.x = attr.y = 0; attr.width = attr.height = 100;
131 attr.colormap = fl_colormap;
132 attr.border_width = 0;
133 XGetWindowAttributes(fl_display, window, &attr);
135 left = top = dwidth = dheight = 0; // pretend border is zero-width for now
136 app_border_width = attr.border_width;
137 x(attr.x+app_border_width); restore_x = x();
138 y(attr.y+app_border_width); restore_y = y();
139 w(attr.width); restore_w = w();
140 h(attr.height); restore_h = h();
141 colormap = attr.colormap;}
143 // TRULY HORRIBLE HACK TO WORK AROUND Y < 20...
144 if ( y( ) == 20 ) y( 0 );
146 getColormaps();
148 //group_ = 0;
149 {XWMHints* hints = XGetWMHints(fl_display, window_);
150 if (hints) {
151 if ((hints->flags & InputHint) && !hints->input) set_flag(NO_FOCUS);
152 //if (hints && hints->flags&WindowGroupHint) group_ = hints->window_group;
154 switch (getIntProperty(wm_state, wm_state, 0)) {
155 case NormalState:
156 state_ = NORMAL; break;
157 case IconicState:
158 state_ = ICONIC; break;
159 // X also defines obsolete values ZoomState and InactiveState
160 default:
161 if (hints && (hints->flags&StateHint) && hints->initial_state==IconicState)
162 state_ = ICONIC;
163 else
164 state_ = NORMAL;
166 if (hints) XFree(hints);}
167 // Maya sets this, seems to mean the same as group:
168 // if (!group_) group_ = getIntProperty(wm_client_leader, XA_WINDOW);
170 XGetTransientForHint(fl_display, window_, &transient_for_xid);
172 getProtocols();
174 getMotifHints();
176 // get Gnome hints:
177 int p = getIntProperty(_win_hints, XA_CARDINAL);
178 if (p&1) set_flag(NO_FOCUS); // WIN_HINTS_SKIP_FOCUS
179 // if (p&2) // WIN_HINTS_SKIP_WINLIST
180 // if (p&4) // WIN_HINTS_SKIP_TASKBAR
181 // if (p&8) ... // WIN_HINTS_GROUP_TRANSIENT
182 if (p&16) set_flag(CLICK_TO_FOCUS); // WIN_HINTS_FOCUS_ON_CLICK
184 // get KDE hints:
185 p = getIntProperty(kwm_win_decoration, kwm_win_decoration, 1);
186 if (!(p&3)) set_flag(NO_BORDER);
187 else if (p & 2) set_flag(THIN_BORDER);
188 if (p & 256) set_flag(NO_FOCUS);
190 fix_transient_for();
192 if (transient_for()) {
193 if (state_ == NORMAL) state_ = transient_for()->state_;
194 #if DESKTOPS
195 desktop_ = transient_for()->desktop_;
196 #endif
198 #if DESKTOPS
199 // see if anybody thinks window is "sticky:"
200 else if ((getIntProperty(_win_state, XA_CARDINAL) & 1) // WIN_STATE_STICKY
201 || getIntProperty(kwm_win_sticky, kwm_win_sticky)) {
202 desktop_ = 0;
203 } else {
204 // get the desktop from either Gnome or KDE (Gnome takes precedence):
205 p = getIntProperty(_win_workspace, XA_CARDINAL, -1) + 1; // Gnome desktop
206 if (p <= 0) p = getIntProperty(kwm_win_desktop, kwm_win_desktop);
207 if (p > 0 && p < 25)
208 desktop_ = Desktop::number(p, 1);
209 else
210 desktop_ = Desktop::current();
212 if (desktop_ && desktop_ != Desktop::current())
213 if (state_ == NORMAL) state_ = OTHER_DESKTOP;
214 #endif
216 int autoplace = getSizes();
217 // some Motif programs assumme this will force the size to conform :-(
218 if (w() < min_w || h() < min_h) {
219 if (w() < min_w) w(min_w);
220 if (h() < min_h) h(min_h);
221 XResizeWindow(fl_display, window_, w(), h());
224 // try to detect programs that think "transient_for" means "no border":
225 if (transient_for_xid && !label() && !flag(NO_BORDER)) {
226 set_flag(THIN_BORDER);
228 updateBorder();
229 show_hide_buttons();
231 if (autoplace && !existing && !(transient_for() && (x() || y()))) {
232 // autoplacement (stupid version for now)
233 x(Root->x()+(Root->w()-w())/2);
234 y(Root->y()+((Root->h()-20)-h())/2);
235 // move it until it does not hide any existing windows:
236 const int delta = TITLE_WIDTH+LEFT;
237 for (Frame* f = next; f; f = f->next) {
238 if (f->x()+delta > x() && f->y()+delta > y() &&
239 f->x()+f->w()-delta < x()+w() && f->y()+f->h()-delta < y()+h()) {
240 x(max(x(),f->x()+delta));
241 y(max(y(),f->y()+delta));
242 f = this;
246 // move window so contents and border are visible:
247 x(force_x_onscreen(x(), w()));
248 y(force_y_onscreen(y(), h()));
250 // guess some values for the "restore" fields, if already maximized:
251 if (max_w_button.value() || getIntProperty( wm_maximize_horz, wm_maximize_horz, 0 )) {
252 restore_w = min_w + ((w()-dwidth-min_w)/2/inc_w) * inc_w;
253 restore_x = x()+left + (w()-dwidth-restore_w)/2;
254 restore_h = min_h + ((h()-dheight-min_h)/2/inc_h) * inc_h;
255 restore_y = y()+top + (h()-dheight-restore_h)/2;
256 w( maximize_width( ) );
257 h( maximize_height( ) );
260 const int mask = CWBorderPixel | CWColormap | CWEventMask | CWBitGravity
261 | CWBackPixel | CWOverrideRedirect;
262 XSetWindowAttributes sattr;
263 sattr.event_mask = XEventMask;
264 sattr.colormap = fl_colormap;
265 sattr.border_pixel = fl_xpixel(FL_GRAY0);
266 sattr.bit_gravity = NorthWestGravity;
267 sattr.override_redirect = 1;
268 sattr.background_pixel = fl_xpixel(FL_GRAY);
269 Fl_X::set_xid(this, XCreateWindow(fl_display, fl_xid(Root),
270 x(), y(), w(), h(), 0,
271 fl_visual->depth,
272 InputOutput,
273 fl_visual->visual,
274 mask, &sattr));
276 setStateProperty();
278 if (!dont_set_event_mask) XAddToSaveSet(fl_display, window_);
279 if (existing) set_state_flag(IGNORE_UNMAP);
280 XReparentWindow(fl_display, window_, fl_xid(this), left, top);
281 XSetWindowBorderWidth(fl_display, window_, 0);
282 if (state_ == NORMAL) XMapWindow(fl_display, window_);
283 sendConfigureNotify(); // many apps expect this even if window size unchanged
285 #if CLICK_RAISES || CLICK_TO_TYPE
286 XGrabButton(fl_display, AnyButton, AnyModifier, window, False,
287 ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
288 #endif
290 if (state_ == NORMAL) {
291 XMapWindow(fl_display, fl_xid(this));
292 if (!existing) activate_if_transient();
296 // modify the passed X & W to a legal horizontal window position
297 int Frame::force_x_onscreen(int X, int W) {
298 // force all except the black border on-screen:
299 X = min(X, Root->x()+Root->w()+1-W);
300 X = max(X, Root->x()-1);
301 // force the contents on-screen:
302 X = min(X, Root->x()+Root->w()-W+dwidth-left);
303 if (W-dwidth > Root->w() || h()-dheight > (Root->h()-20))
304 // windows bigger than the screen need title bar so they can move
305 X = max(X, Root->x()-LEFT);
306 else
307 X = max(X, Root->x()-left);
308 return X;
311 // modify the passed Y & H to a legal vertical window position:
312 int Frame::force_y_onscreen(int Y, int H) {
313 // force border (except black edge) to be on-screen:
314 Y = min(Y, Root->y()+(Root->h()-20)+1-H);
315 Y = max(Y, Root->y()-1);
316 // force contents to be on-screen:
317 Y = min(Y, Root->y()+(Root->h()-20)-H+dheight-top);
318 Y = max(Y, Root->y()-top);
319 return Y;
322 ////////////////////////////////////////////////////////////////
323 // destructor
324 // The destructor is called on DestroyNotify, so I don't have to do anything
325 // to the contained window, which is already been destroyed.
327 // fltk bug: it does not clear these pointers when window is deleted,
328 // causing flwm to crash on window close sometimes:
329 extern Fl_Window *fl_xfocus;
330 extern Fl_Window *fl_xmousewin;
332 Frame::~Frame() {
334 // It is possible for the frame to be destroyed while the menu is
335 // popped-up, and the menu will still contain a pointer to it. To
336 // fix this the menu checks the state_ location for a legal and
337 // non-withdrawn state value before doing anything. This should
338 // be reliable unless something reallocates the memory and writes
339 // a legal state value to this location:
340 state_ = UNMAPPED;
342 // fix fltk bug:
343 fl_xfocus = 0;
344 fl_xmousewin = 0;
345 Fl::focus_ = 0;
347 // remove any pointers to this:
348 Frame** cp; for (cp = &first; *cp; cp = &((*cp)->next))
349 if (*cp == this) {*cp = next; break;}
350 for (Frame* f = first; f; f = f->next) {
351 if (f->transient_for_ == this) f->transient_for_ = transient_for_;
352 if (f->revert_to == this) f->revert_to = revert_to;
354 throw_focus(1);
356 if (colormapWinCount) {
357 XFree((char *)colormapWindows);
358 delete[] window_Colormaps;
360 //if (iconlabel()) XFree((char*)iconlabel());
361 if (label()) XFree((char*)label());
364 ////////////////////////////////////////////////////////////////
366 void Frame::getLabel(int del) {
367 char* old = (char*)label();
368 char* nu = del ? 0 : (char*)getProperty(XA_WM_NAME);
369 if (nu) {
370 // since many window managers print a default label when none is
371 // given, many programs send spaces to make a blank label. Detect
372 // this and make it really be blank:
373 char* c = nu; while (*c == ' ') c++;
374 if (!*c) {XFree(nu); nu = 0;}
376 if (old) {
377 if (nu && !strcmp(old,nu)) {XFree(nu); return;}
378 XFree(old);
379 } else {
380 if (!nu) return;
382 Fl_Widget::label(nu);
383 if (nu) {
384 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE);
385 label_w = int(fl_width(nu))+6;
386 } else
387 label_w = 0;
388 if (shown() && label_h > 3 && left > 3)
389 XClearArea(fl_display, fl_xid(this), 1, label_y+3, left-1, label_h-3, 1);
392 ////////////////////////////////////////////////////////////////
394 int Frame::getGnomeState(int &) {
395 // values for _WIN_STATE property are from Gnome WM compliance docs:
396 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
397 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
398 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
399 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
400 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
401 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
402 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
403 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
404 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
405 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
406 // nyi
407 return 0;
410 ////////////////////////////////////////////////////////////////
412 // Read the sizeHints, and try to remove the vast number of mistakes
413 // that some applications seem to do writing them.
414 // Returns true if autoplace should be done.
416 int Frame::getSizes() {
418 XSizeHints sizeHints;
419 long junk;
420 if (!XGetWMNormalHints(fl_display, window_, &sizeHints, &junk))
421 sizeHints.flags = 0;
423 // get the increment, use 1 if none or illegal values:
424 if (sizeHints.flags & PResizeInc) {
425 inc_w = sizeHints.width_inc; if (inc_w < 1) inc_w = 1;
426 inc_h = sizeHints.height_inc; if (inc_h < 1) inc_h = 1;
427 } else {
428 inc_w = inc_h = 1;
431 // get the current size of the window:
432 int W = w()-dwidth;
433 int H = h()-dheight;
434 // I try a lot of places to get a good minimum size value. Lots of
435 // programs set illegal or junk values, so getting this correct is
436 // difficult:
437 min_w = W;
438 min_h = H;
440 // guess a value for minimum size in case it is not set anywhere:
441 min_w = min(min_w, 4*BUTTON_H);
442 min_w = ((min_w+inc_w-1)/inc_w) * inc_w;
443 min_h = min(min_h, 4*BUTTON_H);
444 min_h = ((min_h+inc_h-1)/inc_h) * inc_h;
445 // some programs put the minimum size here:
446 if (sizeHints.flags & PBaseSize) {
447 junk = sizeHints.base_width; if (junk > 0) min_w = junk;
448 junk = sizeHints.base_height; if (junk > 0) min_h = junk;
450 // finally, try the actual place the minimum size should be:
451 if (sizeHints.flags & PMinSize) {
452 junk = sizeHints.min_width; if (junk > 0) min_w = junk;
453 junk = sizeHints.min_height; if (junk > 0) min_h = junk;
456 max_w = max_h = 0; // default maximum size is "infinity"
457 if (sizeHints.flags & PMaxSize) {
458 // Though not defined by ICCCM standard, I interpret any maximum
459 // size that is less than the minimum to mean "infinity". This
460 // allows the maximum to be set in one direction only:
461 junk = sizeHints.max_width;
462 if (junk >= min_w && junk <= W) max_w = junk;
463 junk = sizeHints.max_height;
464 if (junk >= min_h && junk <= H) max_h = junk;
467 // set the maximize buttons according to current size:
468 max_w_button.value(H == maximize_height() && W == maximize_width());
470 // Currently only 1x1 aspect works:
471 if (sizeHints.flags & PAspect
472 && sizeHints.min_aspect.x == sizeHints.min_aspect.y)
473 set_flag(KEEP_ASPECT);
475 // another fix for gimp, which sets PPosition to 0,0:
476 if (x() <= 0 && y() <= 0) sizeHints.flags &= ~PPosition;
478 return !(sizeHints.flags & (USPosition|PPosition));
481 int max_w_switch;
482 // return width of contents when maximize button pressed:
483 int Frame::maximize_width() {
484 int W = max_w_switch; if (!W) W = Root->w();
485 return flag( NO_BORDER ) ? W : ((W-TITLE_WIDTH-min_w)/inc_w) * inc_w + min_w;
488 int max_h_switch;
489 int Frame::maximize_height() {
490 int H = max_h_switch; if (!H) H = Root->h()-20;
491 return flag( NO_BORDER ) ? H : ((H-min_h)/inc_h) * inc_h + min_h;
494 ////////////////////////////////////////////////////////////////
496 void Frame::getProtocols() {
497 int n; Atom* p = (Atom*)getProperty(wm_protocols, XA_ATOM, &n);
498 if (p) {
499 clear_flag(DELETE_WINDOW_PROTOCOL|TAKE_FOCUS_PROTOCOL|QUIT_PROTOCOL);
500 for (int i = 0; i < n; ++i) {
501 if (p[i] == wm_delete_window) {
502 set_flag(DELETE_WINDOW_PROTOCOL);
503 } else if (p[i] == wm_take_focus) {
504 set_flag(TAKE_FOCUS_PROTOCOL);
505 } else if (p[i] == wm_save_yourself) {
506 set_flag(SAVE_PROTOCOL);
507 } else if (p[i] == _wm_quit_app) {
508 set_flag(QUIT_PROTOCOL);
512 XFree((char*)p);
515 ////////////////////////////////////////////////////////////////
517 int Frame::getMotifHints() {
518 long* prop = (long*)getProperty(_motif_wm_hints, _motif_wm_hints);
519 if (!prop) return 0;
521 // see /usr/include/X11/Xm/MwmUtil.h for meaning of these bits...
522 // prop[0] = flags (what props are specified)
523 // prop[1] = functions (all, resize, move, minimize, maximize, close, quit)
524 // prop[2] = decorations (all, border, resize, title, menu, minimize,
525 // maximize)
526 // prop[3] = input_mode (modeless, primary application modal, system modal,
527 // full application modal)
528 // prop[4] = status (tear-off window)
530 // Fill in the default value for missing fields:
531 if (!(prop[0]&1)) prop[1] = 1;
532 if (!(prop[0]&2)) prop[2] = 1;
534 // The low bit means "turn the marked items off", invert this.
535 // Transient windows already have size & iconize buttons turned off:
536 if (prop[1]&1) prop[1] = ~prop[1] & (transient_for_xid ? ~0x58 : -1);
537 if (prop[2]&1) prop[2] = ~prop[2] & (transient_for_xid ? ~0x60 : -1);
539 int old_flags = flags();
541 // see if they are trying to turn off border:
542 if (!(prop[2])) set_flag(NO_BORDER); else clear_flag(NO_BORDER);
544 // see if they are trying to turn off title & close box:
545 if (!(prop[2]&0x18)) set_flag(THIN_BORDER); else clear_flag(THIN_BORDER);
547 // some Motif programs use this to disable resize :-(
548 // and some programs change this after the window is shown (*&%$#%)
549 if (!(prop[1]&2) || !(prop[2]&4))
550 set_flag(NO_RESIZE); else clear_flag(NO_RESIZE);
552 // and some use this to disable the Close function. The commented
553 // out test is it trying to turn off the mwm menu button: it appears
554 // programs that do that still expect Alt+F4 to close them, so I
555 // leave the close on then:
556 if (!(prop[1]&0x20) /*|| !(prop[2]&0x10)*/)
557 set_flag(NO_CLOSE); else clear_flag(NO_CLOSE);
559 // see if they set "input hint" to non-zero:
560 // prop[3] should be nonzero but the only example of this I have
561 // found is Netscape 3.0 and it sets it to zero...
562 if (!shown() && (prop[0]&4) /*&& prop[3]*/) set_flag(MODAL);
564 // see if it is forcing the iconize button back on. This makes
565 // transient_for act like group instead...
566 if ((prop[1]&0x8) || (prop[2]&0x20)) set_flag(ICONIZE);
568 // Silly 'ol Amazon paint ignores WM_DELETE_WINDOW and expects to
569 // get the SGI-specific "_WM_QUIT_APP". It indicates this by trying
570 // to turn off the close box. SIGH!!!
571 if (flag(QUIT_PROTOCOL) && !(prop[1]&0x20))
572 clear_flag(DELETE_WINDOW_PROTOCOL);
574 XFree((char*)prop);
575 return (flags() ^ old_flags);
578 ////////////////////////////////////////////////////////////////
580 void Frame::getColormaps(void) {
581 if (colormapWinCount) {
582 XFree((char *)colormapWindows);
583 delete[] window_Colormaps;
585 int n;
586 Window* cw = (Window*)getProperty(wm_colormap_windows, XA_WINDOW, &n);
587 if (cw) {
588 colormapWinCount = n;
589 colormapWindows = cw;
590 window_Colormaps = new Colormap[n];
591 for (int i = 0; i < n; ++i) {
592 if (cw[i] == window_) {
593 window_Colormaps[i] = colormap;
594 } else {
595 XWindowAttributes attr;
596 XSelectInput(fl_display, cw[i], ColormapChangeMask);
597 XGetWindowAttributes(fl_display, cw[i], &attr);
598 window_Colormaps[i] = attr.colormap;
601 } else {
602 colormapWinCount = 0;
606 void Frame::installColormap() const {
607 for (int i = colormapWinCount; i--;)
608 if (colormapWindows[i] != window_ && window_Colormaps[i])
609 XInstallColormap(fl_display, window_Colormaps[i]);
610 if (colormap)
611 XInstallColormap(fl_display, colormap);
614 ////////////////////////////////////////////////////////////////
616 // figure out transient_for(), based on the windows that exist, the
617 // transient_for and group attributes, etc:
618 void Frame::fix_transient_for() {
619 Frame* p = 0;
620 if (transient_for_xid && !flag(ICONIZE)) {
621 for (Frame* f = first; f; f = f->next) {
622 if (f != this && f->window_ == transient_for_xid) {p = f; break;}
624 // loops are illegal:
625 for (Frame* q = p; q; q = q->transient_for_) if (q == this) {p = 0; break;}
627 transient_for_ = p;
630 int Frame::is_transient_for(const Frame* f) const {
631 if (f)
632 for (Frame* p = transient_for(); p; p = p->transient_for())
633 if (p == f) return 1;
634 return 0;
637 // When a program maps or raises a window, this is called. It guesses
638 // if this window is in fact a modal window for the currently active
639 // window and if so transfers the active state to this:
640 // This also activates new main windows automatically
641 int Frame::activate_if_transient() {
642 if (!Fl::pushed())
643 if (!transient_for() || is_transient_for(active_)) return activate(1);
644 return 0;
647 ////////////////////////////////////////////////////////////////
649 int Frame::activate(int warp) {
650 // see if a modal & newer window is up:
651 for (Frame* c = first; c && c != this; c = c->next)
652 if (c->flag(MODAL) && c->transient_for() == this)
653 if (c->activate(warp)) return 1;
654 // ignore invisible windows:
655 if (state() != NORMAL || w() <= dwidth) return 0;
656 // always put in the colormap:
657 installColormap();
658 // move the pointer if desired:
659 // (note that moving the pointer is pretty much required for point-to-type
660 // unless you know the pointer is already in the window):
661 if (!warp || Fl::event_state() & (FL_BUTTON1|FL_BUTTON2|FL_BUTTON3)) {
663 } else if (warp==2) {
664 // warp to point at title:
665 //XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, min(label_y+label_w/2+1,h()/2));
666 } else {
667 warp_pointer();
669 // skip windows that don't want focus:
670 if (flag(NO_FOCUS)) return 0;
671 // set this even if we think it already has it, this seems to fix
672 // bugs with Motif popups:
673 XSetInputFocus(fl_display, window_, RevertToPointerRoot, fl_event_time);
674 if (active_ != this) {
675 if (active_) active_->deactivate();
676 active_ = this;
677 #if defined(ACTIVE_COLOR)
678 XSetWindowAttributes a;
679 a.background_pixel = fl_xpixel(FL_SELECTION_COLOR);
680 XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a);
681 labelcolor(contrast(FL_BLACK, FL_SELECTION_COLOR));
682 XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1);
683 #else
684 #if defined(SHOW_CLOCK)
685 redraw();
686 #endif
687 #endif
688 if (flag(TAKE_FOCUS_PROTOCOL))
689 sendMessage(wm_protocols, wm_take_focus);
691 return 1;
694 // this private function should only be called by constructor and if
695 // the window is active():
696 void Frame::deactivate() {
697 #if defined(ACTIVE_COLOR)
698 XSetWindowAttributes a;
699 a.background_pixel = fl_xpixel(FL_GRAY);
700 XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a);
701 labelcolor(FL_BLACK);
702 XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1);
703 #else
704 #if defined(SHOW_CLOCK)
705 redraw();
706 #endif
707 #endif
710 #if CLICK_RAISES || CLICK_TO_TYPE
711 // After the XGrabButton, the main loop will get the mouse clicks, and
712 // it will call here when it gets them:
713 void click_raise(Frame* f) {
714 f->activate();
715 #if CLICK_RAISES
716 if (fl_xevent->xbutton.button <= 1) f->raise();
717 #endif
718 XAllowEvents(fl_display, ReplayPointer, CurrentTime);
720 #endif
722 // get rid of the focus by giving it to somebody, if possible:
723 void Frame::throw_focus(int destructor) {
724 if (!active()) return;
725 if (!destructor) deactivate();
726 active_ = 0;
727 if (revert_to && revert_to->activate()) return;
728 for (Frame* f = first; f; f = f->next)
729 if (f != this && f->activate()) return;
732 ////////////////////////////////////////////////////////////////
734 // change the state of the window (this is a private function and
735 // it ignores the transient-for or desktop information):
737 void Frame::state(short newstate) {
738 short oldstate = state();
739 if (newstate == oldstate) return;
740 state_ = newstate;
741 switch (newstate) {
742 case UNMAPPED:
743 throw_focus();
744 set_state_flag(IGNORE_UNMAP);
745 XUnmapWindow(fl_display, fl_xid(this));
746 XUnmapWindow(fl_display, window_);
747 XRemoveFromSaveSet(fl_display, window_);
748 break;
749 case NORMAL:
750 if (oldstate == UNMAPPED) XAddToSaveSet(fl_display, window_);
751 if (w() > dwidth) XMapWindow(fl_display, window_);
752 XMapWindow(fl_display, fl_xid(this));
753 clear_state_flag(IGNORE_UNMAP);
754 break;
755 default:
756 if (oldstate == UNMAPPED) {
757 XAddToSaveSet(fl_display, window_);
758 } else if (oldstate == NORMAL) {
759 throw_focus();
760 set_state_flag(IGNORE_UNMAP);
761 XUnmapWindow(fl_display, fl_xid(this));
762 XUnmapWindow(fl_display, window_);
763 } else {
764 return; // don't setStateProperty IconicState multiple times
766 break;
768 setStateProperty();
771 void Frame::setStateProperty() const {
772 long data[2];
773 switch (state()) {
774 case UNMAPPED :
775 data[0] = WithdrawnState; break;
776 case NORMAL :
777 case OTHER_DESKTOP :
778 data[0] = NormalState; break;
779 default :
780 data[0] = IconicState; break;
782 data[1] = (long)None;
783 XChangeProperty(fl_display, window_, wm_state, wm_state,
784 32, PropModeReplace, (unsigned char *)data, 2);
787 ////////////////////////////////////////////////////////////////
788 // Public state modifiers that move all transient_for(this) children
789 // with the frame and do the desktops right:
791 void Frame::raise() {
792 Frame* newtop = 0;
793 Frame* previous = 0;
794 int previous_state = state_;
795 Frame** p;
796 // Find all the transient-for windows and this one, and raise them,
797 // preserving stacking order:
798 for (p = &first; *p;) {
799 Frame* f = *p;
800 if (f == this || f->is_transient_for(this) && f->state() != UNMAPPED) {
801 *p = f->next; // remove it from list
802 if (previous) {
803 XWindowChanges w;
804 w.sibling = fl_xid(previous);
805 w.stack_mode = Below;
806 XConfigureWindow(fl_display, fl_xid(f), CWSibling|CWStackMode, &w);
807 previous->next = f;
808 } else {
809 XRaiseWindow(fl_display, fl_xid(f));
810 newtop = f;
812 #if DESKTOPS
813 if (f->desktop_ && f->desktop_ != Desktop::current())
814 f->state(OTHER_DESKTOP);
815 else
816 #endif
817 f->state(NORMAL);
818 previous = f;
819 } else {
820 p = &((*p)->next);
823 previous->next = first;
824 first = newtop;
825 #if DESKTOPS
826 if (!transient_for() && desktop_ && desktop_ != Desktop::current()) {
827 // for main windows we also must move to the current desktop
828 desktop(Desktop::current());
830 #endif
831 if (previous_state != NORMAL && newtop->state_==NORMAL)
832 newtop->activate_if_transient();
835 void Frame::lower() {
836 Frame* t = transient_for(); if (t) t->lower();
837 if (!next || next == t) return; // already on bottom
838 // pull it out of the list:
839 Frame** p = &first;
840 for (; *p != this; p = &((*p)->next)) {}
841 *p = next;
842 // find end of list:
843 Frame* f = next; while (f->next != t) f = f->next;
844 // insert it after that:
845 f->next = this; next = t;
846 // and move the X window:
847 XWindowChanges w;
848 w.sibling = fl_xid(f);
849 w.stack_mode = Below;
850 XConfigureWindow(fl_display, fl_xid(this), CWSibling|CWStackMode, &w);
853 void Frame::iconize() {
854 for (Frame* c = first; c; c = c->next) {
855 if (c == this || c->is_transient_for(this) && c->state() != UNMAPPED)
856 c->state(ICONIC);
860 #if DESKTOPS
861 void Frame::desktop(Desktop* d) {
862 if (d == desktop_) return;
863 // Put all the relatives onto the desktop as well:
864 for (Frame* c = first; c; c = c->next) {
865 if (c == this || c->is_transient_for(this)) {
866 c->desktop_ = d;
867 c->setProperty(_win_state, XA_CARDINAL, !d);
868 c->setProperty(kwm_win_sticky, kwm_win_sticky, !d);
869 if (d) {
870 c->setProperty(kwm_win_desktop, kwm_win_desktop, d->number());
871 c->setProperty(_win_workspace, XA_CARDINAL, d->number()-1);
873 if (!d || d == Desktop::current()) {
874 if (c->state() == OTHER_DESKTOP) c->state(NORMAL);
875 } else {
876 if (c->state() == NORMAL) c->state(OTHER_DESKTOP);
881 #endif
883 ////////////////////////////////////////////////////////////////
885 // Resize and/or move the window. The size is given for the frame, not
886 // the contents. This also sets the buttons on/off as needed:
888 void Frame::set_size(int nx, int ny, int nw, int nh, int warp) {
889 if ( ( flag( NO_BORDER ) || flag( THIN_BORDER ) ) && ( max_w_button.value( ) || nh == maximize_height( ) ) ) {
890 nx = ny = 0;
891 nw = Root->w( );
892 nh = maximize_height( );
893 dwidth = 0;
894 app_border_width = 0;
896 if ( nh >= maximize_height( ) ) nh = maximize_height( );
897 int dx = nx-x(); x(nx);
898 int dy = ny-y(); y(ny);
899 if (!dx && !dy && nw == w() && nh == h()) return;
900 int unmap = 0;
901 int remap = 0;
902 // use XClearArea to cause correct damage events:
903 if (nw != w()) {
904 max_w_button.value(nw-dwidth == maximize_width());
905 min_w_button.value(nw <= dwidth);
906 if (nw <= dwidth) {
907 unmap = 1;
908 } else {
909 if (w() <= dwidth) remap = 1;
911 int minw = (nw < w()) ? nw : w();
912 XClearArea(fl_display, fl_xid(this), minw-RIGHT, 0, RIGHT, nh, 1);
913 w(nw);
915 if (nh != h()) {
916 int minh = (nh < h()) ? nh : h();
917 XClearArea(fl_display, fl_xid(this), 0, minh-BOTTOM, w(), BOTTOM, 1);
918 // see if label or close box moved, erase the minimum area:
919 int old_label_y = label_y;
920 int old_label_h = label_h;
921 h(nh); show_hide_buttons();
922 #ifdef SHOW_CLOCK
923 int t = label_y + 3; // we have to clear the entire label area
924 #else
925 int t = nh;
926 if (label_y != old_label_y) {
927 t = label_y; if (old_label_y < t) t = old_label_y;
928 } else if (label_y+label_h != old_label_y+old_label_h) {
929 t = label_y+label_h;
930 if (old_label_y+old_label_h < t) t = old_label_y+old_label_h;
932 #endif
933 if (t < nh && left>LEFT)
934 XClearArea(fl_display,fl_xid(this), 1, t, left-1, nh-t, 1);
936 // for maximize button move the cursor first if window gets smaller
937 //if (warp == 1 && (dx || dy))
938 //XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy);
939 // for configure request, move the cursor first
940 if (warp == 2 && active() && !Fl::pushed()) warp_pointer();
941 XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh);
942 if (nw <= dwidth) {
943 if (unmap) {
944 set_state_flag(IGNORE_UNMAP);
945 XUnmapWindow(fl_display, window_);
947 } else {
948 XResizeWindow(fl_display, window_, nw-dwidth, nh-dheight);
949 if (remap) {
950 XMapWindow(fl_display, window_);
951 #if CLICK_TO_TYPE
952 if (active()) activate();
953 #else
954 activate();
955 #endif
958 // for maximize button move the cursor second if window gets bigger:
959 //if (warp == 3 && (dx || dy))
960 //XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy);
961 //if (nw > dwidth)
962 sendConfigureNotify();
963 XSync(fl_display,0);
964 fprintf( stderr, "using %dx%d+%d+%d\n", w(), h(), x(), y() );
967 void Frame::sendConfigureNotify() const {
968 XConfigureEvent ce;
969 ce.type = ConfigureNotify;
970 ce.event = window_;
971 ce.window = window_;
972 ce.x = x()+left-app_border_width;
973 ce.y = y()+top-app_border_width;
974 ce.width = w()-dwidth;
975 ce.height = h()-dheight;
976 ce.border_width = app_border_width;
977 ce.above = None;
978 ce.override_redirect = 0;
979 XSendEvent(fl_display, window_, False, StructureNotifyMask, (XEvent*)&ce);
982 // move the pointer inside the window:
983 void Frame::warp_pointer() {
984 int X,Y; Fl::get_mouse(X,Y);
985 X -= x();
986 if (X <= 0) X = left/2+1;
987 if (X >= w()) X = w()-(RIGHT/2+1);
988 Y -= y();
989 if (Y < 0) Y = TOP/2+1;
990 if (Y >= h()) Y = h()-(BOTTOM/2+1);
991 //if (X != Xi || Y != Yi)
992 //XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, X, Y);
995 // Resize the frame to match the current border type:
996 void Frame::updateBorder() {
997 if ( flag( NO_BORDER ) )
998 return;
1000 int nx = x()+left;
1001 int ny = y()+top;
1002 int nw = w()-dwidth;
1003 int nh = h()-dheight;
1004 if (flag(NO_BORDER)) {
1005 left = top = dwidth = dheight = 0;
1006 } else {
1007 left = flag(THIN_BORDER) ? LEFT : LEFT+TITLE_WIDTH;
1008 dwidth = left+RIGHT;
1009 top = TOP;
1010 dheight = TOP+BOTTOM;
1012 nx -= left;
1013 ny -= top;
1014 nw += dwidth;
1015 nh += dheight;
1016 if (x()==nx && y()==ny && w()==nw && h()==nh) return;
1017 x(nx); y(ny); w(nw); h(nh);
1018 if (!shown()) return; // this is so constructor can call this
1019 // try to make the contents not move while the border changes around it:
1020 XSetWindowAttributes a;
1021 a.win_gravity = StaticGravity;
1022 XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a);
1023 XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh);
1024 a.win_gravity = NorthWestGravity;
1025 XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a);
1026 // fix the window position if the X server didn't do the gravity:
1027 XMoveWindow(fl_display, window_, left, top);
1030 // position and show the buttons according to current border, size,
1031 // and other state information:
1032 void Frame::show_hide_buttons() {
1033 if (flag(THIN_BORDER|NO_BORDER)) {
1034 iconize_button.hide();
1035 max_w_button.hide();
1036 min_w_button.hide();
1037 close_button.hide();
1038 return;
1040 int by = BUTTON_TOP;
1041 #if CLOSE_BOX
1042 if (flag(NO_CLOSE)) {
1043 #endif
1044 close_button.hide();
1045 #if CLOSE_BOX
1046 } else {
1047 close_button.show();
1048 close_button.position(BUTTON_LEFT,by);
1049 by += BUTTON_H;
1051 #endif
1052 if (transient_for()) {
1053 iconize_button.hide();
1054 min_w_button.hide();
1055 } else {
1056 iconize_button.hide();
1057 //iconize_button.position(BUTTON_LEFT,by);
1058 //iconize_button.show();
1059 //by += BUTTON_H;
1060 #if MINIMIZE_BOX
1061 min_w_button.position(BUTTON_LEFT,by);
1062 min_w_button.show();
1063 by += BUTTON_H;
1064 #else
1065 min_w_button.hide();
1066 #endif
1068 if (min_w == max_w || flag(KEEP_ASPECT|NO_RESIZE) ||
1069 !max_w_button.value() && by+label_w+2*BUTTON_H > h()-BUTTON_BOTTOM) {
1070 max_w_button.hide();
1071 } else {
1072 max_w_button.position(BUTTON_LEFT,by);
1073 max_w_button.show();
1074 by += BUTTON_H;
1076 if (label_y != by && shown())
1077 XClearArea(fl_display,fl_xid(this), 1, by, left-1, label_h+label_y-by, 1);
1078 label_y = by;
1079 label_h = h( ) - by;
1082 // make sure fltk does not try to set the window size:
1083 void Frame::resize(int, int, int, int) {}
1085 ////////////////////////////////////////////////////////////////
1087 void Frame::close() {
1088 if (flag(DELETE_WINDOW_PROTOCOL))
1089 sendMessage(wm_protocols, wm_delete_window);
1090 else if (flag(QUIT_PROTOCOL))
1091 sendMessage(wm_protocols, _wm_quit_app);
1092 else
1093 kill();
1096 void Frame::kill() {
1097 XKillClient(fl_display, window_);
1100 // this is called when window manager exits:
1101 void Frame::save_protocol() {
1102 Frame* f;
1103 for (f = first; f; f = f->next) if (f->flag(SAVE_PROTOCOL)) {
1104 f->set_state_flag(SAVE_PROTOCOL_WAIT);
1105 f->sendMessage(wm_protocols, wm_save_yourself);
1107 double t = 10.0; // number of seconds to wait before giving up
1108 while (t > 0.0) {
1109 for (f = first; ; f = f->next) {
1110 if (!f) return;
1111 if (f->flag(SAVE_PROTOCOL) && f->state_flags_&SAVE_PROTOCOL_WAIT) break;
1113 t = Fl::wait(t);
1117 ////////////////////////////////////////////////////////////////
1118 // Drawing code:
1120 void Frame::draw() {
1121 if (flag(NO_BORDER)) return;
1122 if (!flag(THIN_BORDER)) Fl_Window::draw();
1123 if (damage() != FL_DAMAGE_CHILD) {
1124 #if ACTIVE_COLOR
1125 fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h());
1126 if (active()) {
1127 fl_color(FL_GRAY_RAMP+('N'-'A'));
1128 fl_xyline(2, h()-3, w()-3, 2);
1130 #else
1131 fl_frame("AAAAWWJJTTNN",0,0,w(),h());
1132 #endif
1133 if (!flag(THIN_BORDER) && label_h > 3) {
1134 #ifdef SHOW_CLOCK
1135 if (active()) {
1136 int clkw = int(fl_width(clock_buf));
1137 if (clock_alarm_on) {
1138 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE);
1139 fl_rectf(LEFT-1, label_y + label_h - 3 - clkw, TITLE_WIDTH, clkw,
1140 (ALARM_BG_COLOR>>16)&0xff,
1141 (ALARM_BG_COLOR>>8)&0xff,
1142 ALARM_BG_COLOR&0xff);
1143 fl_color((ALARM_FG_COLOR>>16)&0xff,
1144 (ALARM_FG_COLOR>>8)&0xff,
1145 ALARM_FG_COLOR&0xff);
1146 } else
1147 fl_font(MENU_FONT_SLOT, TITLE_FONT_SIZE);
1148 // This might overlay the label if the label is long enough
1149 // and the window height is short enough. For now, we'll
1150 // assume this is not enough of a problem to be concerned
1151 // about.
1152 draw_rotated90(clock_buf, 1, label_y+3, left-1, label_h-6,
1153 Fl_Align(FL_ALIGN_BOTTOM|FL_ALIGN_CLIP));
1154 } else
1155 // Only show the clock on the active frame.
1156 XClearArea(fl_display, fl_xid(this), 1, label_y+3,
1157 left-1, label_h-3, 0);
1158 #endif
1159 fl_color(labelcolor());
1160 fl_font(TITLE_FONT_SLOT, TITLE_FONT_SIZE);
1161 draw_rotated90(label(), 1, label_y+3, left-1, label_h-3,
1162 Fl_Align(FL_ALIGN_TOP|FL_ALIGN_CLIP));
1167 #ifdef SHOW_CLOCK
1168 void Frame::redraw_clock() {
1169 double clkw = fl_width(clock_buf);
1170 XClearArea(fl_display, fl_xid(this),
1171 1, label_y+label_h-3-(int)clkw,
1172 left-1, (int)clkw, 1);
1174 #endif
1176 void FrameButton::draw() {
1177 Fl_Widget::draw_box(value() ? FL_DOWN_FRAME : FL_UP_FRAME, FL_GRAY);
1178 fl_color(parent()->labelcolor());
1179 switch (label()[0]) {
1180 case 'W':
1181 #if MINIMIZE_ARROW
1182 fl_line (x()+2,y()+(h())/2,x()+w()-4,y()+h()/2);
1183 fl_line (x()+2,y()+(h())/2,x()+2+4,y()+h()/2+4);
1184 fl_line (x()+2,y()+(h())/2,x()+2+4,y()+h()/2-4);
1185 #else
1186 fl_rect(x()+(h()-7)/2,y()+3,2,h()-6);
1187 #endif
1188 return;
1189 case 'w':
1190 fl_rect(x()+2,y()+(h()-7)/2,w()-4,7);
1191 return;
1192 case 'h':
1193 fl_rect(x()+(h()-7)/2,y()+2,7,h()-4);
1194 return;
1195 case 'X':
1196 #if CLOSE_X
1197 fl_line(x()+2,y()+3,x()+w()-5,y()+h()-4);
1198 fl_line(x()+3,y()+3,x()+w()-4,y()+h()-4);
1199 fl_line(x()+2,y()+h()-4,x()+w()-5,y()+3);
1200 fl_line(x()+3,y()+h()-4,x()+w()-4,y()+3);
1201 #endif
1202 #if CLOSE_HITTITE_LIGHTNING
1203 fl_arc(x()+3,y()+3,w()-6,h()-6,0,360);
1204 fl_line(x()+7,y()+3, x()+7,y()+11);
1205 #endif
1206 return;
1207 case 'i':
1208 #if ICONIZE_BOX
1209 fl_rect(x()+w()/2-1,y()+h()/2-1,3,3);
1210 #endif
1211 return;
1215 ////////////////////////////////////////////////////////////////
1216 // User interface code:
1218 // this is called when user clicks the buttons:
1219 void Frame::button_cb(Fl_Button* b) {
1220 switch (b->label()[0]) {
1221 case 'W': // minimize button
1222 if (b->value()) {
1223 if (!max_w_button.value()) {
1224 restore_x = x()+left;
1225 restore_y = y()+top;
1226 #if MINIMIZE_HEIGHT
1227 restore_w=w()-dwidth;
1228 restore_h = h()-dwidth;
1229 #endif
1231 #if MINIMIZE_HEIGHT
1232 set_size(x(), y(), dwidth-1,
1233 min(h(),min(350,label_w+3*BUTTON_H+BUTTON_TOP+BUTTON_BOTTOM)),
1235 #else
1236 set_size(x(), y(), dwidth-1, h(), 1);
1237 #endif
1238 } else {
1239 #if MINIMIZE_HEIGHT
1240 set_size(x(), y(), restore_w+dwidth, restore_h+dwidth, 1);
1241 #else
1242 set_size(x(), y(), restore_w+dwidth, h(), 1);
1243 #endif
1245 show_hide_buttons();
1246 break;
1247 case 'w': // max-width button
1248 if (b->value()) {
1249 if (!min_w_button.value()) {restore_x=x()+left; restore_w=w()-dwidth;}
1250 int W = maximize_width()+dwidth;
1251 int X = force_x_onscreen(x() + (w()-W)/2, W);
1252 set_size(X, y(), W, h(), 3);
1253 } else {
1254 set_size(restore_x-left, y(), restore_w+dwidth, h(), 1);
1256 show_hide_buttons();
1257 if (b->value()) {
1258 restore_y = y()+top;
1259 restore_h = h()-dwidth;
1260 int H = maximize_height()+dheight;
1261 int Y = force_y_onscreen(y() + (h()-H)/2, H);
1262 set_size(x(), Y, w(), H, 3);
1263 } else {
1264 set_size(x(), restore_y-top, w(), restore_h+dwidth, 1);
1266 break;
1267 case 'X':
1268 close();
1269 break;
1270 default: // iconize button
1271 iconize();
1272 break;
1276 // static callback for fltk:
1277 void Frame::button_cb_static(Fl_Widget* w, void*) {
1278 ((Frame*)(w->parent()))->button_cb((Fl_Button*)w);
1281 // This method figures out what way the mouse will resize the window.
1282 // It is used to set the cursor and to actually control what you grab.
1283 // If the window cannot be resized in some direction this should not
1284 // return that direction.
1285 int Frame::mouse_location() {
1286 int x = Fl::event_x();
1287 int y = Fl::event_y();
1288 int r = 0;
1289 if (flag(NO_RESIZE)) return 0;
1290 if (min_h != max_h) {
1291 if (y < RESIZE_EDGE) r |= FL_ALIGN_TOP;
1292 else if (y >= h()-RESIZE_EDGE) r |= FL_ALIGN_BOTTOM;
1294 if (min_w != max_w) {
1295 #if RESIZE_LEFT
1296 if (x < RESIZE_EDGE) r |= FL_ALIGN_LEFT;
1297 #else
1298 if (x < RESIZE_EDGE && r) r |= FL_ALIGN_LEFT;
1299 #endif
1300 else if (x >= w()-RESIZE_EDGE) r |= FL_ALIGN_RIGHT;
1302 return r;
1305 // set the cursor correctly for a return value from mouse_location():
1306 void Frame::set_cursor(int r) {
1307 Fl_Cursor c = r ? FL_CURSOR_ARROW : FL_CURSOR_MOVE;
1308 switch (r) {
1309 case FL_ALIGN_TOP:
1310 case FL_ALIGN_BOTTOM:
1311 c = FL_CURSOR_NS;
1312 break;
1313 case FL_ALIGN_LEFT:
1314 case FL_ALIGN_RIGHT:
1315 c = FL_CURSOR_WE;
1316 break;
1317 case FL_ALIGN_LEFT|FL_ALIGN_TOP:
1318 case FL_ALIGN_RIGHT|FL_ALIGN_BOTTOM:
1319 c = FL_CURSOR_NWSE;
1320 break;
1321 case FL_ALIGN_LEFT|FL_ALIGN_BOTTOM:
1322 case FL_ALIGN_RIGHT|FL_ALIGN_TOP:
1323 c = FL_CURSOR_NESW;
1324 break;
1326 static Frame* previous_frame;
1327 static Fl_Cursor previous_cursor;
1328 if (this != previous_frame || c != previous_cursor) {
1329 previous_frame = this;
1330 previous_cursor = c;
1331 cursor(c, CURSOR_FG_SLOT, CURSOR_BG_SLOT);
1335 #ifdef AUTO_RAISE
1336 // timeout callback to cause autoraise:
1337 void auto_raise(void*) {
1338 if (Frame::activeFrame() && !Fl::grab() && !Fl::pushed())
1339 Frame::activeFrame()->raise();
1341 #endif
1343 extern void ShowMenu();
1345 // If cursor is in the contents of a window this is set to that window.
1346 // This is only used to force the cursor to an arrow even though X keeps
1347 // sending mysterious erroneous move events:
1348 static Frame* cursor_inside = 0;
1350 // Handle an fltk event.
1351 int Frame::handle(int e) {
1352 static int what, dx, dy, ix, iy, iw, ih;
1353 // see if child widget handles event:
1354 if (Fl_Window::handle(e) && e != FL_ENTER && e != FL_MOVE) {
1355 if (e == FL_PUSH) set_cursor(-1);
1356 return 1;
1358 switch (e) {
1360 case FL_SHOW:
1361 case FL_HIDE:
1362 return 0; // prevent fltk from messing things up
1364 case FL_ENTER:
1365 #if !CLICK_TO_TYPE
1366 if (Fl::pushed() || Fl::grab()) return 1;
1367 if (activate()) {
1368 #ifdef AUTO_RAISE
1369 Fl::remove_timeout(auto_raise);
1370 Fl::add_timeout(AUTO_RAISE, auto_raise);
1371 #endif
1373 #endif
1374 goto GET_CROSSINGS;
1376 case FL_LEAVE:
1377 #if !CLICK_TO_TYPE && !STICKY_FOCUS
1378 if (active()) {
1379 deactivate();
1380 XSetInputFocus(fl_display, PointerRoot, RevertToPointerRoot,
1381 fl_event_time);
1382 active_ = 0;
1384 #endif
1385 goto GET_CROSSINGS;
1387 case 0:
1388 GET_CROSSINGS:
1389 // set cursor_inside to true when the mouse is inside a window
1390 // set it false when mouse is on a frame or outside a window.
1391 // fltk mangles the X enter/leave events, we need the original ones:
1393 switch (fl_xevent->type) {
1394 case EnterNotify:
1396 // see if cursor skipped over frame and directly to interior:
1397 if (fl_xevent->xcrossing.detail == NotifyVirtual ||
1398 fl_xevent->xcrossing.detail == NotifyNonlinearVirtual)
1399 cursor_inside = this;
1401 else {
1402 // cursor is now pointing at frame:
1403 cursor_inside = 0;
1406 // fall through to FL_MOVE:
1407 break;
1409 case LeaveNotify:
1410 if (fl_xevent->xcrossing.detail == NotifyInferior) {
1411 // cursor moved from frame to interior
1412 cursor_inside = this;
1413 set_cursor(-1);
1414 return 1;
1416 return 1;
1418 default:
1419 return 0; // other X event we don't understand
1422 case FL_MOVE:
1423 if (Fl::belowmouse() != this || cursor_inside == this)
1424 set_cursor(-1);
1425 else
1426 set_cursor(mouse_location());
1427 return 1;
1429 case FL_PUSH:
1430 if (Fl::event_button() > 2) {
1431 set_cursor(-1);
1432 ShowMenu();
1433 return 1;
1435 ix = x(); iy = y(); iw = w(); ih = h();
1436 if (!max_w_button.value() && !min_w_button.value()) {
1437 restore_x = ix+left; restore_w = iw-dwidth;
1439 #if MINIMIZE_HEIGHT
1440 if (!min_w_button.value())
1441 #endif
1442 if (!max_w_button.value()) {
1443 restore_y = iy+top; restore_h = ih-dwidth;
1445 what = mouse_location();
1446 if (Fl::event_button() > 1) what = 0; // middle button does drag
1447 dx = Fl::event_x_root()-ix;
1448 if (what & FL_ALIGN_RIGHT) dx -= iw;
1449 dy = Fl::event_y_root()-iy;
1450 if (what & FL_ALIGN_BOTTOM) dy -= ih;
1451 set_cursor(what);
1452 return 1;
1453 case FL_DRAG:
1454 if (Fl::event_is_click()) return 1; // don't drag yet
1455 case FL_RELEASE:
1456 if (Fl::event_is_click()) {
1457 if (Fl::grab()) return 1;
1458 #if CLICK_TO_TYPE
1459 if (activate()) {
1460 if (Fl::event_button() <= 1) raise();
1461 return 1;
1463 #endif
1464 if (Fl::event_button() > 1) lower(); else raise();
1465 } else if (!what) {
1466 int nx = Fl::event_x_root()-dx;
1467 int W = Root->x()+Root->w();
1468 if (nx+iw > W && nx+iw < W+SCREEN_SNAP) {
1469 int t = W+1-iw;
1470 if (iw >= Root->w() || x() > t || nx+iw >= W+EDGE_SNAP)
1471 t = W+(dwidth-left)-iw;
1472 if (t >= x() && t < nx) nx = t;
1474 int X = Root->x();
1475 if (nx < X && nx > X-SCREEN_SNAP) {
1476 int t = X-1;
1477 if (iw >= Root->w() || x() < t || nx <= X-EDGE_SNAP) t = X-BUTTON_LEFT;
1478 if (t <= x() && t > nx) nx = t;
1480 int ny = Fl::event_y_root()-dy;
1481 int H = Root->y()+Root->h()-20;
1482 if (ny+ih > H && ny+ih < H+SCREEN_SNAP) {
1483 int t = H+1-ih;
1484 if (ih >= (Root->h()-20) || y() > t || ny+ih >= H+EDGE_SNAP)
1485 t = H+(dheight-top)-ih;
1486 if (t >= y() && t < ny) ny = t;
1488 int Y = Root->y();
1489 if (ny < Y && ny > Y-SCREEN_SNAP) {
1490 int t = Y-1;
1491 if (ih >= H || y() < t || ny <= Y-EDGE_SNAP) t = Y-top;
1492 if (t <= y() && t > ny) ny = t;
1494 set_size(nx, ny, iw, ih);
1495 } else {
1496 int nx = ix;
1497 int ny = iy;
1498 int nw = iw;
1499 int nh = ih;
1500 if (what & FL_ALIGN_RIGHT)
1501 nw = Fl::event_x_root()-dx-nx;
1502 else if (what & FL_ALIGN_LEFT)
1503 nw = ix+iw-(Fl::event_x_root()-dx);
1504 else {nx = x(); nw = w();}
1505 if (what & FL_ALIGN_BOTTOM)
1506 nh = Fl::event_y_root()-dy-ny;
1507 else if (what & FL_ALIGN_TOP)
1508 nh = iy+ih-(Fl::event_y_root()-dy);
1509 else {ny = y(); nh = h();}
1510 if (flag(KEEP_ASPECT)) {
1511 if (nw-dwidth > nh-dwidth
1512 && (what&(FL_ALIGN_LEFT|FL_ALIGN_RIGHT))
1513 || !(what&(FL_ALIGN_TOP|FL_ALIGN_BOTTOM)))
1514 nh = nw-dwidth+dheight;
1515 else
1516 nw = nh-dheight+dwidth;
1518 int MINW = min_w+dwidth;
1519 if (nw <= dwidth && dwidth > TITLE_WIDTH) {
1520 nw = dwidth-1;
1521 #if MINIMIZE_HEIGHT
1522 restore_h = nh;
1523 #endif
1524 } else {
1525 if (inc_w > 1) nw = ((nw-MINW+inc_w/2)/inc_w)*inc_w+MINW;
1526 if (nw < MINW) nw = MINW;
1527 else if (max_w && nw > max_w+dwidth) nw = max_w+dwidth;
1529 int MINH = min_h+dheight;
1530 const int MINH_B = BUTTON_H+BUTTON_TOP+BUTTON_BOTTOM;
1531 if (MINH_B > MINH) MINH = MINH_B;
1532 if (inc_h > 1) nh = ((nh-MINH+inc_h/2)/inc_h)*inc_h+MINH;
1533 if (nh < MINH) nh = MINH;
1534 else if (max_h && nh > max_h+dheight) nh = max_h+dheight;
1535 if (what & FL_ALIGN_LEFT) nx = ix+iw-nw;
1536 if (what & FL_ALIGN_TOP) ny = iy+ih-nh;
1537 set_size(nx,ny,nw,nh);
1539 return 1;
1541 return 0;
1544 // Handle events that fltk did not recognize (mostly ones directed
1545 // at the desktop):
1547 int Frame::handle(const XEvent* ei) {
1549 switch (ei->type) {
1551 case ConfigureRequest: {
1552 const XConfigureRequestEvent* e = &(ei->xconfigurerequest);
1553 unsigned long mask = e->value_mask;
1554 if (mask & CWBorderWidth) app_border_width = e->border_width;
1555 // Try to detect if the application is really trying to move the
1556 // window, or is simply echoing it's postion, possibly with some
1557 // variation (such as echoing the parent window position), and
1558 // dont' move it in that case:
1559 int X = (mask & CWX && e->x != x()) ? e->x+app_border_width-left : x();
1560 int Y = (mask & CWY && e->y != y()) ? e->y+app_border_width-top : y();
1561 int W = (mask & CWWidth) ? e->width+dwidth : w();
1562 int H = (mask & CWHeight) ? e->height+dheight : h();
1563 // Generally we want to obey any application positioning of the
1564 // window, except when it appears the app is trying to position
1565 // the window "at the edge".
1566 if (!(mask & CWX) || (X >= -2*left && X < 0)) X = force_x_onscreen(X,W);
1567 if (!(mask & CWY) || (Y >= -2*top && Y < 0)) Y = force_y_onscreen(Y,H);
1568 // Fix Rick Sayre's program that resizes it's windows bigger than the
1569 // maximum size:
1570 if (W > max_w+dwidth) max_w = 0;
1571 if (H > max_h+dheight) max_h = 0;
1572 set_size(X, Y, W, H, 2);
1573 if (e->value_mask & CWStackMode && e->detail == Above && state()==NORMAL)
1574 raise();
1575 return 1;}
1577 case MapRequest: {
1578 //const XMapRequestEvent* e = &(ei->xmaprequest);
1579 raise();
1580 return 1;}
1582 case UnmapNotify: {
1583 const XUnmapEvent* e = &(ei->xunmap);
1584 if (e->from_configure);
1585 else if (state_flags_&IGNORE_UNMAP) clear_state_flag(IGNORE_UNMAP);
1586 else state(UNMAPPED);
1587 return 1;}
1589 case DestroyNotify: {
1590 //const XDestroyWindowEvent* e = &(ei->xdestroywindow);
1591 delete this;
1592 return 1;}
1594 case ReparentNotify: {
1595 const XReparentEvent* e = &(ei->xreparent);
1596 if (e->parent==fl_xid(this)) return 1; // echo
1597 if (e->parent==fl_xid(Root)) return 1; // app is trying to tear-off again?
1598 delete this; // guess they are trying to paste tear-off thing back?
1599 return 1;}
1601 case ClientMessage: {
1602 const XClientMessageEvent* e = &(ei->xclient);
1603 if (e->message_type == wm_change_state && e->format == 32) {
1604 if (e->data.l[0] == NormalState) raise();
1605 else if (e->data.l[0] == IconicState) iconize();
1607 else if ( e->message_type == net_wm_state && e->format == 32 )
1609 if ( e->data.l[0] && ( int )e->data.l[1] == ( int )wm_maximize_horz )
1611 max_w_button.value( 1 );
1612 set_size( 0, 0, Root->w( ), maximize_height( ), 1 );
1615 else
1616 // we may want to ignore _WIN_LAYER from xmms?
1617 Fl::warning("flwm: unexpected XClientMessageEvent, type 0x%lx, "
1618 "window 0x%lx\n", e->message_type, e->window);
1619 return 1;}
1621 case ColormapNotify: {
1622 const XColormapEvent* e = &(ei->xcolormap);
1623 if (e->c_new) { // this field is called "new" in the old C++-unaware Xlib
1624 colormap = e->colormap;
1625 if (active()) installColormap();
1627 return 1;}
1629 case PropertyNotify: {
1630 const XPropertyEvent* e = &(ei->xproperty);
1631 Atom a = e->atom;
1633 // case XA_WM_ICON_NAME: (do something similar to name)
1634 if (a == XA_WM_NAME) {
1635 getLabel(e->state == PropertyDelete);
1637 } else if (a == wm_state) {
1638 // it's not clear if I really need to look at this. Need to make
1639 // sure it is not seeing the state echoed by the application by
1640 // checking for it being different...
1641 switch (getIntProperty(wm_state, wm_state, state())) {
1642 case IconicState:
1643 if (state() == NORMAL || state() == OTHER_DESKTOP) iconize(); break;
1644 case NormalState:
1645 if (state() != NORMAL && state() != OTHER_DESKTOP) raise(); break;
1648 } else if (a == wm_colormap_windows) {
1649 getColormaps();
1650 if (active()) installColormap();
1652 } else if (a == _motif_wm_hints) {
1653 // some #%&%$# SGI Motif programs change this after mapping the window!
1654 // :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( :-( :=(
1655 if (getMotifHints()) { // returns true if any flags changed
1656 fix_transient_for();
1657 updateBorder();
1658 show_hide_buttons();
1661 } else if (a == wm_protocols) {
1662 getProtocols();
1663 // get Motif hints since they may do something with QUIT:
1664 getMotifHints();
1666 } else if (a == XA_WM_NORMAL_HINTS || a == XA_WM_SIZE_HINTS) {
1667 getSizes();
1668 show_hide_buttons();
1670 } else if (a == XA_WM_TRANSIENT_FOR) {
1671 XGetTransientForHint(fl_display, window_, &transient_for_xid);
1672 fix_transient_for();
1673 show_hide_buttons();
1675 } else if (a == XA_WM_COMMAND) {
1676 clear_state_flag(SAVE_PROTOCOL_WAIT);
1679 return 1;}
1682 return 0;
1685 ////////////////////////////////////////////////////////////////
1686 // X utility routines:
1688 void* Frame::getProperty(Atom a, Atom type, int* np) const {
1689 return ::getProperty(window_, a, type, np);
1692 void* getProperty(Window w, Atom a, Atom type, int* np) {
1693 Atom realType;
1694 int format;
1695 unsigned long n, extra;
1696 int status;
1697 void* prop;
1698 status = XGetWindowProperty(fl_display, w,
1699 a, 0L, 256L, False, type, &realType,
1700 &format, &n, &extra, (uchar**)&prop);
1701 if (status != Success) return 0;
1702 if (!prop) return 0;
1703 if (!n) {XFree(prop); return 0;}
1704 if (np) *np = (int)n;
1705 return prop;
1708 int Frame::getIntProperty(Atom a, Atom type, int deflt) const {
1709 return ::getIntProperty(window_, a, type, deflt);
1712 int getIntProperty(Window w, Atom a, Atom type, int deflt) {
1713 void* prop = getProperty(w, a, type);
1714 if (!prop) return deflt;
1715 int r = int(*(long*)prop);
1716 XFree(prop);
1717 return r;
1720 void setProperty(Window w, Atom a, Atom type, int v) {
1721 long prop = v;
1722 XChangeProperty(fl_display, w, a, type, 32, PropModeReplace, (uchar*)&prop,1);
1725 void Frame::setProperty(Atom a, Atom type, int v) const {
1726 ::setProperty(window_, a, type, v);
1729 void Frame::sendMessage(Atom a, Atom l) const {
1730 XEvent ev;
1731 long mask;
1732 memset(&ev, 0, sizeof(ev));
1733 ev.xclient.type = ClientMessage;
1734 ev.xclient.window = window_;
1735 ev.xclient.message_type = a;
1736 ev.xclient.format = 32;
1737 ev.xclient.data.l[0] = long(l);
1738 ev.xclient.data.l[1] = long(fl_event_time);
1739 mask = 0L;
1740 XSendEvent(fl_display, window_, False, mask, &ev);