mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
65018f75b7
emenueditor
1701 lines
42 KiB
C++
1701 lines
42 KiB
C++
/*
|
|
* $Id: Frame.cpp 1736 2006-08-19 00:38:53Z karijes $
|
|
*
|
|
* Edewm, window manager
|
|
* Part of Equinox Desktop Environment (EDE).
|
|
* Copyright (c) 2000-2006 EDE Authors.
|
|
*
|
|
* This program is licenced under terms of the
|
|
* GNU General Public Licence version 2 or newer.
|
|
* See COPYING for details.
|
|
*/
|
|
|
|
#include "Frame.h"
|
|
#include "Events.h"
|
|
#include "Hints.h"
|
|
#include "Windowmanager.h"
|
|
#include "Atoms.h"
|
|
#include "Utils.h"
|
|
#include "Titlebar.h"
|
|
#include "debug.h"
|
|
#include "Sound.h"
|
|
#include "Tracers.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h> // snprintf
|
|
|
|
#ifdef SHAPE
|
|
#include <X11/extensions/shape.h>
|
|
#endif // SHAPE
|
|
|
|
|
|
#define FRAME_NAME(frame) (frame->label ? frame->label : "NULL")
|
|
|
|
// TODO: just for testing
|
|
#define BORDER_UPDOWN 3
|
|
#define BORDER_LEFTRIGHT 3
|
|
|
|
#define BORDER_THIN_UPDOWN 1
|
|
#define BORDER_THIN_LEFTRIGHT 1
|
|
|
|
#define SIZER_W 15
|
|
#define SIZER_H 15
|
|
|
|
#define MIN_W 0
|
|
#define MIN_H 0
|
|
|
|
#define TITLEBAR_H 20
|
|
|
|
#define EDGE_SNAP 10
|
|
|
|
|
|
void FrameBorders::item_color(Fl_Color c, FrameBordersState s, bool is_border)
|
|
{
|
|
switch(s)
|
|
{
|
|
case FOCUSED:
|
|
(is_border) ? (focused = c) : (sizers_focused = c);
|
|
break;
|
|
case UNFOCUSED:
|
|
(is_border) ? (unfocused = c) : (sizers_unfocused = c);
|
|
break;
|
|
case CLICKED:
|
|
(is_border) ? (clicked = c) : (sizers_clicked = c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Fl_Color FrameBorders::item_color(FrameBordersState s, bool is_border)
|
|
{
|
|
switch(s)
|
|
{
|
|
case FOCUSED:
|
|
if(is_border)
|
|
return focused;
|
|
else
|
|
return sizers_focused;
|
|
case UNFOCUSED:
|
|
if(is_border)
|
|
return unfocused;
|
|
else
|
|
return sizers_unfocused;
|
|
case CLICKED:
|
|
if(is_border)
|
|
return clicked;
|
|
else
|
|
return sizers_clicked;
|
|
}
|
|
|
|
return FL_GRAY;
|
|
}
|
|
|
|
void FrameBorders::updown(int s)
|
|
{
|
|
updown_w = s;
|
|
updown2x_w = s * 2;
|
|
}
|
|
|
|
void FrameBorders::leftright(int s)
|
|
{
|
|
leftright_h = s;
|
|
leftright2x_h = s * 2;
|
|
}
|
|
|
|
void FrameBorders::shaped(bool s)
|
|
{
|
|
is_shaped = s;
|
|
}
|
|
|
|
CoordinatesView::CoordinatesView() : Fl_Window(120, 20)
|
|
{
|
|
color(FL_WHITE);
|
|
box(FL_BORDER_BOX);
|
|
data_box = new Fl_Box(0, 0, w(), h());
|
|
data_box->label_size(11);
|
|
end();
|
|
}
|
|
|
|
CoordinatesView::~CoordinatesView()
|
|
{
|
|
}
|
|
|
|
void CoordinatesView::display_data(int x, int y, int w, int h)
|
|
{
|
|
snprintf(data, sizeof(data)-1, "%i x %i x %i x %i", x, y, w, h);
|
|
data_box->label(data);
|
|
data_box->redraw_label();
|
|
redraw();
|
|
}
|
|
|
|
Frame::Frame(Window win, XWindowAttributes* attrs) :
|
|
Fl_Window(0, 0),
|
|
fdata(new FrameData),
|
|
screen_x(0),
|
|
screen_y(0),
|
|
screen_w(0),
|
|
screen_h(0),
|
|
restore_x(0),
|
|
restore_y(0),
|
|
restore_w(0),
|
|
restore_h(0),
|
|
opaque_move_resize(false),
|
|
show_titlebar(true),
|
|
is_moving(false),
|
|
is_resizing(false),
|
|
cursor_grabbed(false),
|
|
snap_move(false),
|
|
show_coordinates(true)
|
|
{
|
|
// register our events
|
|
events = new FrameEventHandler(this);
|
|
|
|
if(show_coordinates)
|
|
cview = new CoordinatesView();
|
|
|
|
fdata->window = win;
|
|
fdata->transient_win = None;
|
|
fdata->colormap = DefaultColormap(fl_display, fl_screen);
|
|
fdata->option = 0;
|
|
fdata->type = FrameTypeNormal;
|
|
fdata->state = 0;
|
|
fdata->win_gravity = 0;
|
|
fdata->autoplace = true;
|
|
fdata->icon_pixmap = 0;
|
|
fdata->icon_mask = 0;
|
|
fdata->label = 0;
|
|
fdata->label_alocated = false;
|
|
|
|
fdata->plain.x = fdata->plain.y = 0;
|
|
fdata->plain.w = fdata->plain.h = 0;
|
|
fdata->plain.offset_x = fdata->plain.offset_y = 0;
|
|
fdata->plain.inc_w = fdata->plain.inc_h = 0;
|
|
fdata->plain.max_w = fdata->plain.max_h = 0;
|
|
fdata->plain.min_w = fdata->plain.min_h = 0;
|
|
|
|
overlay.x = overlay.y = overlay.w = overlay.h = 0;
|
|
|
|
borders.border_color(fl_darker(FL_GRAY), FOCUSED);
|
|
borders.border_color(FL_WHITE, UNFOCUSED);
|
|
borders.border_color(FL_BLUE, CLICKED);
|
|
|
|
borders.sizers_color(FL_GRAY, FOCUSED);
|
|
borders.sizers_color(FL_RED, UNFOCUSED);
|
|
borders.sizers_color(FL_RED, CLICKED);
|
|
// recalculated in setup_borders
|
|
borders.updown(0);
|
|
borders.leftright(0);
|
|
borders.shaped(false);
|
|
|
|
// we does not use window specific borders
|
|
fdata->plain.border = 0;
|
|
|
|
// collect data
|
|
WindowManager* wm = WindowManager::instance();
|
|
|
|
screen_x = wm->x();
|
|
screen_y = wm->y();
|
|
screen_w = wm->w();
|
|
screen_h = wm->h();
|
|
|
|
XWindowAttributes init_attrs;
|
|
if(attrs)
|
|
{
|
|
init_attrs = *attrs;
|
|
set_option(FrameOptIgnoreUnmap);
|
|
}
|
|
else
|
|
XGetWindowAttributes(fl_display, fdata->window, &init_attrs);
|
|
|
|
fdata->plain.x = init_attrs.x;
|
|
fdata->plain.y = init_attrs.y;
|
|
fdata->plain.w = init_attrs.width;
|
|
fdata->plain.h = init_attrs.height;
|
|
|
|
load_colormap(init_attrs.colormap);
|
|
|
|
if(init_attrs.map_state == IsViewable)
|
|
set_state(FrameStateNormal);
|
|
else if(init_attrs.map_state == IsUnmapped)
|
|
set_state(FrameStateIconized);
|
|
else if(init_attrs.map_state == IsUnviewable)
|
|
ELOG("Got IsUnviewable map_state, skiping for now...");
|
|
|
|
wm->hints()->icccm_size(fdata);
|
|
wm->hints()->icccm_wm_hints(fdata);
|
|
wm->hints()->netwm_window_type(fdata);
|
|
wm->hints()->netwm_window_state(fdata);
|
|
wm->hints()->mwm_load_hints(fdata);
|
|
|
|
load_label();
|
|
|
|
// do this asap so we don't miss any events...
|
|
XSelectInput(fl_display, fdata->window,
|
|
VisibilityChangeMask | ColormapChangeMask |
|
|
PropertyChangeMask | FocusChangeMask /*| StructureNotifyMask*/);
|
|
|
|
XGetTransientForHint(fl_display, fdata->window, &fdata->transient_win);
|
|
if(fdata->transient_win != None)
|
|
{
|
|
fdata->type = FrameTypeDialog;
|
|
ELOG("Got transient_win");
|
|
}
|
|
|
|
if(fdata->type == FrameTypeNormal || fdata->type == FrameTypeDialog)
|
|
{
|
|
set_option(FrameOptHaveTitlebar | FrameOptHaveBorder | FrameOptCloseable | FrameOptMoveable);
|
|
if(fdata->type == FrameTypeNormal)
|
|
set_option(FrameOptResizeable | FrameOptHideable);
|
|
|
|
borders.leftright(BORDER_LEFTRIGHT);
|
|
borders.updown(BORDER_UPDOWN);
|
|
}
|
|
|
|
init_overlay(borders.updown());
|
|
|
|
x(fdata->plain.x);
|
|
y(fdata->plain.y);
|
|
|
|
w(fdata->plain.w + borders.leftright2x());
|
|
h(fdata->plain.h + borders.updown2x());
|
|
XSetWindowBorderWidth(fl_display, fdata->window, fdata->plain.border);
|
|
|
|
XMoveResizeWindow(fl_display, fdata->window, fdata->plain.x, fdata->plain.y,
|
|
fdata->plain.w, fdata->plain.h);
|
|
|
|
ELOG("XWindowAttributes: %i %i %i %i", fdata->plain.x, fdata->plain.y, fdata->plain.w, fdata->plain.h);
|
|
begin();
|
|
Fl_Color szc = borders.sizers_color(FOCUSED);
|
|
sizer_top_left = new Fl_Box(1,1,SIZER_W,SIZER_H);
|
|
sizer_top_left->box(FL_FLAT_BOX);
|
|
sizer_top_left->color(szc);
|
|
|
|
sizer_top_right = new Fl_Box(1,1,SIZER_W,SIZER_H);
|
|
sizer_top_right->box(FL_FLAT_BOX);
|
|
sizer_top_right->color(szc);
|
|
|
|
sizer_bottom_left = new Fl_Box(1,1,SIZER_W,SIZER_H);
|
|
sizer_bottom_left->box(FL_FLAT_BOX);
|
|
sizer_bottom_left->color(szc);
|
|
|
|
sizer_bottom_right = new Fl_Box(1,1,SIZER_W,SIZER_H);
|
|
sizer_bottom_right->box(FL_FLAT_BOX);
|
|
sizer_bottom_right->color(szc);
|
|
|
|
|
|
if(show_titlebar)
|
|
{
|
|
titlebar = new Titlebar(this, borders.leftright(), borders.updown(),
|
|
w() - borders.leftright2x(), TITLEBAR_H, "Untitled");
|
|
|
|
if(fdata->label)
|
|
titlebar->label(fdata->label);
|
|
|
|
/* Offset for fdata->window in our frame.
|
|
* Used in reparenting.
|
|
*/
|
|
fdata->plain.offset_x = borders.leftright();
|
|
fdata->plain.offset_y = borders.updown() + titlebar->h();
|
|
}
|
|
else
|
|
{
|
|
fdata->plain.offset_x = borders.leftright();
|
|
fdata->plain.offset_y = borders.updown();
|
|
}
|
|
|
|
end();
|
|
|
|
/* Reposition and resize main frame
|
|
* but skip it if position is already
|
|
* set by some hints or fdata->autoplace is false.
|
|
*
|
|
* NOTE: efltk set minimal position 2x2
|
|
* so this is check. If window is initialy
|
|
* tried to be placed on 0x0 it will be
|
|
* misteriously hidden.
|
|
*/
|
|
int pos_x;
|
|
int pos_y;
|
|
if(fdata->autoplace && wm->query_best_position(&pos_x, &pos_y, w(), h()))
|
|
{
|
|
if(pos_x <= 2)
|
|
pos_x = x();
|
|
if(pos_y <= 2)
|
|
pos_y = y();
|
|
|
|
ELOG("This window does use autoplace");
|
|
}
|
|
else
|
|
{
|
|
// recorrect positions made up in setup_borders() ?@#@! including
|
|
// titlebar if present
|
|
if((x() - borders.leftright()) > screen_x)
|
|
pos_x = x() - borders.leftright();
|
|
else
|
|
pos_x = x();
|
|
|
|
if(show_titlebar && (y() - titlebar->h() - borders.updown()) > screen_y)
|
|
pos_y = y() - titlebar->h() - borders.updown();
|
|
else if((y() - borders.updown()) > screen_y)
|
|
pos_y = y() - borders.updown();
|
|
else
|
|
pos_y = y();
|
|
}
|
|
|
|
if(show_titlebar)
|
|
resize(pos_x, pos_y, w(), h() + titlebar->h());
|
|
else
|
|
resize(pos_x, pos_y, w(), h());
|
|
|
|
place_sizers(x(), y(), w(), h());
|
|
|
|
// only normal windows can be resized
|
|
if(fdata->type != FrameTypeNormal)
|
|
hide_sizers();
|
|
|
|
set_override();
|
|
// color of main window background, aka borders
|
|
color(borders.border_color(FOCUSED));
|
|
create();
|
|
reparent_window();
|
|
configure_notify();
|
|
|
|
// rest is in content_click()
|
|
XGrabButton(fl_display, AnyButton, AnyModifier, fdata->window,
|
|
False, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
|
|
|
|
// show our creation
|
|
XMapWindow(fl_display, fdata->window);
|
|
show();
|
|
|
|
if(borders.shaped())
|
|
shape_borders();
|
|
|
|
//shape_edges();
|
|
|
|
XAddToSaveSet(fl_display, fdata->window);
|
|
|
|
// TODO: this handling should be in WindowManager
|
|
wm->window_list.push_back(this);
|
|
|
|
if(fdata->transient_win != None)
|
|
{
|
|
ELOG("Added to aot_list");
|
|
// latest mapped windows will be _before_ others iff they are transient
|
|
wm->aot_list.push_front(this);
|
|
}
|
|
else
|
|
{
|
|
ELOG("Added to stack_list");
|
|
wm->stack_list.push_front(this);
|
|
}
|
|
|
|
if(fdata->option & FrameOptTakeFocus)
|
|
focus();
|
|
|
|
ELOG("Window loaded, frame: 0x%x plain: 0x%x", fl_xid(this), fdata->window);
|
|
XFlush(fl_display);
|
|
}
|
|
|
|
Frame::~Frame()
|
|
{
|
|
ELOG("Frame::~Frame");
|
|
if(fdata->label_alocated)
|
|
free(fdata->label);
|
|
|
|
XFreeGC(fl_display, overlay.inverted_gc);
|
|
|
|
if(show_coordinates)
|
|
delete cview;
|
|
|
|
delete events;
|
|
delete fdata;
|
|
}
|
|
#if 0
|
|
void Frame::feed_data(XWindowAttributes* existing)
|
|
{
|
|
WindowManager* wm = WindowManager::instance();
|
|
|
|
screen_x = wm->x();
|
|
screen_y = wm->y();
|
|
screen_w = wm->w();
|
|
screen_h = wm->h();
|
|
|
|
wm->hints()->icccm_size(fdata);
|
|
|
|
// TODO: For testing only. Better solution will be.
|
|
fdata->type = wm->hints()->netwm_window_type(fdata);
|
|
|
|
setup_borders();
|
|
|
|
if(fdata->type == FrameTypeSplash
|
|
|| fdata->type == FrameTypeMenu
|
|
|| fdata->type == FrameTypeDesktop
|
|
|| fdata->type == FrameTypeDock)
|
|
{
|
|
show_titlebar = false;
|
|
}
|
|
// ------------------------------------------------
|
|
|
|
wm->hints()->netwm_window_state(fdata);
|
|
wm->hints()->mwm_load_hints(fdata);
|
|
|
|
load_wm_hints();
|
|
load_colormap();
|
|
load_label();
|
|
|
|
XWindowAttributes attrs;
|
|
if(existing)
|
|
attrs = *existing;
|
|
else
|
|
XGetWindowAttributes(fl_display, fdata->window, &attrs);
|
|
|
|
// according to ICCCM standard, returned values must
|
|
// be used for window only, excluding decorations
|
|
fdata->plain.x = attrs.x;
|
|
fdata->plain.y = attrs.y;
|
|
// TODO: add checking if window out of screen
|
|
fdata->plain.w = attrs.width;
|
|
fdata->plain.h = attrs.height;
|
|
|
|
ELOG("XWindowAttributes: %i %i %i %i", fdata->plain.x, fdata->plain.y, fdata->plain.w, fdata->plain.h);
|
|
|
|
// Shorthand for: fdata->colormap = attrs->colormap;
|
|
// ...for easier tracking
|
|
if(fdata->colormap != attrs.colormap)
|
|
load_colormap(attrs.colormap);
|
|
|
|
switch(attrs.map_state)
|
|
{
|
|
case IsUnmapped:
|
|
set_state(FrameStateIconized);
|
|
break;
|
|
case IsViewable:
|
|
set_state(FrameStateNormal);
|
|
break;
|
|
case IsUnviewable:
|
|
ELOG("Got IsUnviewable map_state, skiping for now...");
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
/* load WM_HINTS property, finding if window
|
|
* have icons and in what state is in
|
|
* TODO: place this as Hint::icccm_load_wm_hints()
|
|
*/
|
|
#if 0
|
|
void Frame::load_wm_hints(void)
|
|
{
|
|
XWMHints* wm_hints = XAllocWMHints();
|
|
wm_hints = XGetWMHints(fl_display, fdata->window);
|
|
if(!wm_hints)
|
|
{
|
|
ELOG("XGetWMHints failed!");
|
|
return;
|
|
}
|
|
|
|
if((wm_hints->flags & IconPixmapHint) && wm_hints->icon_pixmap)
|
|
fdata->icon_pixmap = wm_hints->icon_pixmap;
|
|
if((wm_hints->flags & IconMaskHint) &&wm_hints->icon_mask)
|
|
fdata->icon_mask = wm_hints->icon_mask;
|
|
|
|
switch(wm_hints->initial_state)
|
|
{
|
|
case WithdrawnState:
|
|
XRemoveFromSaveSet(fl_display, fdata->window);
|
|
break;
|
|
case IconicState:
|
|
fdata->state = FrameStateIconized;
|
|
break;
|
|
case NormalState:
|
|
default:
|
|
fdata->state = FrameStateNormal;
|
|
break;
|
|
}
|
|
|
|
// check for focus
|
|
if((wm_hints->flags & InputHint) && !wm_hints->input)
|
|
fdata->option &= ~FrameOptTakeFocus; // window does not want focus
|
|
else
|
|
fdata->option |= FrameOptTakeFocus; // window want focus;
|
|
|
|
XFree(wm_hints);
|
|
}
|
|
#endif
|
|
// guess title
|
|
void Frame::load_label(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::load_label(void)");
|
|
WindowManager* wm = WindowManager::instance();
|
|
|
|
// first try NETWM style
|
|
fdata->label = wm->hints()->netwm_label(fdata->window, &fdata->label_alocated);
|
|
if(!fdata->label)
|
|
{
|
|
// then ICCCM
|
|
fdata->label = wm->hints()->icccm_label(fdata->window, &fdata->label_alocated);
|
|
}
|
|
ELOG("Window: %s (%p) loaded", (fdata->label ? fdata->label : "NULL"), fdata->window);
|
|
}
|
|
|
|
void Frame::destroy(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::destroy(void)");
|
|
|
|
ELOG("window goes down");
|
|
if(state(FrameStateDestroyed))
|
|
return;
|
|
|
|
if(!state(FrameStateUnmapped))
|
|
{
|
|
if(ValidateDrawable(fdata->window))
|
|
{
|
|
XRemoveFromSaveSet(fl_display, fdata->window);
|
|
XUnmapWindow(fl_display, fdata->window);
|
|
XUnmapWindow(fl_display, fl_xid(this));
|
|
}
|
|
}
|
|
|
|
set_state(FrameStateDestroyed);
|
|
WindowManager::instance()->update_client_list();
|
|
Fl::awake();
|
|
}
|
|
|
|
void Frame::map(void)
|
|
{
|
|
if(!state(FrameStateUnmapped))
|
|
return;
|
|
|
|
XAddToSaveSet(fl_display, fdata->window);
|
|
XMapWindow(fl_display, fdata->window);
|
|
XMapWindow(fl_display, fl_xid(this));
|
|
|
|
clear_state(FrameStateUnmapped);
|
|
}
|
|
|
|
void Frame::unmap(void)
|
|
{
|
|
if(state(FrameStateUnmapped))
|
|
return;
|
|
|
|
if(ValidateDrawable(fdata->window))
|
|
{
|
|
XRemoveFromSaveSet(fl_display, fdata->window);
|
|
XUnmapWindow(fl_display, fdata->window);
|
|
XUnmapWindow(fl_display, fl_xid(this));
|
|
}
|
|
|
|
set_state(FrameStateUnmapped);
|
|
}
|
|
|
|
// Install custom or default colormap.
|
|
// Default colormap is read only once, in Frame constructor.
|
|
void Frame::load_colormap(Colormap col)
|
|
{
|
|
TRACE_FUNCTION("void Frame::load_colormap(Colormap col)");
|
|
|
|
if(col != 0)
|
|
{
|
|
ELOG("Installing custom colormap");
|
|
fdata->colormap = col;
|
|
}
|
|
else
|
|
ELOG("Loading default colormap");
|
|
|
|
XInstallColormap(fl_display, fdata->colormap);
|
|
}
|
|
|
|
// Recalculate framed based on plain.
|
|
// If window is outside screen, fix what needs to be fixed
|
|
void Frame::setup_borders(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::setup_borders(void)");
|
|
|
|
int tx, ty;
|
|
switch(fdata->type)
|
|
{
|
|
case FrameTypeNormal:
|
|
/* border = BORDER_NORMAL; */
|
|
borders.leftright(BORDER_LEFTRIGHT);
|
|
borders.updown(BORDER_UPDOWN);
|
|
break;
|
|
case FrameTypeDialog:
|
|
/*border = BORDER_THIN; */
|
|
//borders.leftright(BORDER_THIN_LEFTRIGHT);
|
|
//borders.updown(BORDER_THIN_UPDOWN);
|
|
borders.leftright(BORDER_LEFTRIGHT);
|
|
borders.updown(BORDER_UPDOWN);
|
|
break;
|
|
case FrameTypeSplash:
|
|
case FrameTypeDesktop:
|
|
case FrameTypeMenu:
|
|
case FrameTypeDock:
|
|
borders.leftright(0);
|
|
borders.updown(0);
|
|
// they don't have visible borders
|
|
break;
|
|
}
|
|
|
|
// TODO: setting up initial window sizes
|
|
// should not be in setup_borders();
|
|
tx = fdata->plain.x - borders.leftright();
|
|
ty = fdata->plain.y - borders.updown();
|
|
if(tx < 0)
|
|
fdata->plain.x = borders.leftright();
|
|
if(ty < 0)
|
|
fdata->plain.y = borders.updown();
|
|
x(fdata->plain.x);
|
|
y(fdata->plain.y);
|
|
|
|
w(fdata->plain.w + borders.leftright2x());
|
|
h(fdata->plain.h + borders.updown2x());
|
|
|
|
// set fdata->window borders, althought fdata->plain.borders is
|
|
// always 0
|
|
XSetWindowBorderWidth(fl_display, fdata->window, fdata->plain.border);
|
|
}
|
|
#if 0
|
|
/* Setup window sizes to minimal usable (MIN_W, MIN_H)
|
|
* but only for normal windows. Transient should set
|
|
* their size internally.
|
|
*/
|
|
void Frame::init_sizes(void)
|
|
{
|
|
if(fdata->type == FrameTypeDialog)
|
|
return;
|
|
|
|
if(fdata->plain.w < MIN_W)
|
|
fdata->plain.w = MIN_W;
|
|
if(fdata->plain.h < MIN_H)
|
|
fdata->plain.h = MIN_H;
|
|
}
|
|
#endif
|
|
void Frame::init_overlay(int border_size)
|
|
{
|
|
XGCValues v;
|
|
v.subwindow_mode = IncludeInferiors;
|
|
v.foreground = 0xffffffff;
|
|
v.function = GXxor;
|
|
v.line_width = border_size;
|
|
v.graphics_exposures = False;
|
|
int mask = GCForeground | GCSubwindowMode | GCFunction | GCLineWidth | GCGraphicsExposures;
|
|
overlay.inverted_gc = XCreateGC(fl_display,
|
|
WindowManager::instance()->root_window(), mask, &v);
|
|
}
|
|
|
|
void Frame::reparent_window(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::reparent_window(void)");
|
|
|
|
if(!ValidateDrawable(fdata->window))
|
|
return;
|
|
|
|
const int XEventMask =
|
|
ExposureMask |
|
|
StructureNotifyMask|
|
|
VisibilityChangeMask |
|
|
KeyPressMask|
|
|
KeyReleaseMask|
|
|
KeymapStateMask|
|
|
FocusChangeMask |
|
|
ButtonPressMask |
|
|
ButtonReleaseMask |
|
|
EnterWindowMask |
|
|
LeaveWindowMask |
|
|
PointerMotionMask |
|
|
SubstructureRedirectMask |
|
|
SubstructureNotifyMask;
|
|
|
|
|
|
XSetWindowAttributes sattr;
|
|
// TODO: some windows set bit_gravity too
|
|
sattr.bit_gravity = NorthWestGravity;
|
|
sattr.event_mask = XEventMask;
|
|
sattr.colormap = fl_colormap;
|
|
sattr.border_pixel = fl_xpixel(FL_BLACK);
|
|
sattr.override_redirect = 0;
|
|
sattr.background_pixel = fl_xpixel(FL_GRAY);
|
|
XChangeWindowAttributes(fl_display, fl_xid(this),
|
|
CWBitGravity | CWBorderPixel | CWColormap | CWEventMask | CWBackPixel | CWOverrideRedirect, &sattr);
|
|
|
|
XReparentWindow(fl_display, fdata->window, fl_xid(this), fdata->plain.offset_x, fdata->plain.offset_y);
|
|
/*
|
|
if(!show_titlebar)
|
|
{
|
|
XReparentWindow(fl_display, fdata->window, fl_xid(this),
|
|
borders.leftright(), borders.updown());
|
|
}
|
|
else
|
|
{
|
|
XReparentWindow(fl_display, fdata->window, fl_xid(this),
|
|
borders.leftright(), borders.updown() + titlebar->h());
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
void Frame::recalc_geometry(int x_pos, int y_pos, int w_sz, int h_sz, short rtype)
|
|
{
|
|
TRACE_FUNCTION("void Frame::recalc_geometry(int x_pos, int y_pos, int w_sz, int h_sz, short rtype");
|
|
|
|
if(rtype == GeometryRecalcAll)
|
|
{
|
|
x(x_pos); y(y_pos);
|
|
w(w_sz); h(h_sz);
|
|
|
|
fdata->plain.x = x() + borders.leftright();
|
|
fdata->plain.y = y() + borders.updown();
|
|
fdata->plain.w = w() - borders.leftright2x();
|
|
}
|
|
else if(rtype == GeometryRecalcAllXY)
|
|
{
|
|
// do not include fdata->plain.w
|
|
// FIXME: duplication as above
|
|
x(x_pos); y(y_pos);
|
|
w(w_sz); h(h_sz);
|
|
fdata->plain.x = x() + borders.leftright();
|
|
fdata->plain.y = y() + borders.updown();
|
|
}
|
|
else if(rtype == GeometryRecalcPlain)
|
|
{
|
|
fdata->plain.x = x_pos;
|
|
fdata->plain.y = y_pos;
|
|
fdata->plain.w = w_sz;
|
|
fdata->plain.h = h_sz;
|
|
|
|
x(x_pos - borders.leftright());
|
|
y(y_pos - borders.updown());
|
|
w(w_sz + borders.leftright2x());
|
|
}
|
|
else
|
|
{
|
|
// we should no be here
|
|
assert(0);
|
|
}
|
|
|
|
|
|
if(show_titlebar)
|
|
{
|
|
// titlebar->resize(border, border, w() - border2x, titlebar->h());
|
|
if(rtype == GeometryRecalcAll)
|
|
{
|
|
fdata->plain.h = h() - (titlebar->h() + borders.updown2x());
|
|
}
|
|
else if(rtype == GeometryRecalcPlain)
|
|
{
|
|
h(h_sz + titlebar->h() + borders.updown2x());
|
|
}
|
|
|
|
titlebar->size(w() - borders.leftright2x(), titlebar->h());
|
|
}
|
|
else
|
|
{
|
|
if(rtype == GeometryRecalcAll)
|
|
{
|
|
fdata->plain.h = h() - borders.updown2x();
|
|
}
|
|
else if(rtype == GeometryRecalcPlain)
|
|
{
|
|
h(h_sz + borders.updown2x());
|
|
}
|
|
}
|
|
|
|
/* Sanitize sizes. If we go below min width and height
|
|
* even X will start to yell. Who likes yelling ?
|
|
*
|
|
* TODO: maybe when we detect this, should disallow any
|
|
* further moves/resizes ?
|
|
*/
|
|
if(fdata->plain.w <= fdata->plain.min_w)
|
|
{
|
|
int offset = fdata->plain.min_w - fdata->plain.w;
|
|
fdata->plain.w = fdata->plain.min_w;
|
|
w(w() + offset);
|
|
}
|
|
|
|
if(fdata->plain.h <= fdata->plain.min_h)
|
|
{
|
|
int offset = fdata->plain.min_h - fdata->plain.h;
|
|
fdata->plain.h = fdata->plain.min_h;
|
|
h(h() + offset);
|
|
}
|
|
|
|
|
|
ELOG("p: %i %i %i %i w: %i %i %i %i",
|
|
fdata->plain.x, fdata->plain.y, fdata->plain.w, fdata->plain.h,
|
|
x(), y(), w(), h());
|
|
}
|
|
|
|
/* Set size of window, accepting coordinates for fdata->window + frame,
|
|
* int which case it will be resized both.
|
|
*/
|
|
void Frame::set_size(int x_pos, int y_pos, int w_sz, int h_sz, bool apply_on_plain)
|
|
{
|
|
TRACE_FUNCTION("void Frame::set_size(int x_pos, int y_pos, int w_sz, int h_sz, bool apply_on_plain)");
|
|
|
|
if(!ValidateDrawable(fdata->window))
|
|
return;
|
|
|
|
short how;
|
|
if(apply_on_plain)
|
|
how = GeometryRecalcPlain;
|
|
else
|
|
how = GeometryRecalcAll;
|
|
|
|
recalc_geometry(x_pos, y_pos, w_sz, h_sz, how);
|
|
|
|
XGrabServer(fl_display);
|
|
|
|
place_sizers(x(), y(), w(), h());
|
|
XMoveWindow(fl_display, fl_xid(this), x(), y());
|
|
XResizeWindow(fl_display, fl_xid(this), w(), h());
|
|
XResizeWindow(fl_display, fdata->window, fdata->plain.w, fdata->plain.h);
|
|
|
|
if(borders.shaped())
|
|
shape_borders();
|
|
|
|
XUngrabServer(fl_display);
|
|
|
|
configure_notify();
|
|
redraw();
|
|
}
|
|
|
|
void Frame::move_window(int x_pos, int y_pos)
|
|
{
|
|
TRACE_FUNCTION("void Frame::move_window(int x_pos, int y_pos)");
|
|
|
|
//draw_overlay(x_pos, y_pos, w(), h());
|
|
//return;
|
|
|
|
// very dummy snapping with screen edges
|
|
// TODO: what about other window(s) edges ?
|
|
if(snap_move)
|
|
{
|
|
int snap_x = screen_x + EDGE_SNAP;
|
|
int snap_y = snap_x;
|
|
int snap_w = screen_w - EDGE_SNAP;
|
|
int snap_h = screen_h - EDGE_SNAP;
|
|
int w_pos = x_pos + w();
|
|
int h_pos = y_pos + h();
|
|
|
|
if(snap_x > x_pos - EDGE_SNAP && snap_x < x_pos + EDGE_SNAP)
|
|
x_pos = screen_x;
|
|
if(snap_y > y_pos - EDGE_SNAP && snap_y < y_pos + EDGE_SNAP)
|
|
y_pos = screen_y;
|
|
|
|
if(snap_w > w_pos - EDGE_SNAP && snap_w < w_pos + EDGE_SNAP)
|
|
x_pos = screen_w - w();
|
|
if(snap_h > h_pos - EDGE_SNAP && snap_h < h_pos + EDGE_SNAP)
|
|
y_pos = screen_h - h();
|
|
}
|
|
|
|
recalc_geometry(x_pos, y_pos, w(), h(), GeometryRecalcAllXY);
|
|
XMoveWindow(fl_display, fl_xid(this), x(), y());
|
|
configure_notify();
|
|
}
|
|
|
|
void Frame::draw_overlay(int x, int y, int w, int h)
|
|
{
|
|
TRACE_FUNCTION("void Frame::draw_overlay(int x, int y, int w, int h)");
|
|
/*
|
|
XGCValues v;
|
|
v.subwindow_mode = IncludeInferiors;
|
|
v.foreground = 0xffffffff;
|
|
v.function = GXxor;
|
|
v.line_width = borders.updown();
|
|
v.graphics_exposures = False;
|
|
int mask = GCForeground|GCSubwindowMode|GCFunction|GCLineWidth|GCGraphicsExposures;
|
|
GC invertGc = XCreateGC(fl_display, RootWindow(fl_display, fl_screen), mask, &v);
|
|
*/
|
|
|
|
if (w < 0) {x += w; w = -w;}
|
|
else if (!w) w = 1;
|
|
if (h < 0) {y += h; h = -h;}
|
|
else if (!h) h = 1;
|
|
Window root = WindowManager::instance()->root_window();
|
|
|
|
if(overlay.w > 0)
|
|
{
|
|
if(x == overlay.x && y == overlay.y && w == overlay.w && h == overlay.h)
|
|
return;
|
|
XDrawRectangle(fl_display, root, overlay.inverted_gc, overlay.x, overlay.y, overlay.w, overlay.h);
|
|
}
|
|
|
|
overlay.x = x;
|
|
overlay.y = y;
|
|
overlay.w = w;
|
|
overlay.h = h;
|
|
|
|
XDrawRectangle(fl_display, root, overlay.inverted_gc, overlay.x, overlay.y, overlay.w, overlay.h);
|
|
}
|
|
|
|
void Frame::clear_overlay(void)
|
|
{
|
|
if(overlay.w > 0)
|
|
{
|
|
Window root = WindowManager::instance()->root_window();
|
|
XDrawRectangle(fl_display, root, overlay.inverted_gc, overlay.x, overlay.y, overlay.w, overlay.h);
|
|
overlay.w = 0;
|
|
}
|
|
}
|
|
|
|
void Frame::place_sizers(int x, int y, int w, int h)
|
|
{
|
|
TRACE_FUNCTION("void Frame::place_sizers(int x, int y, int w, int h)");
|
|
|
|
// using position() instead resize() will be only
|
|
// valid if sizers are initialy set to SIZER_W and SIZER_H
|
|
|
|
sizer_top_left->position(0, 0);
|
|
sizer_top_right->position(w - SIZER_W, 0);
|
|
sizer_bottom_left->position(0, h - SIZER_H);
|
|
sizer_bottom_right->position(w - SIZER_W, h - SIZER_H);
|
|
}
|
|
|
|
/* Determine type of resizing based on position
|
|
* (NOTE: here is used relative position - Fl::event_x() and Fl::event_y(),
|
|
* which are valid only in frame window).
|
|
*
|
|
* TODO: any way this can be simplified ?
|
|
*/
|
|
long Frame::resize_type(int x, int y)
|
|
{
|
|
// TRACE_FUNCTION("long Frame::resize_type(int x, int y)");
|
|
|
|
//ELOG("%i %i", x, y);
|
|
// we are in top left sizer
|
|
if((x >= sizer_top_left->x() && x <= sizer_top_left->w()) &&
|
|
(y >= sizer_top_left->y() && y <= sizer_top_left->h()))
|
|
return ResizeTypeUpLeft;
|
|
|
|
// we are in top right sizer
|
|
if((x >= sizer_top_right->x() && x <= sizer_top_right->x() + sizer_top_right->w()) &&
|
|
(y >= sizer_top_right->y() && y <= sizer_top_right->y() + sizer_top_right->h()))
|
|
return ResizeTypeUpRight;
|
|
|
|
// we are in bottom left sizer
|
|
if((x >= sizer_bottom_left->x() && x <= sizer_bottom_left->x() + sizer_bottom_left->w()) &&
|
|
(y >= sizer_bottom_left->y() && y <= sizer_bottom_left->y() + sizer_bottom_left->h()))
|
|
return ResizeTypeDownLeft;
|
|
|
|
// we are in bottom right sizer
|
|
if((x >= sizer_bottom_right->x() && x <= sizer_bottom_right->x() + sizer_bottom_right->w()) &&
|
|
y >= sizer_bottom_right->y() && y <= sizer_bottom_right->y() + sizer_bottom_right->h())
|
|
return ResizeTypeDownRight;
|
|
|
|
// we are in top border
|
|
if((x > sizer_top_left->x() + sizer_top_left->w() && x < sizer_top_right->x()) &&
|
|
(y < borders.updown()))
|
|
return ResizeTypeUp;
|
|
|
|
// we are in left border
|
|
if((x < borders.leftright()) && (y > sizer_top_left->y() + sizer_top_left->h()) &&
|
|
y < sizer_bottom_left->y())
|
|
return ResizeTypeLeft;
|
|
|
|
// we are in right border
|
|
if((x < sizer_top_right->x() + sizer_top_right->w()) &&
|
|
(x > sizer_top_right->x() + sizer_top_right->w() - borders.leftright()) &&
|
|
(y > sizer_top_right->y() + sizer_top_right->w()) &&
|
|
(y < sizer_bottom_left->y()))
|
|
return ResizeTypeRight;
|
|
|
|
// we are in bottom border
|
|
if((x > sizer_bottom_left->x() + sizer_bottom_left->w()) &&
|
|
(x < sizer_bottom_right->x()) &&
|
|
(y > sizer_bottom_left->y() + sizer_bottom_left->h() - borders.updown()) &&
|
|
(y < sizer_bottom_left->y() + sizer_bottom_left->h()))
|
|
return ResizeTypeDown;
|
|
|
|
|
|
return ResizeTypeNone;
|
|
}
|
|
|
|
/* unscramble direction, and check what combination it have
|
|
*
|
|
* TODO: direction should not have oposite sides included
|
|
* like ResizeTypeRight | ResizeTypeLeft
|
|
*/
|
|
void Frame::resize_window(int mouse_x, int mouse_y, long direction)
|
|
{
|
|
TRACE_FUNCTION("void Frame::resize_window(int mouse_x, int mouse_y, long direction)");
|
|
|
|
if(direction == 0)
|
|
EFATAL("calling resize on possible unresizable window");
|
|
|
|
if(direction & ResizeTypeNone)
|
|
return;
|
|
|
|
#warning "Add increment/decrement info from icccm"
|
|
|
|
// use collected increment/decrement info from icccm
|
|
/*int inc_w = fdata->plain.inc_w;
|
|
int inc_h = fdata->plain.inc_h; */
|
|
|
|
int tw = 0, th = 0;
|
|
|
|
if(direction & ResizeTypeLeft)
|
|
{
|
|
if(mouse_x > x())
|
|
tw = w() - (mouse_x - x()); // decrease
|
|
else
|
|
tw = w() + (x() - mouse_x); // increase
|
|
|
|
set_size(mouse_x, y(), tw, h(), false);
|
|
}
|
|
|
|
if(direction & ResizeTypeRight)
|
|
{
|
|
tw = mouse_x - x();
|
|
set_size(x(), y(), tw, h(), false);
|
|
}
|
|
|
|
if(direction & ResizeTypeUp)
|
|
{
|
|
if(mouse_y > y())
|
|
th = h() - (mouse_y - y()); //decrease
|
|
else
|
|
th = h() + (y() - mouse_y); //increase
|
|
|
|
set_size(x(), mouse_y, w(), th, false);
|
|
}
|
|
|
|
if(direction & ResizeTypeDown)
|
|
{
|
|
th = mouse_y - y();
|
|
set_size(x(), y(), w(), th, false);
|
|
}
|
|
|
|
}
|
|
|
|
void Frame::configure_notify(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::configure_notify(void)");
|
|
if(!ValidateDrawable(fdata->window))
|
|
return;
|
|
|
|
WindowManager::instance()->hints()->icccm_configure(fdata);
|
|
}
|
|
|
|
// apply shape on borders, based on shape mask
|
|
void Frame::shape_borders(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::shape_borders(void)");
|
|
|
|
#ifdef SHAPE
|
|
Window shape = XCreateSimpleWindow(fl_display, RootWindow(fl_display, fl_screen),
|
|
x(), y(), w(), h(), 0, 0, 0);
|
|
|
|
XRectangle rect[4];
|
|
// top
|
|
rect[0].x = SIZER_W;
|
|
rect[0].y = 0;
|
|
rect[0].width = w() - SIZER_W * 2;
|
|
rect[0].height = borders.updown();
|
|
|
|
// right
|
|
rect[1].x = w() - borders.leftright();
|
|
rect[1].y = SIZER_H;
|
|
rect[1].width = borders.leftright();
|
|
rect[1].height = h() - SIZER_H * 2;
|
|
|
|
// bottom
|
|
rect[2].x = SIZER_W;
|
|
rect[2].y = h() - borders.updown();
|
|
rect[2].width = w() - SIZER_W * 2;
|
|
rect[2].height = borders.updown();
|
|
|
|
// left
|
|
rect[3].x = 0;
|
|
rect[3].y = SIZER_H;
|
|
rect[3].width = borders.leftright();
|
|
rect[3].height = h() - SIZER_H * 2;
|
|
|
|
// modify our shape window
|
|
XShapeCombineRectangles(fl_display, shape, ShapeBounding, 0, 0, rect, 4, ShapeSubtract, Unsorted);
|
|
// apply our shape window as mask
|
|
XShapeCombineShape(fl_display, fl_xid(this), ShapeBounding, 0, 0, shape, ShapeBounding, ShapeSet);
|
|
XDestroyWindow(fl_display, shape);
|
|
#endif
|
|
}
|
|
|
|
// apply shape on edges, based on pixmap mask
|
|
#include "mask.xpm"
|
|
#include <efltk/Fl_Image.h>
|
|
void Frame::shape_edges(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::shape_edges(void)");
|
|
#if 0
|
|
// TODO: finish with other three edges
|
|
Fl_Image img((const char**)mask_xpm);
|
|
Pixmap mask = img.create_mask(img.width(), img.height());
|
|
|
|
Window shape = XCreateSimpleWindow(fl_display, RootWindow(fl_display, fl_screen),
|
|
x(), y(), w(), h(), 0, 0, 0);
|
|
|
|
XShapeCombineMask(fl_display, shape, ShapeBounding, 0, 0, mask, ShapeSubtract);
|
|
XShapeCombineShape(fl_display, fl_xid(this), ShapeBounding, 0, 0, shape, ShapeBounding, ShapeSet);
|
|
XDestroyWindow(fl_display, shape);
|
|
#endif
|
|
}
|
|
|
|
void Frame::maximize(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::maximize(void)");
|
|
|
|
if(state(FrameStateShaded))
|
|
{
|
|
ELOG("Maximizing shaded windows is bugy as devil himself !");
|
|
return;
|
|
}
|
|
|
|
if(fdata->type == FrameTypeDialog)
|
|
{
|
|
ELOG("Dialogs should not be resized !");
|
|
return;
|
|
}
|
|
|
|
if(state(FrameStateMaximized))
|
|
{
|
|
ELOG("Window is alread maximized");
|
|
return;
|
|
}
|
|
|
|
restore_x = x();
|
|
restore_y = y();
|
|
restore_w = w();
|
|
restore_h = h();
|
|
|
|
set_size(screen_x, screen_y, screen_w, screen_h, false);
|
|
set_state(FrameStateMaximized);
|
|
|
|
WindowManager::instance()->play_sound(SOUND_MAXIMIZE);
|
|
}
|
|
|
|
void Frame::restore(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::restore(void)");
|
|
|
|
if(!state(FrameStateMaximized))
|
|
{
|
|
ELOG("Restore unmaximized window ???");
|
|
return;
|
|
}
|
|
|
|
set_size(restore_x, restore_y, restore_w, restore_h, false);
|
|
clear_state(FrameStateMaximized);
|
|
|
|
WindowManager::instance()->play_sound(SOUND_RESTORE);
|
|
}
|
|
|
|
/* First will inspect does window participate for WM_DELETE_WINDOW
|
|
* message, and if not, it will be killed. That is the life...
|
|
*/
|
|
void Frame::close_kill(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::close_kill(void)");
|
|
|
|
Atom* protocols;
|
|
int n;
|
|
bool have_close = false;
|
|
|
|
if(XGetWMProtocols(fl_display, fdata->window, &protocols, &n))
|
|
{
|
|
for(int i = 0; i < n; i++)
|
|
{
|
|
if(protocols[i] == _XA_WM_DELETE_WINDOW)
|
|
have_close = true;
|
|
}
|
|
|
|
XFree(protocols);
|
|
}
|
|
if(have_close)
|
|
SendMessage(fdata->window, _XA_WM_PROTOCOLS, _XA_WM_DELETE_WINDOW);
|
|
else
|
|
{
|
|
ELOG("Frame killed");
|
|
XKillClient(fl_display, fdata->window);
|
|
}
|
|
|
|
WindowManager::instance()->play_sound(SOUND_CLOSE);
|
|
}
|
|
|
|
// window accepts input
|
|
void Frame::focus(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::focus(void)");
|
|
|
|
if(state(FrameStateFocused))
|
|
return;
|
|
|
|
if(!ValidateDrawable(fdata->window))
|
|
return;
|
|
|
|
WindowManager::instance()->clear_focus_windows();
|
|
|
|
borders_color(FOCUSED);
|
|
if(show_titlebar)
|
|
titlebar->focus();
|
|
|
|
//XSetInputFocus(fl_display, fdata->window, RevertToPointerRoot, fl_event_time);
|
|
XSetInputFocus(fl_display, fdata->window, RevertToPointerRoot, CurrentTime);
|
|
XInstallColormap(fl_display, fdata->colormap);
|
|
|
|
/* DO NOT use this !
|
|
* SendMessage(fdata->window, _XA_WM_PROTOCOLS, _XA_WM_TAKE_FOCUS);
|
|
*/
|
|
|
|
WindowManager::instance()->hints()->netwm_set_active_window(fdata->window);
|
|
set_state(FrameStateFocused);
|
|
}
|
|
|
|
void Frame::unfocus(void)
|
|
{
|
|
if(!state(FrameStateFocused))
|
|
return;
|
|
|
|
TRACE_FUNCTION("void Frame::unfocus(void)");
|
|
|
|
borders_color(UNFOCUSED);
|
|
if(show_titlebar)
|
|
titlebar->unfocus();
|
|
|
|
clear_state(FrameStateFocused);
|
|
}
|
|
|
|
void Frame::raise(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::raise(void)");
|
|
|
|
WindowManager* wm = WindowManager::instance();
|
|
if(wm->stack_list.size() <= 1)
|
|
{
|
|
ELOG("Only one window, restacking skipped");
|
|
focus();
|
|
return;
|
|
}
|
|
|
|
FrameList::iterator it = wm->stack_list.begin();
|
|
FrameList::iterator last = wm->stack_list.end();
|
|
|
|
while(it != last)
|
|
{
|
|
if(*it == this)
|
|
{
|
|
wm->stack_list.erase(it);
|
|
wm->stack_list.push_front(*it);
|
|
}
|
|
|
|
++it;
|
|
}
|
|
wm->restack_windows();
|
|
focus();
|
|
}
|
|
|
|
/* Send frame one stack position below.
|
|
* Note that as in raise(), aot_list is not checked
|
|
* since those windows are _always_ on top. This will
|
|
* rearange stack_list and call restack_windows(), as you guess.
|
|
*
|
|
* Yes, this can be avoided using XWindowChanges with
|
|
* x.sibling and x.stack_mode = Below, which is much faster
|
|
* but we will lose arangements in stack_list and posibility
|
|
* to update _NET_CLIENT_LIST_STACKING atom. So stick with this.
|
|
*
|
|
* PS: if you have idea how to combine XWindowChanges with X calls
|
|
* without needs to update stack_list, but to extract full stacking
|
|
* list for _NET_CLIENT_LIST_STACKING, let me know.
|
|
*/
|
|
void Frame::lower(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::lower(void)");
|
|
|
|
WindowManager* wm = WindowManager::instance();
|
|
|
|
if(wm->stack_list.size() <= 1)
|
|
{
|
|
ELOG("Only one window, restacking skipped");
|
|
return;
|
|
}
|
|
|
|
FrameList::iterator top = wm->stack_list.begin();
|
|
|
|
/* We are not at the top.
|
|
* This will prevent rise-lower-rise effect when
|
|
* function is called multiple times.
|
|
*/
|
|
if(this != *top)
|
|
return;
|
|
|
|
FrameList::iterator below = top;
|
|
++below;
|
|
|
|
if(*below)
|
|
{
|
|
std::swap(*top, *below);
|
|
}
|
|
|
|
wm->restack_windows();
|
|
focus();
|
|
}
|
|
|
|
/* When we want to shade window, child (fdata->window) must
|
|
* completely disapear (as should it be). Funny thing is
|
|
* if few pixels child is visible, some programs-windows
|
|
* will crash (like mrxvt).
|
|
*/
|
|
void Frame::shade(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::shade(void)");
|
|
|
|
if(!show_titlebar)
|
|
return;
|
|
|
|
if(state(FrameStateShaded))
|
|
return;
|
|
|
|
restore_x = x();
|
|
restore_y = y();
|
|
restore_w = w();
|
|
restore_h = h();
|
|
|
|
// place it to outside frame
|
|
int px = -(fdata->plain.offset_x + fdata->plain.w);
|
|
int py = -(fdata->plain.offset_y + fdata->plain.h);
|
|
|
|
XMoveWindow(fl_display, fdata->window, px, py);
|
|
XResizeWindow(fl_display, fl_xid(this), w(), titlebar->h() + borders.updown2x());
|
|
|
|
set_state(FrameStateShaded);
|
|
WindowManager::instance()->hints()->netwm_set_window_state(fdata);
|
|
|
|
WindowManager::instance()->play_sound(SOUND_SHADE);
|
|
|
|
// Configure not needed, since we do nothing usefull to window
|
|
//configure_notify();
|
|
}
|
|
|
|
void Frame::unshade(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::unshade(void)");
|
|
|
|
if(!show_titlebar)
|
|
return;
|
|
|
|
if(!state(FrameStateShaded))
|
|
{
|
|
ELOG("Unshade not shaded window ???");
|
|
return;
|
|
}
|
|
|
|
XMoveResizeWindow(fl_display, fdata->window, fdata->plain.offset_x, fdata->plain.offset_y,
|
|
fdata->plain.w, fdata->plain.h);
|
|
XResizeWindow(fl_display, fl_xid(this), w(), restore_h);
|
|
|
|
clear_state(FrameStateShaded);
|
|
WindowManager::instance()->hints()->netwm_set_window_state(fdata);
|
|
|
|
WindowManager::instance()->play_sound(SOUND_SHADE);
|
|
|
|
// Configure not needed, since we do nothing usefull to window
|
|
//configure_notify();
|
|
}
|
|
|
|
void Frame::borders_color(FrameBordersState s)
|
|
{
|
|
TRACE_FUNCTION("void Frame::borders_color(FrameBordersState s)");
|
|
|
|
Fl_Color szc = borders.sizers_color(s);
|
|
sizer_top_left->color(szc);
|
|
sizer_top_right->color(szc);
|
|
sizer_bottom_left->color(szc);
|
|
sizer_bottom_right->color(szc);
|
|
|
|
color(borders.border_color(s));
|
|
redraw();
|
|
}
|
|
|
|
void Frame::show_sizers(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::show_sizers(void)");
|
|
|
|
sizer_top_left->show();
|
|
sizer_top_right->show();
|
|
sizer_bottom_left->show();
|
|
sizer_bottom_right->show();
|
|
}
|
|
|
|
void Frame::hide_sizers(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::hide_sizers(void)");
|
|
|
|
sizer_top_left->hide();
|
|
sizer_top_right->hide();
|
|
sizer_bottom_left->hide();
|
|
sizer_bottom_right->hide();
|
|
}
|
|
|
|
void Frame::set_cursor(CursorType t)
|
|
{
|
|
WindowManager::instance()->set_cursor(this, t);
|
|
}
|
|
|
|
#if 0
|
|
/* This function will change window type, to some of
|
|
* netwm types. Note, this function should _not_ be used
|
|
* anywhere except from toolbar menu, where window can
|
|
* be modified (if allowed in options).
|
|
*
|
|
* TODO: If we want borderless window, netwm does not provide
|
|
* anything like _NET_WM_WINDOW_TYPE_SPLASH, but not for splashes.
|
|
* Maybe we should add something like _EDE_WM_WINDOW_TYPE_PLAIN ?
|
|
*/
|
|
void Frame::change_window_type(short type)
|
|
{
|
|
TRACE_FUNCTION("void Frame::change_window_type(short type)");
|
|
|
|
if(type == fdata->type)
|
|
return;
|
|
|
|
// set fdata->window borders, althought fdata->plain.borders is
|
|
// always 0
|
|
XSetWindowBorderWidth(fl_display, fdata->window, fdata->plain.border);
|
|
|
|
fdata->type = type;
|
|
|
|
switch(fdata->type)
|
|
{
|
|
case FrameTypeNormal:
|
|
borders.leftright(BORDER_LEFTRIGHT);
|
|
borders.updown(BORDER_UPDOWN);
|
|
break;
|
|
case FrameTypeDialog:
|
|
borders.leftright(BORDER_THIN_LEFTRIGHT);
|
|
borders.updown(BORDER_THIN_UPDOWN);
|
|
break;
|
|
case FrameTypeSplash:
|
|
case FrameTypeDesktop:
|
|
case FrameTypeMenu:
|
|
case FrameTypeDock:
|
|
borders.leftright(0);
|
|
borders.updown(0);
|
|
if(show_titlebar && titlebar)
|
|
{
|
|
titlebar->hide();
|
|
show_titlebar = false;
|
|
}
|
|
// they don't have visible borders
|
|
break;
|
|
}
|
|
|
|
// TODO: setting up initial window sizes
|
|
// should not be in setup_borders();
|
|
if(fdata->type == FrameTypeNormal || fdata->type == FrameTypeDialog)
|
|
{
|
|
int tx, ty;
|
|
tx = fdata->plain.x - borders.leftright();
|
|
ty = fdata->plain.y - borders.updown();
|
|
/*
|
|
if(tx < 0)
|
|
fdata->plain.x = borders.leftright();
|
|
if(ty < 0)
|
|
fdata->plain.y = borders.updown();
|
|
*/
|
|
x(fdata->plain.x);
|
|
y(fdata->plain.y);
|
|
|
|
w(fdata->plain.w + borders.leftright2x());
|
|
h(fdata->plain.h + borders.updown2x());
|
|
}
|
|
// means only borders
|
|
else
|
|
{
|
|
/* First resize window to size of child (x,y will be preserved)
|
|
* then resize child.
|
|
* XXX: child window's x,y are offsets from x,y of parent, so
|
|
* they are 0.
|
|
* We can't use Fl_Window::size(), since explicit window sizing
|
|
* does not work. In that case, slap it with hammer !
|
|
*/
|
|
XResizeWindow(fl_display, fl_xid(this), fdata->plain.w, fdata->plain.h);
|
|
XMoveResizeWindow(fl_display, fdata->window, 0, 0, fdata->plain.w, fdata->plain.h);
|
|
}
|
|
|
|
WindowManager::instance()->hints()->netwm_set_window_type(fdata);
|
|
}
|
|
#endif
|
|
/* After XGrabButton, WindowManager will FL_PUSH events
|
|
* redirect here. We must make sure after processing them
|
|
* redirect events further to fdata->window.
|
|
*
|
|
* We should not worry about transients (or any window
|
|
* it aot_list) since they will be always on top.
|
|
*
|
|
* TODO: Should we allow events on below windows event
|
|
* if transients are shown ???
|
|
*/
|
|
void Frame::content_click(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::content_click(void)");
|
|
|
|
ELOG("Clicked on frame: %s", FRAME_NAME(fdata));
|
|
|
|
if(fl_xevent.xbutton.button == 1)
|
|
raise();
|
|
|
|
XAllowEvents(fl_display, ReplayPointer, CurrentTime);
|
|
}
|
|
|
|
void Frame::show_coordinates_window(void)
|
|
{
|
|
if(!show_coordinates)
|
|
return;
|
|
|
|
TRACE_FUNCTION("void Frame::show_coordinates_window(void)");
|
|
|
|
// calculate center of frame, and show it there
|
|
int cx = (x() + w()/2) - (cview->w()/2);
|
|
int cy = (y() + h()/2) - (cview->h()/2);
|
|
|
|
/* Check if we are inside screen coordinates or showing window
|
|
* beyond them will not show window at all!
|
|
*/
|
|
if(cx < screen_x)
|
|
cx = screen_x;
|
|
if(cy < screen_y)
|
|
cy = screen_y;
|
|
if((cx + cview->w()) > screen_w)
|
|
cx = screen_w - cview->w();
|
|
if((cy + cview->h()) > screen_h)
|
|
cy = screen_h - cview->h();
|
|
|
|
cview->position(cx, cy);
|
|
//if(!cview->shown())
|
|
cview->show();
|
|
}
|
|
|
|
void Frame::update_coordinates_window(void)
|
|
{
|
|
if(!show_coordinates)
|
|
return;
|
|
|
|
TRACE_FUNCTION("void Frame::update_coordinates_window(void)");
|
|
int cx = (x() + w()/2) - (cview->w()/2);
|
|
int cy = (y() + h()/2) - (cview->h()/2);
|
|
|
|
if(cx < screen_x)
|
|
cx = screen_x;
|
|
if(cy < screen_y)
|
|
cy = screen_y;
|
|
if((cx + cview->w()) > screen_w)
|
|
cx = screen_w - cview->w();
|
|
if((cy + cview->h()) > screen_h)
|
|
cy = screen_h - cview->h();
|
|
|
|
cview->position(cx, cy);
|
|
cview->display_data(x(), y(), w(), h());
|
|
}
|
|
|
|
void Frame::hide_coordinates_window(void)
|
|
{
|
|
if(!show_coordinates)
|
|
return;
|
|
|
|
TRACE_FUNCTION("void Frame::hide_coordinates_window(void)");
|
|
cview->hide();
|
|
}
|
|
|
|
// handle events that efltk understainds
|
|
int Frame::handle(int event)
|
|
{
|
|
return events->handle_fltk(event);
|
|
}
|
|
|
|
int Frame::handle(XEvent* event)
|
|
{
|
|
return events->handle_x(event);
|
|
}
|
|
|
|
// this is here so FrameEventHandler can access Fl_Window
|
|
int Frame::handle_parent(int event)
|
|
{
|
|
return Fl_Window::handle(event);
|
|
}
|
|
|
|
/* Grab pointer, scheduling pointer events for future use.
|
|
* The only thing I can say is this is the only way of correct
|
|
* usage, althought I would like to see pointer in GrabModeAsync.
|
|
* Later is hardly possible (think so) because efltk will not be
|
|
* able to synchronize itself with X events.
|
|
*/
|
|
void Frame::grab_cursor(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::grab_cursor(void)");
|
|
|
|
if(cursor_grabbed)
|
|
return;
|
|
|
|
// use currently set cursor
|
|
Cursor cc = WindowManager::instance()->cursor_handler()->current_cursor();
|
|
|
|
if(XGrabPointer(fl_display, fl_xid(this),
|
|
true, /* owner events */
|
|
ButtonMotionMask | ButtonPressMask |ButtonReleaseMask | PointerMotionMask,
|
|
GrabModeAsync, /* pointer mode */
|
|
GrabModeAsync, /* keyboard mode */
|
|
None,
|
|
cc, fl_event_time) == GrabSuccess)
|
|
{
|
|
//XAllowEvents(fl_display, AsyncPointer, CurrentTime);
|
|
cursor_grabbed = true;
|
|
ELOG("Cursor grabbed");
|
|
}
|
|
}
|
|
|
|
void Frame::ungrab_cursor(void)
|
|
{
|
|
TRACE_FUNCTION("void Frame::ungrab_cursor(void)");
|
|
|
|
if(!cursor_grabbed)
|
|
return;
|
|
|
|
ELOG("Cursor ungrabbed");
|
|
Fl::event_is_click(0); // avoid double click
|
|
//XAllowEvents(fl_display, Fl::event() == FL_PUSH ? ReplayPointer : AsyncPointer, CurrentTime);
|
|
XUngrabPointer(fl_display, fl_event_time);
|
|
XFlush(fl_display); // make sure we are out of danger before continuing...
|
|
// because we "pushed back" the FL_PUSH, make it think no buttons are down:
|
|
Fl::e_state &= 0xffffff;
|
|
Fl::e_keysym = 0;
|
|
|
|
cursor_grabbed = false;
|
|
}
|
|
|
|
void Frame::display_internals(void)
|
|
{
|
|
EPRINTF("-----------------------------------\n");
|
|
EPRINTF("window : %lx\n", fdata->window);
|
|
EPRINTF("transient : %lx\n", fdata->transient_win);
|
|
EPRINTF("colormap : %lx\n", fdata->colormap);
|
|
EPRINTF("state : %li\n", fdata->state);
|
|
EPRINTF("options : %li\n", fdata->option);
|
|
EPRINTF("type : %i\n", fdata->type);
|
|
EPRINTF("min width : %i\n", fdata->plain.min_w);
|
|
EPRINTF("min height : %i\n", fdata->plain.min_h);
|
|
EPRINTF("max width : %i\n", fdata->plain.max_w);
|
|
EPRINTF("max height : %i\n", fdata->plain.max_h);
|
|
EPRINTF("x : %i\n", fdata->plain.x);
|
|
EPRINTF("y : %i\n", fdata->plain.y);
|
|
EPRINTF("w : %i\n", fdata->plain.w);
|
|
EPRINTF("h : %i\n", fdata->plain.h);
|
|
EPRINTF("x (framed) : %i\n", x());
|
|
EPRINTF("y (framed) : %i\n", y());
|
|
EPRINTF("w (framed) : %i\n", w());
|
|
EPRINTF("h (framed) : %i\n", h());
|
|
EPRINTF("gravity : %i\n", fdata->win_gravity);
|
|
EPRINTF("autoplace : %i\n", fdata->autoplace);
|
|
EPRINTF("have icon : %s\n", (fdata->icon_pixmap ? "yes" : "no"));
|
|
EPRINTF("have icon mask : %s\n", (fdata->icon_mask ? "yes" : "no"));
|
|
EPRINTF("label : %s\n", (fdata->label ? fdata->label : "NULL"));
|
|
EPRINTF("-----------------------------------\n");
|
|
}
|