container.c 8.42 KB
Newer Older
1 2 3 4
#include "gui.h"
#include "gui_defs.h"
#include <stdio.h>
#include <stdlib.h>
5
#include <string.h>
6

7 8
#define MAX(a, b) ((a)>(b)?(a):(b))

9 10
static void repack(gui *g, widget *_this)
{
Cedric Roux's avatar
Cedric Roux committed
11
  LOGD("REPACK container %p\n", _this);
12 13 14 15 16
  struct container_widget *this = _this;
  this->hints_are_valid = 0;
  return this->common.parent->repack(g, this->common.parent);
}

17
static void add_child(gui *g, widget *_this, widget *child, int position)
18
{
Cedric Roux's avatar
Cedric Roux committed
19
  LOGD("ADD_CHILD container\n");
20
  struct container_widget *this = _this;
Cedric Roux's avatar
Cedric Roux committed
21

22
  this->hints_are_valid = 0;
23
  widget_add_child_internal(g, this, child, position);
24 25 26 27 28 29 30 31 32 33 34 35 36

  /* initially not growable */
  this->growable = realloc(this->growable, (this->nchildren+1)*sizeof(int));
  if (this->growable == NULL) abort();

  if (position == -1) position = this->nchildren;

  memmove(this->growable + position+1, this->growable + position,
          (this->nchildren - position) * sizeof(int));

  this->growable[position] = 0;

  this->nchildren++;
37 38
}

