/* * Twin - A Tiny Window System * Copyright © 2004 Keith Packard * All rights reserved. * * This Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with the Gnome Library; see the file COPYING.LIB. If not, * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "twinint.h" void _twin_box_init (twin_box_t *box, twin_box_t *parent, twin_window_t *window, twin_box_dir_t dir, twin_dispatch_proc_t dispatch) { static twin_widget_layout_t preferred = { 0, 0, 0, 0 }; _twin_widget_init (&box->widget, parent, window, preferred, dispatch); box->dir = dir; box->children = NULL; box->button_down = NULL; box->focus = NULL; } static twin_dispatch_result_t _twin_box_query_geometry (twin_box_t *box) { twin_widget_t *child; twin_event_t ev; twin_widget_layout_t preferred; preferred.width = 0; preferred.height = 0; if (box->dir == TwinBoxHorz) { preferred.stretch_width = 0; preferred.stretch_height = 10000; } else { preferred.stretch_width = 10000; preferred.stretch_height = 0; } /* * Find preferred geometry */ for (child = box->children; child; child = child->next) { if (child->layout) { ev.kind = TwinEventQueryGeometry; (*child->dispatch) (child, &ev); } if (box->dir == TwinBoxHorz) { preferred.width += child->preferred.width; preferred.stretch_width += child->preferred.stretch_width; if (child->preferred.height > preferred.height) preferred.height = child->preferred.height; if (child->preferred.stretch_height < preferred.stretch_height) preferred.stretch_height = child->preferred.stretch_height; } else { preferred.height += child->preferred.height; preferred.stretch_height += child->preferred.stretch_height; if (child->preferred.width > preferred.width) preferred.width = child->preferred.width; if (child->preferred.stretch_width < preferred.stretch_width) preferred.stretch_width = child->preferred.stretch_width; } } box->widget.preferred = preferred; return TwinDispatchContinue; } static twin_dispatch_result_t _twin_box_configure (twin_box_t *box) { twin_coord_t width = _twin_widget_width (box); twin_coord_t height = _twin_widget_height (box); twin_coord_t actual; twin_coord_t pref; twin_coord_t delta; twin_coord_t delta_remain; twin_coord_t stretch = 0; twin_coord_t pos = 0; twin_widget_t *child; if (box->dir == TwinBoxHorz) { stretch = box->widget.preferred.stretch_width; actual = width; pref = box->widget.preferred.width; } else { stretch = box->widget.preferred.stretch_height; actual = height; pref = box->widget.preferred.height; } if (!stretch) stretch = 1; delta = delta_remain = actual - pref; for (child = box->children; child; child = child->next) { twin_event_t ev; twin_coord_t stretch_this; twin_coord_t delta_this; twin_rect_t extents; if (!child->next) delta_this = delta_remain; else { if (box->dir == TwinBoxHorz) stretch_this = child->preferred.stretch_width; else stretch_this = child->preferred.stretch_height; delta_this = delta * stretch_this / stretch; } if (delta_remain < 0) { if (delta_this < delta_remain) delta_this = delta_remain; } else { if (delta_this > delta_remain) delta_this = delta_remain; } delta_remain -= delta_this; if (box->dir == TwinBoxHorz) { twin_coord_t child_w = child->preferred.width; extents.top = 0; extents.bottom = height; extents.left = pos; pos = extents.right = pos + child_w + delta_this; } else { twin_coord_t child_h = child->preferred.height; extents.left = 0; extents.right = width; extents.top = pos; pos = extents.bottom = pos + child_h + delta_this; } if (extents.left != ev.u.configure.extents.left || extents.top != ev.u.configure.extents.top || extents.right != ev.u.configure.extents.right || extents.bottom != ev.u.configure.extents.bottom) { ev.kind = TwinEventConfigure; ev.u.configure.extents = extents; (*child->dispatch) (child, &ev); } } return TwinDispatchContinue; } static twin_widget_t * _twin_box_xy_to_widget (twin_box_t *box, twin_coord_t x, twin_coord_t y) { twin_widget_t *widget; for (widget = box->children; widget; widget = widget->next) { if (widget->extents.left <= x && x < widget->extents.right && widget->extents.top <= y && y < widget->extents.bottom) return widget; } return NULL; } twin_dispatch_result_t _twin_box_dispatch (twin_widget_t *widget, twin_event_t *event) { twin_box_t *box = (twin_box_t *) widget; twin_event_t ev; twin_widget_t *child; if (event->kind != TwinEventPaint && _twin_widget_dispatch (widget, event) == TwinDispatchDone) return TwinDispatchDone; switch (event->kind) { case TwinEventQueryGeometry: return _twin_box_query_geometry (box); case TwinEventConfigure: return _twin_box_configure (box); case TwinEventButtonDown: box->button_down = _twin_box_xy_to_widget (box, event->u.pointer.x, event->u.pointer.y); if (box->button_down && box->button_down->want_focus) box->focus = box->button_down; /* fall through ... */ case TwinEventButtonUp: case TwinEventMotion: if (box->button_down) { child = box->button_down; ev = *event; ev.u.pointer.x -= child->extents.left; ev.u.pointer.y -= child->extents.top; return (*box->button_down->dispatch) (child, &ev); } break; case TwinEventKeyDown: case TwinEventKeyUp: case TwinEventUcs4: if (box->focus) return (*box->focus->dispatch) (box->focus, event); break; case TwinEventPaint: box->widget.paint = TWIN_FALSE; for (child = box->children; child; child = child->next) if (child->paint) { twin_pixmap_t *pixmap = box->widget.window->pixmap; twin_rect_t clip = twin_pixmap_save_clip (pixmap); twin_coord_t ox, oy; twin_pixmap_get_origin (pixmap, &ox, &oy); if (child->shape != TwinShapeRectangle) twin_fill (child->window->pixmap, widget->background, TWIN_SOURCE, child->extents.left, child->extents.top, child->extents.right, child->extents.bottom); twin_pixmap_set_clip (pixmap, child->extents); twin_pixmap_origin_to_clip (pixmap); child->paint = TWIN_FALSE; (*child->dispatch) (child, event); twin_pixmap_restore_clip (pixmap, clip); twin_pixmap_set_origin (pixmap, ox, oy); } break; default: break; } return TwinDispatchContinue; } twin_box_t * twin_box_create (twin_box_t *parent, twin_box_dir_t dir) { twin_box_t *box = malloc (sizeof (twin_box_t)); if (!box) return 0; _twin_box_init (box, parent, 0, dir, _twin_box_dispatch); return box; }