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