| 1 /* |
|
| 2 * GNT - The GLib Ncurses Toolkit |
|
| 3 * |
|
| 4 * GNT is the legal property of its developers, whose names are too numerous |
|
| 5 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 6 * source distribution. |
|
| 7 * |
|
| 8 * This library is free software; you can redistribute it and/or modify |
|
| 9 * it under the terms of the GNU General Public License as published by |
|
| 10 * the Free Software Foundation; either version 2 of the License, or |
|
| 11 * (at your option) any later version. |
|
| 12 * |
|
| 13 * This program is distributed in the hope that it will be useful, |
|
| 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 * GNU General Public License for more details. |
|
| 17 * |
|
| 18 * You should have received a copy of the GNU General Public License |
|
| 19 * along with this program; if not, write to the Free Software |
|
| 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
|
| 21 */ |
|
| 22 |
|
| 23 #include "gntinternal.h" |
|
| 24 #include "gntbox.h" |
|
| 25 #include "gntstyle.h" |
|
| 26 #include "gntutils.h" |
|
| 27 |
|
| 28 #include <string.h> |
|
| 29 |
|
| 30 #define PROP_LAST_RESIZE_S "last-resize" |
|
| 31 #define PROP_SIZE_QUEUED_S "size-queued" |
|
| 32 |
|
| 33 enum |
|
| 34 { |
|
| 35 PROP_0, |
|
| 36 PROP_VERTICAL, |
|
| 37 PROP_HOMO /* ... */ |
|
| 38 }; |
|
| 39 |
|
| 40 enum |
|
| 41 { |
|
| 42 SIGS = 1, |
|
| 43 }; |
|
| 44 |
|
| 45 static GntWidgetClass *parent_class = NULL; |
|
| 46 |
|
| 47 static GntWidget * find_focusable_widget(GntBox *box); |
|
| 48 |
|
| 49 static void |
|
| 50 add_to_focus(gpointer value, gpointer data) |
|
| 51 { |
|
| 52 GntBox *box = GNT_BOX(data); |
|
| 53 GntWidget *w = GNT_WIDGET(value); |
|
| 54 |
|
| 55 if (GNT_IS_BOX(w)) |
|
| 56 g_list_foreach(GNT_BOX(w)->list, add_to_focus, box); |
|
| 57 else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_CAN_TAKE_FOCUS)) |
|
| 58 box->focus = g_list_append(box->focus, w); |
|
| 59 } |
|
| 60 |
|
| 61 static void |
|
| 62 get_title_thingies(GntBox *box, char *title, int *p, int *r) |
|
| 63 { |
|
| 64 GntWidget *widget = GNT_WIDGET(box); |
|
| 65 int len; |
|
| 66 char *end = (char*)gnt_util_onscreen_width_to_pointer(title, widget->priv.width - 4, &len); |
|
| 67 |
|
| 68 if (p) |
|
| 69 *p = (widget->priv.width - len) / 2; |
|
| 70 if (r) |
|
| 71 *r = (widget->priv.width + len) / 2; |
|
| 72 *end = '\0'; |
|
| 73 } |
|
| 74 |
|
| 75 static void |
|
| 76 gnt_box_draw(GntWidget *widget) |
|
| 77 { |
|
| 78 GntBox *box = GNT_BOX(widget); |
|
| 79 |
|
| 80 if (box->focus == NULL && widget->parent == NULL) |
|
| 81 g_list_foreach(box->list, add_to_focus, box); |
|
| 82 |
|
| 83 g_list_foreach(box->list, (GFunc)gnt_widget_draw, NULL); |
|
| 84 |
|
| 85 if (box->title && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) |
|
| 86 { |
|
| 87 int pos, right; |
|
| 88 char *title = g_strdup(box->title); |
|
| 89 |
|
| 90 get_title_thingies(box, title, &pos, &right); |
|
| 91 |
|
| 92 if (gnt_widget_has_focus(widget)) |
|
| 93 wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TITLE)); |
|
| 94 else |
|
| 95 wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TITLE_D)); |
|
| 96 mvwaddch(widget->window, 0, pos-1, ACS_RTEE | gnt_color_pair(GNT_COLOR_NORMAL)); |
|
| 97 mvwaddstr(widget->window, 0, pos, C_(title)); |
|
| 98 mvwaddch(widget->window, 0, right, ACS_LTEE | gnt_color_pair(GNT_COLOR_NORMAL)); |
|
| 99 g_free(title); |
|
| 100 } |
|
| 101 |
|
| 102 gnt_box_sync_children(box); |
|
| 103 } |
|
| 104 |
|
| 105 static void |
|
| 106 reposition_children(GntWidget *widget) |
|
| 107 { |
|
| 108 GList *iter; |
|
| 109 GntBox *box = GNT_BOX(widget); |
|
| 110 int w, h, curx, cury, max; |
|
| 111 gboolean has_border = FALSE; |
|
| 112 |
|
| 113 w = h = 0; |
|
| 114 max = 0; |
|
| 115 curx = widget->priv.x; |
|
| 116 cury = widget->priv.y; |
|
| 117 if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_NO_BORDER)) |
|
| 118 { |
|
| 119 has_border = TRUE; |
|
| 120 curx += 1; |
|
| 121 cury += 1; |
|
| 122 } |
|
| 123 |
|
| 124 for (iter = box->list; iter; iter = iter->next) |
|
| 125 { |
|
| 126 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(iter->data), GNT_WIDGET_INVISIBLE)) |
|
| 127 continue; |
|
| 128 gnt_widget_set_position(GNT_WIDGET(iter->data), curx, cury); |
|
| 129 gnt_widget_get_size(GNT_WIDGET(iter->data), &w, &h); |
|
| 130 if (box->vertical) |
|
| 131 { |
|
| 132 if (h) |
|
| 133 { |
|
| 134 cury += h + box->pad; |
|
| 135 if (max < w) |
|
| 136 max = w; |
|
| 137 } |
|
| 138 } |
|
| 139 else |
|
| 140 { |
|
| 141 if (w) |
|
| 142 { |
|
| 143 curx += w + box->pad; |
|
| 144 if (max < h) |
|
| 145 max = h; |
|
| 146 } |
|
| 147 } |
|
| 148 } |
|
| 149 |
|
| 150 if (has_border) |
|
| 151 { |
|
| 152 curx += 1; |
|
| 153 cury += 1; |
|
| 154 max += 2; |
|
| 155 } |
|
| 156 |
|
| 157 if (box->list) |
|
| 158 { |
|
| 159 if (box->vertical) |
|
| 160 cury -= box->pad; |
|
| 161 else |
|
| 162 curx -= box->pad; |
|
| 163 } |
|
| 164 |
|
| 165 if (box->vertical) |
|
| 166 { |
|
| 167 widget->priv.width = max; |
|
| 168 widget->priv.height = cury - widget->priv.y; |
|
| 169 } |
|
| 170 else |
|
| 171 { |
|
| 172 widget->priv.width = curx - widget->priv.x; |
|
| 173 widget->priv.height = max; |
|
| 174 } |
|
| 175 } |
|
| 176 |
|
| 177 static void |
|
| 178 gnt_box_set_position(GntWidget *widget, int x, int y) |
|
| 179 { |
|
| 180 GList *iter; |
|
| 181 int changex, changey; |
|
| 182 |
|
| 183 changex = widget->priv.x - x; |
|
| 184 changey = widget->priv.y - y; |
|
| 185 |
|
| 186 for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) |
|
| 187 { |
|
| 188 GntWidget *w = GNT_WIDGET(iter->data); |
|
| 189 gnt_widget_set_position(w, w->priv.x - changex, |
|
| 190 w->priv.y - changey); |
|
| 191 } |
|
| 192 } |
|
| 193 |
|
| 194 static void |
|
| 195 gnt_box_size_request(GntWidget *widget) |
|
| 196 { |
|
| 197 GntBox *box = GNT_BOX(widget); |
|
| 198 GList *iter; |
|
| 199 int maxw = 0, maxh = 0; |
|
| 200 |
|
| 201 g_list_foreach(box->list, (GFunc)gnt_widget_size_request, NULL); |
|
| 202 |
|
| 203 for (iter = box->list; iter; iter = iter->next) |
|
| 204 { |
|
| 205 int w, h; |
|
| 206 gnt_widget_get_size(GNT_WIDGET(iter->data), &w, &h); |
|
| 207 if (maxh < h) |
|
| 208 maxh = h; |
|
| 209 if (maxw < w) |
|
| 210 maxw = w; |
|
| 211 } |
|
| 212 |
|
| 213 for (iter = box->list; iter; iter = iter->next) |
|
| 214 { |
|
| 215 int w, h; |
|
| 216 GntWidget *wid = GNT_WIDGET(iter->data); |
|
| 217 |
|
| 218 gnt_widget_get_size(wid, &w, &h); |
|
| 219 |
|
| 220 if (box->homogeneous) |
|
| 221 { |
|
| 222 if (box->vertical) |
|
| 223 h = maxh; |
|
| 224 else |
|
| 225 w = maxw; |
|
| 226 } |
|
| 227 if (box->fill) |
|
| 228 { |
|
| 229 if (box->vertical) |
|
| 230 w = maxw; |
|
| 231 else |
|
| 232 h = maxh; |
|
| 233 } |
|
| 234 |
|
| 235 if (gnt_widget_confirm_size(wid, w, h)) |
|
| 236 gnt_widget_set_size(wid, w, h); |
|
| 237 } |
|
| 238 |
|
| 239 reposition_children(widget); |
|
| 240 } |
|
| 241 |
|
| 242 static void |
|
| 243 gnt_box_map(GntWidget *widget) |
|
| 244 { |
|
| 245 if (widget->priv.width == 0 || widget->priv.height == 0) |
|
| 246 { |
|
| 247 gnt_widget_size_request(widget); |
|
| 248 find_focusable_widget(GNT_BOX(widget)); |
|
| 249 } |
|
| 250 GNTDEBUG; |
|
| 251 } |
|
| 252 |
|
| 253 /* Ensures that the current widget can take focus */ |
|
| 254 static GntWidget * |
|
| 255 find_focusable_widget(GntBox *box) |
|
| 256 { |
|
| 257 /* XXX: Make sure the widget is visible? */ |
|
| 258 if (box->focus == NULL && GNT_WIDGET(box)->parent == NULL) |
|
| 259 g_list_foreach(box->list, add_to_focus, box); |
|
| 260 |
|
| 261 if (box->active == NULL && box->focus) |
|
| 262 box->active = box->focus->data; |
|
| 263 |
|
| 264 return box->active; |
|
| 265 } |
|
| 266 |
|
| 267 static void |
|
| 268 find_next_focus(GntBox *box) |
|
| 269 { |
|
| 270 gpointer last = box->active; |
|
| 271 do |
|
| 272 { |
|
| 273 GList *iter = g_list_find(box->focus, box->active); |
|
| 274 if (iter && iter->next) |
|
| 275 box->active = iter->next->data; |
|
| 276 else if (box->focus) |
|
| 277 box->active = box->focus->data; |
|
| 278 if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE) && |
|
| 279 GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_CAN_TAKE_FOCUS)) |
|
| 280 break; |
|
| 281 } while (box->active != last); |
|
| 282 } |
|
| 283 |
|
| 284 static void |
|
| 285 find_prev_focus(GntBox *box) |
|
| 286 { |
|
| 287 gpointer last = box->active; |
|
| 288 |
|
| 289 if (!box->focus) |
|
| 290 return; |
|
| 291 |
|
| 292 do |
|
| 293 { |
|
| 294 GList *iter = g_list_find(box->focus, box->active); |
|
| 295 if (!iter) |
|
| 296 box->active = box->focus->data; |
|
| 297 else if (!iter->prev) |
|
| 298 box->active = g_list_last(box->focus)->data; |
|
| 299 else |
|
| 300 box->active = iter->prev->data; |
|
| 301 if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE)) |
|
| 302 break; |
|
| 303 } while (box->active != last); |
|
| 304 } |
|
| 305 |
|
| 306 static gboolean |
|
| 307 gnt_box_key_pressed(GntWidget *widget, const char *text) |
|
| 308 { |
|
| 309 GntBox *box = GNT_BOX(widget); |
|
| 310 gboolean ret; |
|
| 311 |
|
| 312 if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_DISABLE_ACTIONS)) |
|
| 313 return FALSE; |
|
| 314 |
|
| 315 if (box->active == NULL && !find_focusable_widget(box)) |
|
| 316 return FALSE; |
|
| 317 |
|
| 318 if (gnt_widget_key_pressed(box->active, text)) |
|
| 319 return TRUE; |
|
| 320 |
|
| 321 /* This dance is necessary to make sure that the child widgets get a chance |
|
| 322 to trigger their bindings first */ |
|
| 323 GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS); |
|
| 324 ret = gnt_widget_key_pressed(widget, text); |
|
| 325 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS); |
|
| 326 return ret; |
|
| 327 } |
|
| 328 |
|
| 329 static gboolean |
|
| 330 box_focus_change(GntBox *box, gboolean next) |
|
| 331 { |
|
| 332 GntWidget *now; |
|
| 333 now = box->active; |
|
| 334 |
|
| 335 if (next) { |
|
| 336 find_next_focus(box); |
|
| 337 } else { |
|
| 338 find_prev_focus(box); |
|
| 339 } |
|
| 340 |
|
| 341 if (now && now != box->active) { |
|
| 342 gnt_widget_set_focus(now, FALSE); |
|
| 343 gnt_widget_set_focus(box->active, TRUE); |
|
| 344 return TRUE; |
|
| 345 } |
|
| 346 |
|
| 347 return FALSE; |
|
| 348 } |
|
| 349 |
|
| 350 static gboolean |
|
| 351 action_focus_next(GntBindable *bindable, GList *null) |
|
| 352 { |
|
| 353 return box_focus_change(GNT_BOX(bindable), TRUE); |
|
| 354 } |
|
| 355 |
|
| 356 static gboolean |
|
| 357 action_focus_prev(GntBindable *bindable, GList *null) |
|
| 358 { |
|
| 359 return box_focus_change(GNT_BOX(bindable), FALSE); |
|
| 360 } |
|
| 361 |
|
| 362 static void |
|
| 363 gnt_box_lost_focus(GntWidget *widget) |
|
| 364 { |
|
| 365 GntWidget *w = GNT_BOX(widget)->active; |
|
| 366 if (w) |
|
| 367 gnt_widget_set_focus(w, FALSE); |
|
| 368 gnt_widget_draw(widget); |
|
| 369 } |
|
| 370 |
|
| 371 static void |
|
| 372 gnt_box_gained_focus(GntWidget *widget) |
|
| 373 { |
|
| 374 GntWidget *w = GNT_BOX(widget)->active; |
|
| 375 if (w) |
|
| 376 gnt_widget_set_focus(w, TRUE); |
|
| 377 gnt_widget_draw(widget); |
|
| 378 } |
|
| 379 |
|
| 380 static void |
|
| 381 gnt_box_destroy(GntWidget *w) |
|
| 382 { |
|
| 383 GntBox *box = GNT_BOX(w); |
|
| 384 |
|
| 385 gnt_box_remove_all(box); |
|
| 386 gnt_screen_release(w); |
|
| 387 } |
|
| 388 |
|
| 389 static void |
|
| 390 gnt_box_expose(GntWidget *widget, int x, int y, int width, int height) |
|
| 391 { |
|
| 392 WINDOW *win = newwin(height, width, widget->priv.y + y, widget->priv.x + x); |
|
| 393 copywin(widget->window, win, y, x, 0, 0, height - 1, width - 1, FALSE); |
|
| 394 wrefresh(win); |
|
| 395 delwin(win); |
|
| 396 } |
|
| 397 |
|
| 398 static gboolean |
|
| 399 gnt_box_confirm_size(GntWidget *widget, int width, int height) |
|
| 400 { |
|
| 401 GList *iter; |
|
| 402 GntBox *box = GNT_BOX(widget); |
|
| 403 int wchange, hchange; |
|
| 404 GntWidget *child, *last; |
|
| 405 |
|
| 406 if (!box->list) |
|
| 407 return TRUE; |
|
| 408 |
|
| 409 wchange = widget->priv.width - width; |
|
| 410 hchange = widget->priv.height - height; |
|
| 411 |
|
| 412 if (wchange == 0 && hchange == 0) |
|
| 413 return TRUE; /* Quit playing games with my size */ |
|
| 414 |
|
| 415 child = NULL; |
|
| 416 last = g_object_get_data(G_OBJECT(box), PROP_LAST_RESIZE_S); |
|
| 417 |
|
| 418 /* First, make sure all the widgets will fit into the box after resizing. */ |
|
| 419 for (iter = box->list; iter; iter = iter->next) { |
|
| 420 GntWidget *wid = iter->data; |
|
| 421 int w, h; |
|
| 422 |
|
| 423 gnt_widget_get_size(wid, &w, &h); |
|
| 424 |
|
| 425 if (wid != last && !child && w > 0 && h > 0 && |
|
| 426 !GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_INVISIBLE) && |
|
| 427 gnt_widget_confirm_size(wid, w - wchange, h - hchange)) { |
|
| 428 child = wid; |
|
| 429 break; |
|
| 430 } |
|
| 431 } |
|
| 432 |
|
| 433 if (!child && (child = last)) { |
|
| 434 int w, h; |
|
| 435 gnt_widget_get_size(child, &w, &h); |
|
| 436 if (!gnt_widget_confirm_size(child, w - wchange, h - hchange)) |
|
| 437 child = NULL; |
|
| 438 } |
|
| 439 |
|
| 440 g_object_set_data(G_OBJECT(box), PROP_SIZE_QUEUED_S, child); |
|
| 441 |
|
| 442 if (child) { |
|
| 443 for (iter = box->list; iter; iter = iter->next) { |
|
| 444 GntWidget *wid = iter->data; |
|
| 445 int w, h; |
|
| 446 |
|
| 447 if (wid == child) |
|
| 448 continue; |
|
| 449 |
|
| 450 gnt_widget_get_size(wid, &w, &h); |
|
| 451 if (box->vertical) { |
|
| 452 /* For a vertical box, if we are changing the width, make sure the widgets |
|
| 453 * in the box will fit after resizing the width. */ |
|
| 454 if (wchange > 0 && |
|
| 455 w >= child->priv.width && |
|
| 456 !gnt_widget_confirm_size(wid, w - wchange, h)) |
|
| 457 return FALSE; |
|
| 458 } else { |
|
| 459 /* If we are changing the height, make sure the widgets in the box fit after |
|
| 460 * the resize. */ |
|
| 461 if (hchange > 0 && |
|
| 462 h >= child->priv.height && |
|
| 463 !gnt_widget_confirm_size(wid, w, h - hchange)) |
|
| 464 return FALSE; |
|
| 465 } |
|
| 466 |
|
| 467 } |
|
| 468 } |
|
| 469 |
|
| 470 return (child != NULL); |
|
| 471 } |
|
| 472 |
|
| 473 static void |
|
| 474 gnt_box_size_changed(GntWidget *widget, int oldw, int oldh) |
|
| 475 { |
|
| 476 int wchange, hchange; |
|
| 477 GList *i; |
|
| 478 GntBox *box = GNT_BOX(widget); |
|
| 479 GntWidget *wid; |
|
| 480 int tw, th; |
|
| 481 |
|
| 482 wchange = widget->priv.width - oldw; |
|
| 483 hchange = widget->priv.height - oldh; |
|
| 484 |
|
| 485 wid = g_object_get_data(G_OBJECT(box), PROP_SIZE_QUEUED_S); |
|
| 486 if (wid) { |
|
| 487 gnt_widget_get_size(wid, &tw, &th); |
|
| 488 gnt_widget_set_size(wid, tw + wchange, th + hchange); |
|
| 489 g_object_set_data(G_OBJECT(box), PROP_SIZE_QUEUED_S, NULL); |
|
| 490 g_object_set_data(G_OBJECT(box), PROP_LAST_RESIZE_S, wid); |
|
| 491 } |
|
| 492 |
|
| 493 if (box->vertical) |
|
| 494 hchange = 0; |
|
| 495 else |
|
| 496 wchange = 0; |
|
| 497 |
|
| 498 for (i = box->list; i; i = i->next) |
|
| 499 { |
|
| 500 if (wid != i->data) |
|
| 501 { |
|
| 502 gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th); |
|
| 503 gnt_widget_set_size(i->data, tw + wchange, th + hchange); |
|
| 504 } |
|
| 505 } |
|
| 506 |
|
| 507 reposition_children(widget); |
|
| 508 } |
|
| 509 |
|
| 510 static gboolean |
|
| 511 gnt_box_clicked(GntWidget *widget, GntMouseEvent event, int cx, int cy) |
|
| 512 { |
|
| 513 GList *iter; |
|
| 514 for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) { |
|
| 515 int x, y, w, h; |
|
| 516 GntWidget *wid = iter->data; |
|
| 517 |
|
| 518 gnt_widget_get_position(wid, &x, &y); |
|
| 519 gnt_widget_get_size(wid, &w, &h); |
|
| 520 |
|
| 521 if (cx >= x && cx < x + w && cy >= y && cy < y + h) { |
|
| 522 if (event <= GNT_MIDDLE_MOUSE_DOWN && |
|
| 523 GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_CAN_TAKE_FOCUS)) { |
|
| 524 while (widget->parent) |
|
| 525 widget = widget->parent; |
|
| 526 gnt_box_give_focus_to_child(GNT_BOX(widget), wid); |
|
| 527 } |
|
| 528 return gnt_widget_clicked(wid, event, cx, cy); |
|
| 529 } |
|
| 530 } |
|
| 531 return FALSE; |
|
| 532 } |
|
| 533 |
|
| 534 static void |
|
| 535 gnt_box_set_property(GObject *obj, guint prop_id, const GValue *value, |
|
| 536 GParamSpec *spec) |
|
| 537 { |
|
| 538 GntBox *box = GNT_BOX(obj); |
|
| 539 switch (prop_id) { |
|
| 540 case PROP_VERTICAL: |
|
| 541 box->vertical = g_value_get_boolean(value); |
|
| 542 break; |
|
| 543 case PROP_HOMO: |
|
| 544 box->homogeneous = g_value_get_boolean(value); |
|
| 545 break; |
|
| 546 default: |
|
| 547 g_return_if_reached(); |
|
| 548 break; |
|
| 549 } |
|
| 550 } |
|
| 551 |
|
| 552 static void |
|
| 553 gnt_box_get_property(GObject *obj, guint prop_id, GValue *value, |
|
| 554 GParamSpec *spec) |
|
| 555 { |
|
| 556 GntBox *box = GNT_BOX(obj); |
|
| 557 switch (prop_id) { |
|
| 558 case PROP_VERTICAL: |
|
| 559 g_value_set_boolean(value, box->vertical); |
|
| 560 break; |
|
| 561 case PROP_HOMO: |
|
| 562 g_value_set_boolean(value, box->homogeneous); |
|
| 563 break; |
|
| 564 default: |
|
| 565 break; |
|
| 566 } |
|
| 567 } |
|
| 568 |
|
| 569 static void |
|
| 570 gnt_box_class_init(GntBoxClass *klass) |
|
| 571 { |
|
| 572 GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); |
|
| 573 GObjectClass *gclass = G_OBJECT_CLASS(klass); |
|
| 574 parent_class = GNT_WIDGET_CLASS(klass); |
|
| 575 parent_class->destroy = gnt_box_destroy; |
|
| 576 parent_class->draw = gnt_box_draw; |
|
| 577 parent_class->expose = gnt_box_expose; |
|
| 578 parent_class->map = gnt_box_map; |
|
| 579 parent_class->size_request = gnt_box_size_request; |
|
| 580 parent_class->set_position = gnt_box_set_position; |
|
| 581 parent_class->key_pressed = gnt_box_key_pressed; |
|
| 582 parent_class->clicked = gnt_box_clicked; |
|
| 583 parent_class->lost_focus = gnt_box_lost_focus; |
|
| 584 parent_class->gained_focus = gnt_box_gained_focus; |
|
| 585 parent_class->confirm_size = gnt_box_confirm_size; |
|
| 586 parent_class->size_changed = gnt_box_size_changed; |
|
| 587 |
|
| 588 gclass->set_property = gnt_box_set_property; |
|
| 589 gclass->get_property = gnt_box_get_property; |
|
| 590 g_object_class_install_property(gclass, |
|
| 591 PROP_VERTICAL, |
|
| 592 g_param_spec_boolean("vertical", "Vertical", |
|
| 593 "Whether the child widgets in the box should be stacked vertically.", |
|
| 594 TRUE, |
|
| 595 G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS |
|
| 596 ) |
|
| 597 ); |
|
| 598 g_object_class_install_property(gclass, |
|
| 599 PROP_HOMO, |
|
| 600 g_param_spec_boolean("homogeneous", "Homogeneous", |
|
| 601 "Whether the child widgets in the box should have the same size.", |
|
| 602 TRUE, |
|
| 603 G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS |
|
| 604 ) |
|
| 605 ); |
|
| 606 |
|
| 607 gnt_bindable_class_register_action(bindable, "focus-next", action_focus_next, |
|
| 608 "\t", NULL); |
|
| 609 gnt_bindable_register_binding(bindable, "focus-next", GNT_KEY_RIGHT, NULL); |
|
| 610 gnt_bindable_class_register_action(bindable, "focus-prev", action_focus_prev, |
|
| 611 GNT_KEY_BACK_TAB, NULL); |
|
| 612 gnt_bindable_register_binding(bindable, "focus-prev", GNT_KEY_LEFT, NULL); |
|
| 613 |
|
| 614 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); |
|
| 615 } |
|
| 616 |
|
| 617 static void |
|
| 618 gnt_box_init(GTypeInstance *instance, gpointer class) |
|
| 619 { |
|
| 620 GntWidget *widget = GNT_WIDGET(instance); |
|
| 621 GntBox *box = GNT_BOX(widget); |
|
| 622 /* Initially make both the height and width resizable. |
|
| 623 * Update the flags as necessary when widgets are added to it. */ |
|
| 624 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y); |
|
| 625 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_DISABLE_ACTIONS); |
|
| 626 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); |
|
| 627 box->pad = 1; |
|
| 628 box->fill = TRUE; |
|
| 629 GNTDEBUG; |
|
| 630 } |
|
| 631 |
|
| 632 /****************************************************************************** |
|
| 633 * GntBox API |
|
| 634 *****************************************************************************/ |
|
| 635 GType |
|
| 636 gnt_box_get_type(void) |
|
| 637 { |
|
| 638 static GType type = 0; |
|
| 639 |
|
| 640 if(type == 0) |
|
| 641 { |
|
| 642 static const GTypeInfo info = { |
|
| 643 sizeof(GntBoxClass), |
|
| 644 NULL, /* base_init */ |
|
| 645 NULL, /* base_finalize */ |
|
| 646 (GClassInitFunc)gnt_box_class_init, |
|
| 647 NULL, /* class_finalize */ |
|
| 648 NULL, /* class_data */ |
|
| 649 sizeof(GntBox), |
|
| 650 0, /* n_preallocs */ |
|
| 651 gnt_box_init, /* instance_init */ |
|
| 652 NULL /* value_table */ |
|
| 653 }; |
|
| 654 |
|
| 655 type = g_type_register_static(GNT_TYPE_WIDGET, |
|
| 656 "GntBox", |
|
| 657 &info, 0); |
|
| 658 } |
|
| 659 |
|
| 660 return type; |
|
| 661 } |
|
| 662 |
|
| 663 GntWidget *gnt_box_new(gboolean homo, gboolean vert) |
|
| 664 { |
|
| 665 GntWidget *widget = g_object_new(GNT_TYPE_BOX, "homogeneous", homo, |
|
| 666 "vertical", vert, NULL); |
|
| 667 GntBox *box = GNT_BOX(widget); |
|
| 668 |
|
| 669 box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID; |
|
| 670 |
|
| 671 return widget; |
|
| 672 } |
|
| 673 |
|
| 674 void gnt_box_add_widget(GntBox *b, GntWidget *widget) |
|
| 675 { |
|
| 676 b->list = g_list_append(b->list, widget); |
|
| 677 widget->parent = GNT_WIDGET(b); |
|
| 678 } |
|
| 679 |
|
| 680 void gnt_box_add_widget_in_front(GntBox *b, GntWidget *widget) |
|
| 681 { |
|
| 682 b->list = g_list_prepend(b->list, widget); |
|
| 683 widget->parent = GNT_WIDGET(b); |
|
| 684 } |
|
| 685 |
|
| 686 void gnt_box_set_title(GntBox *b, const char *title) |
|
| 687 { |
|
| 688 char *prev = b->title; |
|
| 689 GntWidget *w = GNT_WIDGET(b); |
|
| 690 b->title = g_strdup(title); |
|
| 691 if (w->window && !GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_NO_BORDER)) { |
|
| 692 /* Erase the old title */ |
|
| 693 int pos, right; |
|
| 694 get_title_thingies(b, prev, &pos, &right); |
|
| 695 mvwhline(w->window, 0, pos - 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), |
|
| 696 right - pos + 2); |
|
| 697 } |
|
| 698 g_free(prev); |
|
| 699 } |
|
| 700 |
|
| 701 void gnt_box_set_pad(GntBox *box, int pad) |
|
| 702 { |
|
| 703 box->pad = pad; |
|
| 704 /* XXX: Perhaps redraw if already showing? */ |
|
| 705 } |
|
| 706 |
|
| 707 void gnt_box_set_toplevel(GntBox *box, gboolean set) |
|
| 708 { |
|
| 709 GntWidget *widget = GNT_WIDGET(box); |
|
| 710 if (set) |
|
| 711 { |
|
| 712 GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); |
|
| 713 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); |
|
| 714 } |
|
| 715 else |
|
| 716 { |
|
| 717 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); |
|
| 718 GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); |
|
| 719 } |
|
| 720 } |
|
| 721 |
|
| 722 void gnt_box_sync_children(GntBox *box) |
|
| 723 { |
|
| 724 GList *iter; |
|
| 725 GntWidget *widget = GNT_WIDGET(box); |
|
| 726 int pos = 1; |
|
| 727 |
|
| 728 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) |
|
| 729 pos = 0; |
|
| 730 |
|
| 731 if (!box->active) |
|
| 732 find_focusable_widget(box); |
|
| 733 |
|
| 734 for (iter = box->list; iter; iter = iter->next) |
|
| 735 { |
|
| 736 GntWidget *w = GNT_WIDGET(iter->data); |
|
| 737 int height, width; |
|
| 738 int x, y; |
|
| 739 |
|
| 740 if (G_UNLIKELY(w == NULL)) { |
|
| 741 g_warn_if_reached(); |
|
| 742 continue; |
|
| 743 } |
|
| 744 |
|
| 745 if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_INVISIBLE)) |
|
| 746 continue; |
|
| 747 |
|
| 748 if (GNT_IS_BOX(w)) |
|
| 749 gnt_box_sync_children(GNT_BOX(w)); |
|
| 750 |
|
| 751 gnt_widget_get_size(w, &width, &height); |
|
| 752 |
|
| 753 x = w->priv.x - widget->priv.x; |
|
| 754 y = w->priv.y - widget->priv.y; |
|
| 755 |
|
| 756 if (box->vertical) |
|
| 757 { |
|
| 758 x = pos; |
|
| 759 if (box->alignment == GNT_ALIGN_RIGHT) |
|
| 760 x += widget->priv.width - width; |
|
| 761 else if (box->alignment == GNT_ALIGN_MID) |
|
| 762 x += (widget->priv.width - width)/2; |
|
| 763 if (x + width > widget->priv.width - pos) |
|
| 764 x -= x + width - (widget->priv.width - pos); |
|
| 765 } |
|
| 766 else |
|
| 767 { |
|
| 768 y = pos; |
|
| 769 if (box->alignment == GNT_ALIGN_BOTTOM) |
|
| 770 y += widget->priv.height - height; |
|
| 771 else if (box->alignment == GNT_ALIGN_MID) |
|
| 772 y += (widget->priv.height - height)/2; |
|
| 773 if (y + height >= widget->priv.height - pos) |
|
| 774 y = widget->priv.height - height - pos; |
|
| 775 } |
|
| 776 |
|
| 777 copywin(w->window, widget->window, 0, 0, |
|
| 778 y, x, y + height - 1, x + width - 1, FALSE); |
|
| 779 gnt_widget_set_position(w, x + widget->priv.x, y + widget->priv.y); |
|
| 780 if (w == box->active) { |
|
| 781 wmove(widget->window, y + getcury(w->window), x + getcurx(w->window)); |
|
| 782 } |
|
| 783 } |
|
| 784 } |
|
| 785 |
|
| 786 void gnt_box_set_alignment(GntBox *box, GntAlignment alignment) |
|
| 787 { |
|
| 788 box->alignment = alignment; |
|
| 789 } |
|
| 790 |
|
| 791 void gnt_box_remove(GntBox *box, GntWidget *widget) |
|
| 792 { |
|
| 793 box->list = g_list_remove(box->list, widget); |
|
| 794 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS) |
|
| 795 && GNT_WIDGET(box)->parent == NULL && box->focus) |
|
| 796 { |
|
| 797 if (widget == box->active) |
|
| 798 { |
|
| 799 find_next_focus(box); |
|
| 800 if (box->active == widget) /* There's only one widget */ |
|
| 801 box->active = NULL; |
|
| 802 } |
|
| 803 box->focus = g_list_remove(box->focus, widget); |
|
| 804 } |
|
| 805 |
|
| 806 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(box), GNT_WIDGET_MAPPED)) |
|
| 807 gnt_widget_draw(GNT_WIDGET(box)); |
|
| 808 } |
|
| 809 |
|
| 810 void gnt_box_remove_all(GntBox *box) |
|
| 811 { |
|
| 812 g_list_foreach(box->list, (GFunc)gnt_widget_destroy, NULL); |
|
| 813 g_list_free(box->list); |
|
| 814 g_list_free(box->focus); |
|
| 815 box->list = NULL; |
|
| 816 box->focus = NULL; |
|
| 817 GNT_WIDGET(box)->priv.width = 0; |
|
| 818 GNT_WIDGET(box)->priv.height = 0; |
|
| 819 } |
|
| 820 |
|
| 821 void gnt_box_readjust(GntBox *box) |
|
| 822 { |
|
| 823 GList *iter; |
|
| 824 GntWidget *wid; |
|
| 825 int width, height; |
|
| 826 |
|
| 827 if (GNT_WIDGET(box)->parent != NULL) |
|
| 828 return; |
|
| 829 |
|
| 830 for (iter = box->list; iter; iter = iter->next) |
|
| 831 { |
|
| 832 GntWidget *w = iter->data; |
|
| 833 |
|
| 834 if (G_UNLIKELY(w == NULL)) { |
|
| 835 g_warn_if_reached(); |
|
| 836 continue; |
|
| 837 } |
|
| 838 |
|
| 839 if (GNT_IS_BOX(w)) |
|
| 840 gnt_box_readjust(GNT_BOX(w)); |
|
| 841 else |
|
| 842 { |
|
| 843 GNT_WIDGET_UNSET_FLAGS(w, GNT_WIDGET_MAPPED); |
|
| 844 w->priv.width = 0; |
|
| 845 w->priv.height = 0; |
|
| 846 } |
|
| 847 } |
|
| 848 |
|
| 849 wid = GNT_WIDGET(box); |
|
| 850 GNT_WIDGET_UNSET_FLAGS(wid, GNT_WIDGET_MAPPED); |
|
| 851 wid->priv.width = 0; |
|
| 852 wid->priv.height = 0; |
|
| 853 |
|
| 854 if (wid->parent == NULL) |
|
| 855 { |
|
| 856 g_list_free(box->focus); |
|
| 857 box->focus = NULL; |
|
| 858 box->active = NULL; |
|
| 859 gnt_widget_size_request(wid); |
|
| 860 gnt_widget_get_size(wid, &width, &height); |
|
| 861 gnt_screen_resize_widget(wid, width, height); |
|
| 862 find_focusable_widget(box); |
|
| 863 } |
|
| 864 } |
|
| 865 |
|
| 866 void gnt_box_set_fill(GntBox *box, gboolean fill) |
|
| 867 { |
|
| 868 box->fill = fill; |
|
| 869 } |
|
| 870 |
|
| 871 void gnt_box_move_focus(GntBox *box, int dir) |
|
| 872 { |
|
| 873 GntWidget *now; |
|
| 874 |
|
| 875 if (box->active == NULL) |
|
| 876 { |
|
| 877 find_focusable_widget(box); |
|
| 878 return; |
|
| 879 } |
|
| 880 |
|
| 881 now = box->active; |
|
| 882 |
|
| 883 if (dir == 1) |
|
| 884 find_next_focus(box); |
|
| 885 else if (dir == -1) |
|
| 886 find_prev_focus(box); |
|
| 887 |
|
| 888 if (now && now != box->active) |
|
| 889 { |
|
| 890 gnt_widget_set_focus(now, FALSE); |
|
| 891 gnt_widget_set_focus(box->active, TRUE); |
|
| 892 } |
|
| 893 |
|
| 894 if (GNT_WIDGET(box)->window) |
|
| 895 gnt_widget_draw(GNT_WIDGET(box)); |
|
| 896 } |
|
| 897 |
|
| 898 void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget) |
|
| 899 { |
|
| 900 GList *find; |
|
| 901 gpointer now; |
|
| 902 |
|
| 903 while (GNT_WIDGET(box)->parent) |
|
| 904 box = GNT_BOX(GNT_WIDGET(box)->parent); |
|
| 905 |
|
| 906 find = g_list_find(box->focus, widget); |
|
| 907 now = box->active; |
|
| 908 if (find) |
|
| 909 box->active = widget; |
|
| 910 if (now && now != box->active) |
|
| 911 { |
|
| 912 gnt_widget_set_focus(now, FALSE); |
|
| 913 gnt_widget_set_focus(box->active, TRUE); |
|
| 914 } |
|
| 915 |
|
| 916 if (GNT_WIDGET(box)->window) |
|
| 917 gnt_widget_draw(GNT_WIDGET(box)); |
|
| 918 } |
|
| 919 |
|