mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-18 12:00:31 +01:00
Because the previous patch brought a (welcome) change in the public API, seize the opportunity to go further in the improvement.
688 lines
15 KiB
C
688 lines
15 KiB
C
|
|
#include "WINGsP.h"
|
|
|
|
#include <X11/Xresource.h>
|
|
|
|
/* the notifications about views */
|
|
|
|
const char *WMViewSizeDidChangeNotification = "WMViewSizeDidChangeNotification";
|
|
const char *WMViewFocusDidChangeNotification = "WMViewFocusDidChangeNotification";
|
|
const char *WMViewRealizedNotification = "WMViewRealizedNotification";
|
|
|
|
#define EVENT_MASK \
|
|
KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask| \
|
|
EnterWindowMask|LeaveWindowMask|PointerMotionMask|ExposureMask| \
|
|
VisibilityChangeMask|FocusChangeMask|PropertyChangeMask|\
|
|
SubstructureNotifyMask|SubstructureRedirectMask
|
|
|
|
static const XSetWindowAttributes defAtts = {
|
|
None, /* background_pixmap */
|
|
0, /* background_pixel */
|
|
CopyFromParent, /* border_pixmap */
|
|
0, /* border_pixel */
|
|
ForgetGravity, /* bit_gravity */
|
|
ForgetGravity, /* win_gravity */
|
|
NotUseful, /* backing_store */
|
|
(unsigned)~0, /* backing_planes */
|
|
0, /* backing_pixel */
|
|
False, /* save_under */
|
|
EVENT_MASK, /* event_mask */
|
|
0, /* do_not_propagate_mask */
|
|
False, /* override_redirect */
|
|
None, /* colormap */
|
|
None /* cursor */
|
|
};
|
|
|
|
static XContext ViewContext = 0; /* context for views */
|
|
|
|
W_View *W_GetViewForXWindow(Display * display, Window window)
|
|
{
|
|
W_View *view;
|
|
|
|
if (XFindContext(display, window, ViewContext, (XPointer *) & view) == 0) {
|
|
return view;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void unparentView(W_View * view)
|
|
{
|
|
/* remove from parent's children list */
|
|
if (view->parent != NULL) {
|
|
W_View *ptr;
|
|
|
|
ptr = view->parent->childrenList;
|
|
if (ptr == view) {
|
|
view->parent->childrenList = view->nextSister;
|
|
} else {
|
|
while (ptr != NULL) {
|
|
if (ptr->nextSister == view) {
|
|
ptr->nextSister = view->nextSister;
|
|
break;
|
|
}
|
|
ptr = ptr->nextSister;
|
|
}
|
|
}
|
|
}
|
|
view->parent = NULL;
|
|
}
|
|
|
|
static void adoptChildView(W_View * view, W_View * child)
|
|
{
|
|
child->nextSister = NULL;
|
|
|
|
/* add to end of children list of parent */
|
|
if (view->childrenList == NULL) {
|
|
view->childrenList = child;
|
|
} else {
|
|
W_View *ptr;
|
|
|
|
ptr = view->childrenList;
|
|
while (ptr->nextSister != NULL)
|
|
ptr = ptr->nextSister;
|
|
ptr->nextSister = child;
|
|
}
|
|
child->parent = view;
|
|
}
|
|
|
|
static W_View *createView(W_Screen * screen, W_View * parent)
|
|
{
|
|
W_View *view;
|
|
|
|
if (ViewContext == 0)
|
|
ViewContext = XUniqueContext();
|
|
|
|
view = wmalloc(sizeof(W_View));
|
|
view->screen = screen;
|
|
|
|
if (parent != NULL) {
|
|
/* attributes are not valid for root window */
|
|
view->attribFlags = CWEventMask | CWBitGravity;
|
|
view->attribs = defAtts;
|
|
|
|
view->attribFlags |= CWBackPixel | CWColormap | CWBorderPixel | CWBackPixmap;
|
|
view->attribs.background_pixmap = None;
|
|
view->attribs.background_pixel = W_PIXEL(screen->gray);
|
|
view->attribs.border_pixel = W_PIXEL(screen->black);
|
|
view->attribs.colormap = screen->colormap;
|
|
|
|
view->backColor = WMRetainColor(screen->gray);
|
|
|
|
adoptChildView(parent, view);
|
|
}
|
|
|
|
view->xic = 0;
|
|
|
|
view->refCount = 1;
|
|
|
|
view->eventHandlers = WMCreateArrayWithDestructor(4, wfree);
|
|
|
|
return view;
|
|
}
|
|
|
|
W_View *W_CreateView(W_View * parent)
|
|
{
|
|
return createView(parent->screen, parent);
|
|
}
|
|
|
|
W_View *W_CreateRootView(W_Screen * screen)
|
|
{
|
|
W_View *view;
|
|
|
|
view = createView(screen, NULL);
|
|
|
|
view->window = screen->rootWin;
|
|
|
|
view->flags.realized = 1;
|
|
view->flags.mapped = 1;
|
|
view->flags.root = 1;
|
|
|
|
view->size.width = WidthOfScreen(ScreenOfDisplay(screen->display, screen->screen));
|
|
view->size.height = HeightOfScreen(ScreenOfDisplay(screen->display, screen->screen));
|
|
|
|
return view;
|
|
}
|
|
|
|
W_View *W_CreateTopView(W_Screen * screen)
|
|
{
|
|
W_View *view;
|
|
|
|
view = createView(screen, screen->rootView);
|
|
if (!view)
|
|
return NULL;
|
|
|
|
view->flags.topLevel = 1;
|
|
view->attribs.event_mask |= StructureNotifyMask;
|
|
|
|
return view;
|
|
}
|
|
|
|
W_View *W_CreateUnmanagedTopView(W_Screen * screen)
|
|
{
|
|
W_View *view;
|
|
|
|
view = createView(screen, screen->rootView);
|
|
if (!view)
|
|
return NULL;
|
|
|
|
view->flags.topLevel = 1;
|
|
view->attribs.event_mask |= StructureNotifyMask;
|
|
|
|
view->attribFlags |= CWOverrideRedirect;
|
|
view->attribs.override_redirect = True;
|
|
|
|
return view;
|
|
}
|
|
|
|
void W_RealizeView(W_View * view)
|
|
{
|
|
Window parentWID;
|
|
Display *dpy = view->screen->display;
|
|
W_View *ptr;
|
|
|
|
assert(view->size.width > 0);
|
|
assert(view->size.height > 0);
|
|
|
|
if (view->parent && !view->parent->flags.realized) {
|
|
wwarning("trying to realize widget of unrealized parent");
|
|
return;
|
|
}
|
|
|
|
if (!view->flags.realized) {
|
|
|
|
if (view->parent == NULL) {
|
|
wwarning("trying to realize widget without parent");
|
|
return;
|
|
}
|
|
|
|
parentWID = view->parent->window;
|
|
view->window = XCreateWindow(dpy, parentWID, view->pos.x, view->pos.y,
|
|
view->size.width, view->size.height, 0,
|
|
view->screen->depth, InputOutput,
|
|
view->screen->visual, view->attribFlags, &view->attribs);
|
|
|
|
XSaveContext(dpy, view->window, ViewContext, (XPointer) view);
|
|
|
|
view->flags.realized = 1;
|
|
|
|
if (view->flags.mapWhenRealized) {
|
|
W_MapView(view);
|
|
view->flags.mapWhenRealized = 0;
|
|
}
|
|
|
|
WMPostNotificationName(WMViewRealizedNotification, view, NULL);
|
|
}
|
|
|
|
/* realize children */
|
|
ptr = view->childrenList;
|
|
while (ptr != NULL) {
|
|
W_RealizeView(ptr);
|
|
|
|
ptr = ptr->nextSister;
|
|
}
|
|
}
|
|
|
|
void W_ReparentView(W_View * view, W_View * newParent, int x, int y)
|
|
{
|
|
Display *dpy = view->screen->display;
|
|
|
|
assert(!view->flags.topLevel);
|
|
|
|
unparentView(view);
|
|
adoptChildView(newParent, view);
|
|
|
|
if (view->flags.realized) {
|
|
if (newParent->flags.realized) {
|
|
XReparentWindow(dpy, view->window, newParent->window, x, y);
|
|
} else {
|
|
wwarning("trying to reparent realized view to unrealized parent");
|
|
return;
|
|
}
|
|
}
|
|
|
|
view->pos.x = x;
|
|
view->pos.y = y;
|
|
}
|
|
|
|
void W_RaiseView(W_View * view)
|
|
{
|
|
if (W_VIEW_REALIZED(view))
|
|
XRaiseWindow(W_VIEW_DISPLAY(view), W_VIEW_DRAWABLE(view));
|
|
}
|
|
|
|
void W_LowerView(W_View * view)
|
|
{
|
|
if (W_VIEW_REALIZED(view))
|
|
XLowerWindow(W_VIEW_DISPLAY(view), W_VIEW_DRAWABLE(view));
|
|
}
|
|
|
|
void W_MapView(W_View * view)
|
|
{
|
|
if (!view->flags.mapped) {
|
|
if (view->flags.realized) {
|
|
XMapRaised(view->screen->display, view->window);
|
|
XFlush(view->screen->display);
|
|
view->flags.mapped = 1;
|
|
} else {
|
|
view->flags.mapWhenRealized = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* W_MapSubviews-
|
|
* maps all children of the current view that where already realized.
|
|
*/
|
|
void W_MapSubviews(W_View * view)
|
|
{
|
|
XMapSubwindows(view->screen->display, view->window);
|
|
XFlush(view->screen->display);
|
|
|
|
view = view->childrenList;
|
|
while (view) {
|
|
view->flags.mapped = 1;
|
|
view->flags.mapWhenRealized = 0;
|
|
view = view->nextSister;
|
|
}
|
|
}
|
|
|
|
void W_UnmapSubviews(W_View * view)
|
|
{
|
|
XUnmapSubwindows(view->screen->display, view->window);
|
|
XFlush(view->screen->display);
|
|
|
|
view = view->childrenList;
|
|
while (view) {
|
|
view->flags.mapped = 0;
|
|
view->flags.mapWhenRealized = 0;
|
|
view = view->nextSister;
|
|
}
|
|
}
|
|
|
|
void W_UnmapView(W_View * view)
|
|
{
|
|
view->flags.mapWhenRealized = 0;
|
|
if (!view->flags.mapped)
|
|
return;
|
|
|
|
XUnmapWindow(view->screen->display, view->window);
|
|
XFlush(view->screen->display);
|
|
|
|
view->flags.mapped = 0;
|
|
}
|
|
|
|
W_View *W_TopLevelOfView(W_View * view)
|
|
{
|
|
W_View *toplevel;
|
|
|
|
for (toplevel = view; toplevel && !toplevel->flags.topLevel; toplevel = toplevel->parent) ;
|
|
|
|
return toplevel;
|
|
}
|
|
|
|
static void destroyView(W_View * view)
|
|
{
|
|
W_View *ptr;
|
|
|
|
if (view->flags.alreadyDead)
|
|
return;
|
|
view->flags.alreadyDead = 1;
|
|
|
|
/* delete the balloon text for the view, if there's any */
|
|
WMSetBalloonTextForView(NULL, view);
|
|
|
|
if (view->nextFocusChain)
|
|
view->nextFocusChain->prevFocusChain = view->prevFocusChain;
|
|
if (view->prevFocusChain)
|
|
view->prevFocusChain->nextFocusChain = view->nextFocusChain;
|
|
|
|
/* Do not leave focus in a inexisting control */
|
|
if (W_FocusedViewOfToplevel(W_TopLevelOfView(view)) == view)
|
|
W_SetFocusOfTopLevel(W_TopLevelOfView(view), NULL);
|
|
|
|
if (view->flags.topLevel) {
|
|
W_FocusInfo *info = view->screen->focusInfo;
|
|
/* remove focus information associated to this toplevel */
|
|
|
|
if (info) {
|
|
if (info->toplevel == view) {
|
|
view->screen->focusInfo = info->next;
|
|
wfree(info);
|
|
} else {
|
|
while (info->next) {
|
|
if (info->next->toplevel == view)
|
|
break;
|
|
info = info->next;
|
|
}
|
|
if (info->next) {
|
|
W_FocusInfo *next = info->next->next;
|
|
wfree(info->next);
|
|
info->next = next;
|
|
}
|
|
/* else the toplevel did not have any focused subview */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* destroy children recursively */
|
|
while (view->childrenList != NULL) {
|
|
ptr = view->childrenList;
|
|
ptr->flags.parentDying = 1;
|
|
|
|
W_DestroyView(ptr);
|
|
|
|
if (ptr == view->childrenList) {
|
|
view->childrenList = ptr->nextSister;
|
|
ptr->parent = NULL;
|
|
}
|
|
}
|
|
|
|
W_CallDestroyHandlers(view);
|
|
|
|
if (view->flags.realized) {
|
|
XDeleteContext(view->screen->display, view->window, ViewContext);
|
|
|
|
/* if parent is being destroyed, it will die naturaly */
|
|
if (!view->flags.parentDying || view->flags.topLevel)
|
|
XDestroyWindow(view->screen->display, view->window);
|
|
}
|
|
|
|
/* remove self from parent's children list */
|
|
unparentView(view);
|
|
|
|
/* the array has a wfree() destructor that will be called automatically */
|
|
WMFreeArray(view->eventHandlers);
|
|
view->eventHandlers = NULL;
|
|
|
|
WMRemoveNotificationObserver(view);
|
|
|
|
W_FreeViewXdndPart(view);
|
|
|
|
if (view->backColor)
|
|
WMReleaseColor(view->backColor);
|
|
|
|
wfree(view);
|
|
}
|
|
|
|
void W_DestroyView(W_View * view)
|
|
{
|
|
view->refCount--;
|
|
|
|
if (view->refCount < 1) {
|
|
destroyView(view);
|
|
}
|
|
}
|
|
|
|
void W_MoveView(W_View * view, int x, int y)
|
|
{
|
|
assert(view->flags.root == 0);
|
|
|
|
if (view->delegate && view->delegate->willMove) {
|
|
(*view->delegate->willMove) (view->delegate, view, &x, &y);
|
|
}
|
|
|
|
if (view->pos.x == x && view->pos.y == y)
|
|
return;
|
|
|
|
if (view->flags.realized) {
|
|
XMoveWindow(view->screen->display, view->window, x, y);
|
|
}
|
|
view->pos.x = x;
|
|
view->pos.y = y;
|
|
|
|
if (view->delegate && view->delegate->didMove) {
|
|
(*view->delegate->didMove) (view->delegate, view);
|
|
}
|
|
}
|
|
|
|
void W_ResizeView(W_View * view, unsigned int width, unsigned int height)
|
|
{
|
|
/*int shrinked; */
|
|
|
|
if (view->delegate && view->delegate->willResize) {
|
|
(*view->delegate->willResize) (view->delegate, view, &width, &height);
|
|
}
|
|
|
|
assert(width > 0);
|
|
assert(height > 0);
|
|
|
|
if (view->size.width == width && view->size.height == height)
|
|
return;
|
|
|
|
/*shrinked = width < view->size.width || height < view->size.height; */
|
|
|
|
if (view->flags.realized) {
|
|
XResizeWindow(view->screen->display, view->window, width, height);
|
|
}
|
|
view->size.width = width;
|
|
view->size.height = height;
|
|
|
|
if (view->delegate && view->delegate->didResize) {
|
|
(*view->delegate->didResize) (view->delegate, view);
|
|
}
|
|
|
|
/* // TODO. replace in WINGs code, with the didResize delegate */
|
|
if (view->flags.notifySizeChanged)
|
|
WMPostNotificationName(WMViewSizeDidChangeNotification, view, NULL);
|
|
}
|
|
|
|
void W_RedisplayView(W_View * view)
|
|
{
|
|
XEvent ev;
|
|
|
|
if (!view->flags.mapped)
|
|
return;
|
|
|
|
ev.xexpose.type = Expose;
|
|
ev.xexpose.display = view->screen->display;
|
|
ev.xexpose.window = view->window;
|
|
ev.xexpose.count = 0;
|
|
|
|
ev.xexpose.serial = 0;
|
|
|
|
WMHandleEvent(&ev);
|
|
}
|
|
|
|
void W_SetViewBackgroundColor(W_View * view, WMColor * color)
|
|
{
|
|
if (view->backColor)
|
|
WMReleaseColor(view->backColor);
|
|
view->backColor = WMRetainColor(color);
|
|
|
|
view->attribFlags |= CWBackPixel;
|
|
view->attribFlags &= ~CWBackPixmap;
|
|
view->attribs.background_pixel = W_PIXEL(color);
|
|
if (view->flags.realized) {
|
|
XSetWindowBackground(view->screen->display, view->window, W_PIXEL(color));
|
|
XClearWindow(view->screen->display, view->window);
|
|
}
|
|
}
|
|
|
|
void W_SetViewBackgroundPixmap(W_View *view, WMPixmap *pix)
|
|
{
|
|
if (view->backImage)
|
|
WMReleasePixmap(view->backImage);
|
|
view->backImage = WMRetainPixmap(pix);
|
|
|
|
view->attribFlags |= CWBackPixmap;
|
|
view->attribFlags &= ~CWBackPixel;
|
|
view->attribs.background_pixmap = pix->pixmap;
|
|
if (view->flags.realized) {
|
|
XSetWindowBackgroundPixmap(view->screen->display, view->window, pix->pixmap);
|
|
XClearWindow(view->screen->display, view->window);
|
|
}
|
|
}
|
|
|
|
void W_SetViewCursor(W_View * view, Cursor cursor)
|
|
{
|
|
view->cursor = cursor;
|
|
if (W_VIEW_REALIZED(view)) {
|
|
XDefineCursor(W_VIEW_DISPLAY(view), W_VIEW_DRAWABLE(view), cursor);
|
|
} else {
|
|
view->attribFlags |= CWCursor;
|
|
view->attribs.cursor = cursor;
|
|
}
|
|
}
|
|
|
|
W_View *W_FocusedViewOfToplevel(W_View * view)
|
|
{
|
|
WMScreen *scr = view->screen;
|
|
W_FocusInfo *info;
|
|
|
|
for (info = scr->focusInfo; info != NULL; info = info->next)
|
|
if (view == info->toplevel)
|
|
break;
|
|
|
|
if (!info)
|
|
return NULL;
|
|
|
|
return info->focused;
|
|
}
|
|
|
|
void W_SetFocusOfTopLevel(W_View * toplevel, W_View * view)
|
|
{
|
|
WMScreen *scr = toplevel->screen;
|
|
XEvent event;
|
|
W_FocusInfo *info;
|
|
|
|
for (info = scr->focusInfo; info != NULL; info = info->next)
|
|
if (toplevel == info->toplevel)
|
|
break;
|
|
|
|
if (!info) {
|
|
info = wmalloc(sizeof(W_FocusInfo));
|
|
info->toplevel = toplevel;
|
|
info->focused = view;
|
|
info->next = scr->focusInfo;
|
|
scr->focusInfo = info;
|
|
} else {
|
|
event.xfocus.mode = NotifyNormal;
|
|
event.xfocus.detail = NotifyDetailNone;
|
|
if (info->focused) {
|
|
/* simulate FocusOut event */
|
|
event.xfocus.type = FocusOut;
|
|
W_DispatchMessage(info->focused, &event);
|
|
}
|
|
info->focused = view;
|
|
}
|
|
if (view) {
|
|
/* simulate FocusIn event */
|
|
event.xfocus.type = FocusIn;
|
|
W_DispatchMessage(view, &event);
|
|
}
|
|
}
|
|
|
|
void W_BroadcastMessage(W_View * targetParent, XEvent * event)
|
|
{
|
|
W_View *target;
|
|
|
|
target = targetParent->childrenList;
|
|
while (target != NULL) {
|
|
W_DispatchMessage(target, event);
|
|
|
|
target = target->nextSister;
|
|
}
|
|
}
|
|
|
|
void W_DispatchMessage(W_View * target, XEvent * event)
|
|
{
|
|
if (target->window == None)
|
|
return;
|
|
event->xclient.window = target->window;
|
|
event->xclient.display = target->screen->display;
|
|
|
|
WMHandleEvent(event);
|
|
/*
|
|
XSendEvent(target->screen->display, target->window, False,
|
|
SubstructureNotifyMask, event);
|
|
*/
|
|
}
|
|
|
|
WMView *W_RetainView(WMView * view)
|
|
{
|
|
view->refCount++;
|
|
|
|
return view;
|
|
}
|
|
|
|
void W_ReleaseView(WMView * view)
|
|
{
|
|
view->refCount--;
|
|
|
|
if (view->refCount < 1) {
|
|
destroyView(view);
|
|
}
|
|
}
|
|
|
|
WMWidget *WMWidgetOfView(WMView * view)
|
|
{
|
|
return view->self;
|
|
}
|
|
|
|
WMSize WMGetViewSize(WMView * view)
|
|
{
|
|
return view->size;
|
|
}
|
|
|
|
WMPoint WMGetViewPosition(WMView * view)
|
|
{
|
|
return view->pos;
|
|
}
|
|
|
|
void WMSetViewNotifySizeChanges(WMView * view, Bool flag)
|
|
{
|
|
view->flags.notifySizeChanged = ((flag == 0) ? 0 : 1);
|
|
}
|
|
|
|
Window WMViewXID(WMView * view)
|
|
{
|
|
return view->window;
|
|
}
|
|
|
|
WMPoint WMGetViewScreenPosition(WMView * view)
|
|
{
|
|
WMScreen *scr = W_VIEW_SCREEN(view);
|
|
Window foo;
|
|
int x, y, topX, topY;
|
|
unsigned int bar;
|
|
WMView *topView;
|
|
|
|
topView = view;
|
|
while (topView->parent && topView->parent != scr->rootView)
|
|
topView = topView->parent;
|
|
|
|
if (!XGetGeometry(scr->display, W_VIEW_DRAWABLE(topView), &foo, &topX, &topY, &bar, &bar, &bar, &bar)) {
|
|
topX = topY = 0;
|
|
}
|
|
|
|
XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(view), scr->rootWin, 0, 0, &x, &y, &foo);
|
|
|
|
return wmkpoint(x - topX, y - topY);
|
|
}
|
|
|
|
static void resizedParent(void *self, WMNotification * notif)
|
|
{
|
|
WMSize size = WMGetViewSize((WMView *) WMGetNotificationObject(notif));
|
|
WMView *view = (WMView *) self;
|
|
|
|
W_MoveView(view, view->leftOffs, view->topOffs);
|
|
W_ResizeView(view, size.width - (view->leftOffs + view->rightOffs),
|
|
size.height - (view->topOffs + view->bottomOffs));
|
|
}
|
|
|
|
void WMSetViewExpandsToParent(WMView * view, int leftOffs, int topOffs, int rightOffs, int bottomOffs)
|
|
{
|
|
WMSize size = view->parent->size;
|
|
|
|
view->topOffs = topOffs;
|
|
view->bottomOffs = bottomOffs;
|
|
view->leftOffs = leftOffs;
|
|
view->rightOffs = rightOffs;
|
|
|
|
WMAddNotificationObserver(resizedParent, view, WMViewSizeDidChangeNotification, view->parent);
|
|
WMSetViewNotifySizeChanges(view->parent, True);
|
|
|
|
W_MoveView(view, leftOffs, topOffs);
|
|
W_ResizeView(view, size.width - (leftOffs + rightOffs), size.height - (topOffs + bottomOffs));
|
|
}
|