/* * wview.c - View dispatch layer for WINGs * * Pure logic: tree management, layout, notifications. * All platform-specific operations go through W_ViewBackend vtable. */ #include "WINGsP.h" /* the notifications about views */ const char *WMViewSizeDidChangeNotification = "WMViewSizeDidChangeNotification"; const char *WMViewFocusDidChangeNotification = "WMViewFocusDidChangeNotification"; const char *WMViewRealizedNotification = "WMViewRealizedNotification"; 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; view = wmalloc(sizeof(W_View)); view->screen = screen; if (parent != NULL) { 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; const W_ViewBackend *vb = screen->viewBackend; void *vctx = screen->viewBackendCtx; view = createView(screen, NULL); vb->create_root(vctx, view); view->flags.realized = 1; view->flags.mapped = 1; view->flags.root = 1; 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; 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; return view; } void W_RealizeView(W_View * view) { W_View *ptr; const W_ViewBackend *vb = view->screen->viewBackend; void *vctx = view->screen->viewBackendCtx; 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; } vb->realize(vctx, 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) { const W_ViewBackend *vb = view->screen->viewBackend; void *vctx = view->screen->viewBackendCtx; assert(!view->flags.topLevel); unparentView(view); adoptChildView(newParent, view); if (view->flags.realized) { if (newParent->flags.realized) { vb->reparent(vctx, view, newParent, 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)) { const W_ViewBackend *vb = view->screen->viewBackend; vb->raise(view->screen->viewBackendCtx, view); } } void W_LowerView(W_View * view) { if (W_VIEW_REALIZED(view)) { const W_ViewBackend *vb = view->screen->viewBackend; vb->lower(view->screen->viewBackendCtx, view); } } void W_MapView(W_View * view) { if (!view->flags.mapped) { if (view->flags.realized) { const W_ViewBackend *vb = view->screen->viewBackend; vb->map(view->screen->viewBackendCtx, view); view->flags.mapped = 1; } else { view->flags.mapWhenRealized = 1; } } } void W_MapSubviews(W_View * view) { const W_ViewBackend *vb = view->screen->viewBackend; W_View *ptr; vb->map_subviews(view->screen->viewBackendCtx, view); ptr = view->childrenList; while (ptr) { ptr->flags.mapped = 1; ptr->flags.mapWhenRealized = 0; ptr = ptr->nextSister; } } void W_UnmapSubviews(W_View * view) { const W_ViewBackend *vb = view->screen->viewBackend; W_View *ptr; vb->unmap_subviews(view->screen->viewBackendCtx, view); ptr = view->childrenList; while (ptr) { ptr->flags.mapped = 0; ptr->flags.mapWhenRealized = 0; ptr = ptr->nextSister; } } void W_UnmapView(W_View * view) { view->flags.mapWhenRealized = 0; if (!view->flags.mapped) return; { const W_ViewBackend *vb = view->screen->viewBackend; vb->unmap(view->screen->viewBackendCtx, view); } 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) { const W_ViewBackend *vb = view->screen->viewBackend; vb->destroy(view->screen->viewBackendCtx, view); } /* 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) { const W_ViewBackend *vb = view->screen->viewBackend; vb->move(view->screen->viewBackendCtx, view, 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) { 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; if (view->flags.realized) { const W_ViewBackend *vb = view->screen->viewBackend; vb->resize(view->screen->viewBackendCtx, view, width, height); } view->size.width = width; view->size.height = height; if (view->delegate && view->delegate->didResize) { (*view->delegate->didResize) (view->delegate, view); } if (view->flags.notifySizeChanged) WMPostNotificationName(WMViewSizeDidChangeNotification, view, NULL); } void W_RedisplayView(W_View * view) { if (!view->flags.mapped) return; { const W_ViewBackend *vb = view->screen->viewBackend; vb->redisplay(view->screen->viewBackendCtx, view); } } void W_SetViewBackgroundColor(W_View * view, WMColor * color) { if (view->backColor) WMReleaseColor(view->backColor); view->backColor = WMRetainColor(color); if (view->flags.realized) { const W_ViewBackend *vb = view->screen->viewBackend; vb->set_background_color(view->screen->viewBackendCtx, view, color); } } void W_SetViewBackgroundPixmap(W_View *view, WMPixmap *pix) { if (view->backImage) WMReleasePixmap(view->backImage); view->backImage = WMRetainPixmap(pix); /* Pixmap background is X11-specific; Wayland uses pixman rendering. * The backend's set_background_color handles the realized case for X11. * For Wayland this is a no-op at the view level. */ } void W_SetViewCursor(W_View * view, Cursor cursor) { view->cursor = cursor; if (W_VIEW_REALIZED(view)) { const W_ViewBackend *vb = view->screen->viewBackend; vb->set_cursor(view->screen->viewBackendCtx, view, 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; WMEvent 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 { memset(&event, 0, sizeof(event)); event.u.focus.mode = NotifyNormal; event.u.focus.detail = NotifyDetailNone; if (info->focused) { /* simulate FocusOut event */ event.type = WME_FOCUS_OUT; W_DispatchMessage(info->focused, &event); } info->focused = view; } if (view) { /* simulate FocusIn event */ event.type = WME_FOCUS_IN; W_DispatchMessage(view, &event); } } void W_BroadcastMessage(W_View * targetParent, WMEvent * event) { W_View *target; target = targetParent->childrenList; while (target != NULL) { W_DispatchMessage(target, event); target = target->nextSister; } } void W_DispatchMessage(W_View * target, WMEvent * event) { if (target->window == None) return; event->window = (WNativeWindow)target->window; WMHandleEvent(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) { const W_ViewBackend *vb = view->screen->viewBackend; return vb->get_screen_position(view->screen->viewBackendCtx, view); } 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)); }