Cedric Roux's avatar
Cedric Roux committed
39 40 41 42 43 44 45 46 47 48 49 50 51
static void del_child(gui *g, widget *_this, widget *child)
{
  LOGD("DEL_CHILD container\n");
  struct container_widget *this = _this;
  int position = widget_get_child_position(g, _this, child);

  this->hints_are_valid = 0;
  widget_del_child_internal(g, this, child);

  memmove(this->growable + position, this->growable + position+1,
          (this->nchildren - position - 1) * sizeof(int));

  this->growable = realloc(this->growable, (this->nchildren-1)*sizeof(int));
52
  if (this->nchildren != 1 && this->growable == NULL) abort();
Cedric Roux's avatar
Cedric Roux committed
53 54 55 56

  this->nchildren--;
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
static void compute_vertical_hints(struct gui *g,
    struct container_widget *this)
{
  struct widget_list *l;
  int cwidth, cheight;
  int allocated_width = 0, allocated_height = 0;

  /* get largest width */
  l = this->common.children;
  while (l) {
    l->item->hints(g, l->item, &cwidth, &cheight);
    if (cwidth > allocated_width) allocated_width = cwidth;
    allocated_height += cheight;
    l = l->next;
  }
  this->hint_width = allocated_width;
  this->hint_height = allocated_height;
  this->hints_are_valid = 1;
}

static void compute_horizontal_hints(struct gui *g,
    struct container_widget *this)
{
  struct widget_list *l;
  int cwidth, cheight;
  int allocated_width = 0, allocated_height = 0;

  /* get largest height */
  l = this->common.children;
  while (l) {
    l->item->hints(g, l->item, &cwidth, &cheight);
    if (cheight > allocated_height) allocated_height = cheight;
    allocated_width += cwidth;
    l = l->next;
  }
  this->hint_width = allocated_width;
  this->hint_height = allocated_height;
  this->hints_are_valid = 1;
}

static void vertical_allocate(gui *_gui, widget *_this,
    int x, int y, int width, int height)
{
Cedric Roux's avatar
Cedric Roux committed
100
  LOGD("ALLOCATE container vertical %p\n", _this);
101 102 103 104 105
  int cy = 0;
  int cwidth, cheight;
  struct gui *g = _gui;
  struct container_widget *this = _this;
  struct widget_list *l;
106 107
  int over_pixels = 0;
  int i;
108 109 110 111 112 113 114 115 116 117 118 119

  if (this->hints_are_valid == 1) goto hints_ok;

  compute_vertical_hints(g, this);

hints_ok:

  this->common.x = x;
  this->common.y = y;
  this->common.width = width;
  this->common.height = height;

120 121 122 123 124 125 126 127
  /* TODO: some pixels won't be allocated, take care of it? */
  if (height > this->hint_height) {
    int ngrowable = 0;
    for (i = 0; i < this->nchildren; i++) if (this->growable[i]) ngrowable++;
    if (ngrowable)
      over_pixels = (height - this->hint_height) / ngrowable;
  }

128 129
  /* allocate */
  l = this->common.children;
130
  i = 0;
131
  while (l) {
132
    int allocated_height;
133
    l->item->hints(g, l->item, &cwidth, &cheight);
134
    allocated_height = cheight + (this->growable[i] ? over_pixels : 0);
135
    l->item->allocate(g, l->item, this->common.x, this->common.y + cy,
136 137
        MAX(width, cwidth), allocated_height);
    cy += allocated_height;
138
    l = l->next;
139
    i++;
140 141
  }

142
//  if (cy != this->hint_height) ERR("reachable?\n");
143 144 145 146 147
}

static void horizontal_allocate(gui *_gui, widget *_this,
    int x, int y, int width, int height)
{
Cedric Roux's avatar
Cedric Roux committed
148
  LOGD("ALLOCATE container horizontal %p\n", _this);
149 150 151 152 153
  int cx = 0;
  int cwidth, cheight;
  struct gui *g = _gui;
  struct container_widget *this = _this;
  struct widget_list *l;
154 155
  int over_pixels = 0;
  int i;
156 157 158 159 160 161 162 163 164 165 166 167

  if (this->hints_are_valid == 1) goto hints_ok;

  compute_horizontal_hints(g, this);

hints_ok:

  this->common.x = x;
  this->common.y = y;
  this->common.width = width;
  this->common.height = height;

168 169 170 171 172 173 174 175
  /* TODO: some pixels won't be allocated, take care of it? */
  if (width > this->hint_width) {
    int ngrowable = 0;
    for (i = 0; i < this->nchildren; i++) if (this->growable[i]) ngrowable++;
    if (ngrowable)
      over_pixels = (width - this->hint_width) / ngrowable;
  }

176 177
  /* allocate */
  l = this->common.children;
178
  i = 0;
179
  while (l) {
180
    int allocated_width;
181
    l->item->hints(g, l->item, &cwidth, &cheight);
182
    allocated_width = cwidth + (this->growable[i] ? over_pixels : 0);
183
    l->item->allocate(g, l->item, this->common.x + cx, this->common.y,
184 185
        allocated_width, MAX(height, cheight)/* this->hint_height */);
    cx += allocated_width;
186
    l = l->next;
187
    i++;
188 189
  }

190
//  if (cx != this->hint_width) ERR("reachable?\n");
191 192 193 194
}

static void vertical_hints(gui *_gui, widget *_w, int *width, int *height)
{
Cedric Roux's avatar
Cedric Roux committed
195
  LOGD("HINTS container vertical %p\n", _w);
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
  struct gui *g = _gui;
  struct container_widget *this = _w;

  if (this->hints_are_valid) {
    *width = this->hint_width;
    *height = this->hint_height;
    return;
  }

  compute_vertical_hints(g, this);

  *width = this->hint_width;
  *height = this->hint_height;
}

static void horizontal_hints(gui *_gui, widget *_w, int *width, int *height)
{
Cedric Roux's avatar
Cedric Roux committed
213
  LOGD("HINTS container horizontal %p\n", _w);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
  struct gui *g = _gui;
  struct container_widget *this = _w;

  if (this->hints_are_valid) {
    *width = this->hint_width;
    *height = this->hint_height;
    return;
  }

  compute_horizontal_hints(g, this);

  *width = this->hint_width;
  *height = this->hint_height;
}

Cedric Roux's avatar
Cedric Roux committed
229
static void horizontal_button(gui *_g, widget *_this, int x, int y,
230
    int key_modifiers, int button, int up)
Cedric Roux's avatar
Cedric Roux committed
231
{
Cedric Roux's avatar
Cedric Roux committed
232
  LOGD("BUTTON container horizontal %p xy %d %d button %d up %d\n", _this, x, y, button, up);
Cedric Roux's avatar
Cedric Roux committed
233 234 235 236 237 238 239
  struct gui *g = _g;
  struct container_widget *this = _this;
  struct widget_list *l;

  l = this->common.children;
  while (l) {
    if (l->item->x <= x && x < l->item->x + l->item->width) {
240
      l->item->button(g, l->item, x, y, key_modifiers, button, up);
Cedric Roux's avatar
Cedric Roux committed
241 242 243 244 245 246 247
      break;
    }
    l = l->next;
  }
}

static void vertical_button(gui *_g, widget *_this, int x, int y,
248
    int key_modifiers, int button, int up)
Cedric Roux's avatar
Cedric Roux committed
249
{
Cedric Roux's avatar
Cedric Roux committed
250
  LOGD("BUTTON container vertical %p xy %d %d button %d up %d\n", _this, x, y, button, up);
Cedric Roux's avatar
Cedric Roux committed
251 252 253 254 255 256 257
  struct gui *g = _g;
  struct container_widget *this = _this;
  struct widget_list *l;

  l = this->common.children;
  while (l) {
    if (l->item->y <= y && y < l->item->y + l->item->height) {
258
      l->item->button(g, l->item, x, y, key_modifiers, button, up);
Cedric Roux's avatar
Cedric Roux committed
259 260 261 262 263 264
      break;
    }
    l = l->next;
  }
}

265 266
static void paint(gui *_gui, widget *_this)
{
Cedric Roux's avatar
Cedric Roux committed
267
  LOGD("PAINT container\n");
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  struct gui *g = _gui;
  struct widget *this = _this;
  struct widget_list *l;

  l = this->children;
  while (l) {
    l->item->paint(g, l->item);
    l = l->next;
  }
}

widget *new_container(gui *_gui, int vertical)
{
  struct gui *g = _gui;
  struct container_widget *w;

  glock(g);

  w = new_widget(g, CONTAINER, sizeof(struct container_widget));

  w->vertical = vertical;
  w->hints_are_valid = 0;

  w->common.paint     = paint;
  w->common.add_child = add_child;
Cedric Roux's avatar
Cedric Roux committed
293
  w->common.del_child = del_child;
294 295 296 297 298
  w->common.repack    = repack;

  if (vertical) {
    w->common.allocate  = vertical_allocate;
    w->common.hints     = vertical_hints;
Cedric Roux's avatar
Cedric Roux committed
299
    w->common.button    = vertical_button;
300 301 302
  } else {
    w->common.allocate  = horizontal_allocate;
    w->common.hints     = horizontal_hints;
Cedric Roux's avatar
Cedric Roux committed
303
    w->common.button    = horizontal_button;
304 305 306 307 308 309
  }

  gunlock(g);

  return w;
}
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

/*************************************************************************/
/*                             public functions                          */
/*************************************************************************/

void container_set_child_growable(gui *_gui, widget *_this,
    widget *child, int growable)
{
  gui *g = _gui;
  struct container_widget *this = _this;
  struct widget_list *lcur;
  int i;

  glock(g);

  lcur = this->common.children;
  i = 0;
  while (lcur) {
    if (lcur->item == child) break;
    lcur = lcur->next;
    i++;
  }
  if (lcur == NULL) ERR("%s:%d: child not found\n", __FILE__, __LINE__);

  this->growable[i] = growable;

336
  send_event(g, REPACK, this->common.id);
337 338 339

  gunlock(g);
}