gtkcols.c 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. /*
  2. * gtkcols.c - implementation of the `Columns' GTK layout container.
  3. */
  4. #include <gtk/gtk.h>
  5. #include "gtkcompat.h"
  6. #include "gtkcols.h"
  7. static void columns_init(Columns *cols);
  8. static void columns_class_init(ColumnsClass *klass);
  9. static void columns_map(GtkWidget *widget);
  10. static void columns_unmap(GtkWidget *widget);
  11. #if !GTK_CHECK_VERSION(2,0,0)
  12. static void columns_draw(GtkWidget *widget, GdkRectangle *area);
  13. static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
  14. #endif
  15. static void columns_base_add(GtkContainer *container, GtkWidget *widget);
  16. static void columns_remove(GtkContainer *container, GtkWidget *widget);
  17. static void columns_forall(GtkContainer *container, gboolean include_internals,
  18. GtkCallback callback, gpointer callback_data);
  19. #if !GTK_CHECK_VERSION(2,0,0)
  20. static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
  21. #endif
  22. static GType columns_child_type(GtkContainer *container);
  23. #if GTK_CHECK_VERSION(3,0,0)
  24. static void columns_get_preferred_width(GtkWidget *widget,
  25. gint *min, gint *nat);
  26. static void columns_get_preferred_height(GtkWidget *widget,
  27. gint *min, gint *nat);
  28. static void columns_get_preferred_width_for_height(GtkWidget *widget,
  29. gint height,
  30. gint *min, gint *nat);
  31. static void columns_get_preferred_height_for_width(GtkWidget *widget,
  32. gint width,
  33. gint *min, gint *nat);
  34. #else
  35. static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
  36. #endif
  37. static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
  38. static GtkContainerClass *parent_class = NULL;
  39. #if !GTK_CHECK_VERSION(2,0,0)
  40. GType columns_get_type(void)
  41. {
  42. static GType columns_type = 0;
  43. if (!columns_type) {
  44. static const GtkTypeInfo columns_info = {
  45. "Columns",
  46. sizeof(Columns),
  47. sizeof(ColumnsClass),
  48. (GtkClassInitFunc) columns_class_init,
  49. (GtkObjectInitFunc) columns_init,
  50. /* reserved_1 */ NULL,
  51. /* reserved_2 */ NULL,
  52. (GtkClassInitFunc) NULL,
  53. };
  54. columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
  55. }
  56. return columns_type;
  57. }
  58. #else
  59. GType columns_get_type(void)
  60. {
  61. static GType columns_type = 0;
  62. if (!columns_type) {
  63. static const GTypeInfo columns_info = {
  64. sizeof(ColumnsClass),
  65. NULL,
  66. NULL,
  67. (GClassInitFunc) columns_class_init,
  68. NULL,
  69. NULL,
  70. sizeof(Columns),
  71. 0,
  72. (GInstanceInitFunc)columns_init,
  73. };
  74. columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
  75. &columns_info, 0);
  76. }
  77. return columns_type;
  78. }
  79. #endif
  80. #if !GTK_CHECK_VERSION(2,0,0)
  81. static gint (*columns_inherited_focus)(GtkContainer *container,
  82. GtkDirectionType direction);
  83. #endif
  84. static void columns_class_init(ColumnsClass *klass)
  85. {
  86. #if !GTK_CHECK_VERSION(2,0,0)
  87. /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
  88. GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
  89. GtkContainerClass *container_class = (GtkContainerClass *)klass;
  90. #else
  91. /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
  92. GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
  93. GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
  94. #endif
  95. #if !GTK_CHECK_VERSION(2,0,0)
  96. parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
  97. #else
  98. parent_class = g_type_class_peek_parent(klass);
  99. #endif
  100. widget_class->map = columns_map;
  101. widget_class->unmap = columns_unmap;
  102. #if !GTK_CHECK_VERSION(2,0,0)
  103. widget_class->draw = columns_draw;
  104. widget_class->expose_event = columns_expose;
  105. #endif
  106. #if GTK_CHECK_VERSION(3,0,0)
  107. widget_class->get_preferred_width = columns_get_preferred_width;
  108. widget_class->get_preferred_height = columns_get_preferred_height;
  109. widget_class->get_preferred_width_for_height =
  110. columns_get_preferred_width_for_height;
  111. widget_class->get_preferred_height_for_width =
  112. columns_get_preferred_height_for_width;
  113. #else
  114. widget_class->size_request = columns_size_request;
  115. #endif
  116. widget_class->size_allocate = columns_size_allocate;
  117. container_class->add = columns_base_add;
  118. container_class->remove = columns_remove;
  119. container_class->forall = columns_forall;
  120. container_class->child_type = columns_child_type;
  121. #if !GTK_CHECK_VERSION(2,0,0)
  122. /* Save the previous value of this method. */
  123. if (!columns_inherited_focus)
  124. columns_inherited_focus = container_class->focus;
  125. container_class->focus = columns_focus;
  126. #endif
  127. }
  128. static void columns_init(Columns *cols)
  129. {
  130. gtk_widget_set_has_window(GTK_WIDGET(cols), FALSE);
  131. cols->children = NULL;
  132. cols->spacing = 0;
  133. }
  134. /*
  135. * These appear to be thoroughly tedious functions; the only reason
  136. * we have to reimplement them at all is because we defined our own
  137. * format for our GList of children...
  138. */
  139. static void columns_map(GtkWidget *widget)
  140. {
  141. Columns *cols;
  142. ColumnsChild *child;
  143. GList *children;
  144. g_return_if_fail(widget != NULL);
  145. g_return_if_fail(IS_COLUMNS(widget));
  146. cols = COLUMNS(widget);
  147. gtk_widget_set_mapped(GTK_WIDGET(cols), TRUE);
  148. for (children = cols->children;
  149. children && (child = children->data);
  150. children = children->next) {
  151. if (child->widget &&
  152. gtk_widget_get_visible(child->widget) &&
  153. !gtk_widget_get_mapped(child->widget))
  154. gtk_widget_map(child->widget);
  155. }
  156. }
  157. static void columns_unmap(GtkWidget *widget)
  158. {
  159. Columns *cols;
  160. ColumnsChild *child;
  161. GList *children;
  162. g_return_if_fail(widget != NULL);
  163. g_return_if_fail(IS_COLUMNS(widget));
  164. cols = COLUMNS(widget);
  165. gtk_widget_set_mapped(GTK_WIDGET(cols), FALSE);
  166. for (children = cols->children;
  167. children && (child = children->data);
  168. children = children->next) {
  169. if (child->widget &&
  170. gtk_widget_get_visible(child->widget) &&
  171. gtk_widget_get_mapped(child->widget))
  172. gtk_widget_unmap(child->widget);
  173. }
  174. }
  175. #if !GTK_CHECK_VERSION(2,0,0)
  176. static void columns_draw(GtkWidget *widget, GdkRectangle *area)
  177. {
  178. Columns *cols;
  179. ColumnsChild *child;
  180. GList *children;
  181. GdkRectangle child_area;
  182. g_return_if_fail(widget != NULL);
  183. g_return_if_fail(IS_COLUMNS(widget));
  184. if (GTK_WIDGET_DRAWABLE(widget)) {
  185. cols = COLUMNS(widget);
  186. for (children = cols->children;
  187. children && (child = children->data);
  188. children = children->next) {
  189. if (child->widget &&
  190. GTK_WIDGET_DRAWABLE(child->widget) &&
  191. gtk_widget_intersect(child->widget, area, &child_area))
  192. gtk_widget_draw(child->widget, &child_area);
  193. }
  194. }
  195. }
  196. static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
  197. {
  198. Columns *cols;
  199. ColumnsChild *child;
  200. GList *children;
  201. GdkEventExpose child_event;
  202. g_return_val_if_fail(widget != NULL, FALSE);
  203. g_return_val_if_fail(IS_COLUMNS(widget), FALSE);
  204. g_return_val_if_fail(event != NULL, FALSE);
  205. if (GTK_WIDGET_DRAWABLE(widget)) {
  206. cols = COLUMNS(widget);
  207. child_event = *event;
  208. for (children = cols->children;
  209. children && (child = children->data);
  210. children = children->next) {
  211. if (child->widget &&
  212. GTK_WIDGET_DRAWABLE(child->widget) &&
  213. GTK_WIDGET_NO_WINDOW(child->widget) &&
  214. gtk_widget_intersect(child->widget, &event->area,
  215. &child_event.area))
  216. gtk_widget_event(child->widget, (GdkEvent *)&child_event);
  217. }
  218. }
  219. return FALSE;
  220. }
  221. #endif
  222. static void columns_base_add(GtkContainer *container, GtkWidget *widget)
  223. {
  224. Columns *cols;
  225. g_return_if_fail(container != NULL);
  226. g_return_if_fail(IS_COLUMNS(container));
  227. g_return_if_fail(widget != NULL);
  228. cols = COLUMNS(container);
  229. /*
  230. * Default is to add a new widget spanning all columns.
  231. */
  232. columns_add(cols, widget, 0, 0); /* 0 means ncols */
  233. }
  234. static void columns_remove(GtkContainer *container, GtkWidget *widget)
  235. {
  236. Columns *cols;
  237. ColumnsChild *child;
  238. GtkWidget *childw;
  239. GList *children;
  240. gboolean was_visible;
  241. g_return_if_fail(container != NULL);
  242. g_return_if_fail(IS_COLUMNS(container));
  243. g_return_if_fail(widget != NULL);
  244. cols = COLUMNS(container);
  245. for (children = cols->children;
  246. children && (child = children->data);
  247. children = children->next) {
  248. if (child->widget != widget)
  249. continue;
  250. was_visible = gtk_widget_get_visible(widget);
  251. gtk_widget_unparent(widget);
  252. cols->children = g_list_remove_link(cols->children, children);
  253. g_list_free(children);
  254. if (child->same_height_as) {
  255. g_return_if_fail(child->same_height_as->same_height_as == child);
  256. child->same_height_as->same_height_as = NULL;
  257. if (gtk_widget_get_visible(child->same_height_as->widget))
  258. gtk_widget_queue_resize(GTK_WIDGET(container));
  259. }
  260. g_free(child);
  261. if (was_visible)
  262. gtk_widget_queue_resize(GTK_WIDGET(container));
  263. break;
  264. }
  265. for (children = cols->taborder;
  266. children && (childw = children->data);
  267. children = children->next) {
  268. if (childw != widget)
  269. continue;
  270. cols->taborder = g_list_remove_link(cols->taborder, children);
  271. g_list_free(children);
  272. #if GTK_CHECK_VERSION(2,0,0)
  273. gtk_container_set_focus_chain(container, cols->taborder);
  274. #endif
  275. break;
  276. }
  277. }
  278. static void columns_forall(GtkContainer *container, gboolean include_internals,
  279. GtkCallback callback, gpointer callback_data)
  280. {
  281. Columns *cols;
  282. ColumnsChild *child;
  283. GList *children, *next;
  284. g_return_if_fail(container != NULL);
  285. g_return_if_fail(IS_COLUMNS(container));
  286. g_return_if_fail(callback != NULL);
  287. cols = COLUMNS(container);
  288. for (children = cols->children;
  289. children && (child = children->data);
  290. children = next) {
  291. /*
  292. * We can't wait until after the callback to assign
  293. * `children = children->next', because the callback might
  294. * be gtk_widget_destroy, which would remove the link
  295. * `children' from the list! So instead we must get our
  296. * hands on the value of the `next' pointer _before_ the
  297. * callback.
  298. */
  299. next = children->next;
  300. if (child->widget)
  301. callback(child->widget, callback_data);
  302. }
  303. }
  304. static GType columns_child_type(GtkContainer *container)
  305. {
  306. return GTK_TYPE_WIDGET;
  307. }
  308. GtkWidget *columns_new(gint spacing)
  309. {
  310. Columns *cols;
  311. #if !GTK_CHECK_VERSION(2,0,0)
  312. cols = gtk_type_new(columns_get_type());
  313. #else
  314. cols = g_object_new(TYPE_COLUMNS, NULL);
  315. #endif
  316. cols->spacing = spacing;
  317. return GTK_WIDGET(cols);
  318. }
  319. void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
  320. {
  321. ColumnsChild *childdata;
  322. gint i;
  323. g_return_if_fail(cols != NULL);
  324. g_return_if_fail(IS_COLUMNS(cols));
  325. g_return_if_fail(ncols > 0);
  326. g_return_if_fail(percentages != NULL);
  327. childdata = g_new(ColumnsChild, 1);
  328. childdata->widget = NULL;
  329. childdata->ncols = ncols;
  330. childdata->percentages = g_new(gint, ncols);
  331. childdata->force_left = FALSE;
  332. for (i = 0; i < ncols; i++)
  333. childdata->percentages[i] = percentages[i];
  334. cols->children = g_list_append(cols->children, childdata);
  335. }
  336. void columns_add(Columns *cols, GtkWidget *child,
  337. gint colstart, gint colspan)
  338. {
  339. ColumnsChild *childdata;
  340. g_return_if_fail(cols != NULL);
  341. g_return_if_fail(IS_COLUMNS(cols));
  342. g_return_if_fail(child != NULL);
  343. g_return_if_fail(gtk_widget_get_parent(child) == NULL);
  344. childdata = g_new(ColumnsChild, 1);
  345. childdata->widget = child;
  346. childdata->colstart = colstart;
  347. childdata->colspan = colspan;
  348. childdata->force_left = FALSE;
  349. childdata->same_height_as = NULL;
  350. cols->children = g_list_append(cols->children, childdata);
  351. cols->taborder = g_list_append(cols->taborder, child);
  352. gtk_widget_set_parent(child, GTK_WIDGET(cols));
  353. #if GTK_CHECK_VERSION(2,0,0)
  354. gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
  355. #endif
  356. if (gtk_widget_get_realized(GTK_WIDGET(cols)))
  357. gtk_widget_realize(child);
  358. if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
  359. gtk_widget_get_visible(child)) {
  360. if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
  361. gtk_widget_map(child);
  362. gtk_widget_queue_resize(child);
  363. }
  364. }
  365. static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
  366. {
  367. GList *children;
  368. ColumnsChild *child;
  369. for (children = cols->children;
  370. children && (child = children->data);
  371. children = children->next) {
  372. if (child->widget == widget)
  373. return child;
  374. }
  375. return NULL;
  376. }
  377. void columns_force_left_align(Columns *cols, GtkWidget *widget)
  378. {
  379. ColumnsChild *child;
  380. g_return_if_fail(cols != NULL);
  381. g_return_if_fail(IS_COLUMNS(cols));
  382. g_return_if_fail(widget != NULL);
  383. child = columns_find_child(cols, widget);
  384. g_return_if_fail(child != NULL);
  385. child->force_left = TRUE;
  386. if (gtk_widget_get_visible(widget))
  387. gtk_widget_queue_resize(GTK_WIDGET(cols));
  388. }
  389. void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
  390. {
  391. ColumnsChild *child1, *child2;
  392. g_return_if_fail(cols != NULL);
  393. g_return_if_fail(IS_COLUMNS(cols));
  394. g_return_if_fail(cw1 != NULL);
  395. g_return_if_fail(cw2 != NULL);
  396. child1 = columns_find_child(cols, cw1);
  397. g_return_if_fail(child1 != NULL);
  398. child2 = columns_find_child(cols, cw2);
  399. g_return_if_fail(child2 != NULL);
  400. child1->same_height_as = child2;
  401. child2->same_height_as = child1;
  402. if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
  403. gtk_widget_queue_resize(GTK_WIDGET(cols));
  404. }
  405. void columns_taborder_last(Columns *cols, GtkWidget *widget)
  406. {
  407. GtkWidget *childw;
  408. GList *children;
  409. g_return_if_fail(cols != NULL);
  410. g_return_if_fail(IS_COLUMNS(cols));
  411. g_return_if_fail(widget != NULL);
  412. for (children = cols->taborder;
  413. children && (childw = children->data);
  414. children = children->next) {
  415. if (childw != widget)
  416. continue;
  417. cols->taborder = g_list_remove_link(cols->taborder, children);
  418. g_list_free(children);
  419. cols->taborder = g_list_append(cols->taborder, widget);
  420. #if GTK_CHECK_VERSION(2,0,0)
  421. gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
  422. #endif
  423. break;
  424. }
  425. }
  426. #if !GTK_CHECK_VERSION(2,0,0)
  427. /*
  428. * Override GtkContainer's focus movement so the user can
  429. * explicitly specify the tab order.
  430. */
  431. static gint columns_focus(GtkContainer *container, GtkDirectionType dir)
  432. {
  433. Columns *cols;
  434. GList *pos;
  435. GtkWidget *focuschild;
  436. g_return_val_if_fail(container != NULL, FALSE);
  437. g_return_val_if_fail(IS_COLUMNS(container), FALSE);
  438. cols = COLUMNS(container);
  439. if (!GTK_WIDGET_DRAWABLE(cols) ||
  440. !GTK_WIDGET_IS_SENSITIVE(cols))
  441. return FALSE;
  442. if (!GTK_WIDGET_CAN_FOCUS(container) &&
  443. (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
  444. focuschild = container->focus_child;
  445. gtk_container_set_focus_child(container, NULL);
  446. if (dir == GTK_DIR_TAB_FORWARD)
  447. pos = cols->taborder;
  448. else
  449. pos = g_list_last(cols->taborder);
  450. while (pos) {
  451. GtkWidget *child = pos->data;
  452. if (focuschild) {
  453. if (focuschild == child) {
  454. focuschild = NULL; /* now we can start looking in here */
  455. if (GTK_WIDGET_DRAWABLE(child) &&
  456. GTK_IS_CONTAINER(child) &&
  457. !GTK_WIDGET_HAS_FOCUS(child)) {
  458. if (gtk_container_focus(GTK_CONTAINER(child), dir))
  459. return TRUE;
  460. }
  461. }
  462. } else if (GTK_WIDGET_DRAWABLE(child)) {
  463. if (GTK_IS_CONTAINER(child)) {
  464. if (gtk_container_focus(GTK_CONTAINER(child), dir))
  465. return TRUE;
  466. } else if (GTK_WIDGET_CAN_FOCUS(child)) {
  467. gtk_widget_grab_focus(child);
  468. return TRUE;
  469. }
  470. }
  471. if (dir == GTK_DIR_TAB_FORWARD)
  472. pos = pos->next;
  473. else
  474. pos = pos->prev;
  475. }
  476. return FALSE;
  477. } else
  478. return columns_inherited_focus(container, dir);
  479. }
  480. #endif
  481. /*
  482. * Underlying parts of the layout algorithm, to compute the Columns
  483. * container's width or height given the widths or heights of its
  484. * children. These will be called in various ways with different
  485. * notions of width and height in use, so we abstract them out and
  486. * pass them a 'get width' or 'get height' function pointer.
  487. */
  488. typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
  489. static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
  490. {
  491. ColumnsChild *child;
  492. GList *children;
  493. gint i, ncols, colspan, retwidth, childwidth;
  494. const gint *percentages;
  495. static const gint onecol[] = { 100 };
  496. #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  497. printf("compute_width(%p): start\n", cols);
  498. #endif
  499. retwidth = 0;
  500. ncols = 1;
  501. percentages = onecol;
  502. for (children = cols->children;
  503. children && (child = children->data);
  504. children = children->next) {
  505. if (!child->widget) {
  506. /* Column reconfiguration. */
  507. ncols = child->ncols;
  508. percentages = child->percentages;
  509. continue;
  510. }
  511. /* Only take visible widgets into account. */
  512. if (!gtk_widget_get_visible(child->widget))
  513. continue;
  514. childwidth = get_width(child);
  515. colspan = child->colspan ? child->colspan : ncols-child->colstart;
  516. #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  517. printf("compute_width(%p): ", cols);
  518. if (GTK_IS_LABEL(child->widget))
  519. printf("label %p '%s' wrap=%s: ", child->widget,
  520. gtk_label_get_text(GTK_LABEL(child->widget)),
  521. (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
  522. ? "TRUE" : "FALSE"));
  523. else
  524. printf("widget %p: ", child->widget);
  525. {
  526. gint min, nat;
  527. gtk_widget_get_preferred_width(child->widget, &min, &nat);
  528. printf("minwidth=%d natwidth=%d ", min, nat);
  529. }
  530. printf("thiswidth=%d span=%d\n", childwidth, colspan);
  531. #endif
  532. /*
  533. * To compute width: we know that childwidth + cols->spacing
  534. * needs to equal a certain percentage of the full width of
  535. * the container. So we work this value out, figure out how
  536. * wide the container will need to be to make that percentage
  537. * of it equal to that width, and ensure our returned width is
  538. * at least that much. Very simple really.
  539. */
  540. {
  541. int percent, thiswid, fullwid;
  542. percent = 0;
  543. for (i = 0; i < colspan; i++)
  544. percent += percentages[child->colstart+i];
  545. thiswid = childwidth + cols->spacing;
  546. /*
  547. * Since childwidth is (at least sometimes) the _minimum_
  548. * size the child needs, we must ensure that it gets _at
  549. * least_ that size. Hence, when scaling thiswid up to
  550. * fullwid, we must round up, which means adding percent-1
  551. * before dividing by percent.
  552. */
  553. fullwid = (thiswid * 100 + percent - 1) / percent;
  554. #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  555. printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
  556. cols, child->widget, thiswid, fullwid);
  557. #endif
  558. /*
  559. * The above calculation assumes every widget gets
  560. * cols->spacing on the right. So we subtract
  561. * cols->spacing here to account for the extra load of
  562. * spacing on the right.
  563. */
  564. if (retwidth < fullwid - cols->spacing)
  565. retwidth = fullwid - cols->spacing;
  566. }
  567. }
  568. retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
  569. #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  570. printf("compute_width(%p): done, returning %d\n", cols, retwidth);
  571. #endif
  572. return retwidth;
  573. }
  574. static void columns_alloc_horiz(Columns *cols, gint ourwidth,
  575. widget_dim_fn_t get_width)
  576. {
  577. ColumnsChild *child;
  578. GList *children;
  579. gint i, ncols, colspan, border, *colxpos, childwidth;
  580. const gint *percentages;
  581. static const gint onecol[] = { 100 };
  582. border = gtk_container_get_border_width(GTK_CONTAINER(cols));
  583. ncols = 1;
  584. percentages = onecol;
  585. /* colxpos gives the starting x position of each column.
  586. * We supply n+1 of them, so that we can find the RH edge easily.
  587. * All ending x positions are expected to be adjusted afterwards by
  588. * subtracting the spacing. */
  589. colxpos = g_new(gint, 2);
  590. colxpos[0] = 0;
  591. colxpos[1] = ourwidth - 2*border + cols->spacing;
  592. for (children = cols->children;
  593. children && (child = children->data);
  594. children = children->next) {
  595. if (!child->widget) {
  596. gint percent;
  597. /* Column reconfiguration. */
  598. ncols = child->ncols;
  599. percentages = child->percentages;
  600. colxpos = g_renew(gint, colxpos, ncols + 1);
  601. colxpos[0] = 0;
  602. percent = 0;
  603. for (i = 0; i < ncols; i++) {
  604. percent += percentages[i];
  605. colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
  606. * percent / 100);
  607. }
  608. continue;
  609. }
  610. /* Only take visible widgets into account. */
  611. if (!gtk_widget_get_visible(child->widget))
  612. continue;
  613. childwidth = get_width(child);
  614. colspan = child->colspan ? child->colspan : ncols-child->colstart;
  615. /*
  616. * Starting x position is cols[colstart].
  617. * Ending x position is cols[colstart+colspan] - spacing.
  618. *
  619. * Unless we're forcing left, in which case the width is
  620. * exactly the requisition width.
  621. */
  622. child->x = colxpos[child->colstart];
  623. if (child->force_left)
  624. child->w = childwidth;
  625. else
  626. child->w = (colxpos[child->colstart+colspan] -
  627. colxpos[child->colstart] - cols->spacing);
  628. }
  629. g_free(colxpos);
  630. }
  631. static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
  632. {
  633. ColumnsChild *child;
  634. GList *children;
  635. gint i, ncols, colspan, *colypos, retheight, childheight;
  636. retheight = cols->spacing;
  637. ncols = 1;
  638. colypos = g_new(gint, 1);
  639. colypos[0] = 0;
  640. for (children = cols->children;
  641. children && (child = children->data);
  642. children = children->next) {
  643. if (!child->widget) {
  644. /* Column reconfiguration. */
  645. for (i = 1; i < ncols; i++) {
  646. if (colypos[0] < colypos[i])
  647. colypos[0] = colypos[i];
  648. }
  649. ncols = child->ncols;
  650. colypos = g_renew(gint, colypos, ncols);
  651. for (i = 1; i < ncols; i++)
  652. colypos[i] = colypos[0];
  653. continue;
  654. }
  655. /* Only take visible widgets into account. */
  656. if (!gtk_widget_get_visible(child->widget))
  657. continue;
  658. childheight = get_height(child);
  659. if (child->same_height_as) {
  660. gint childheight2 = get_height(child->same_height_as);
  661. if (childheight < childheight2)
  662. childheight = childheight2;
  663. }
  664. colspan = child->colspan ? child->colspan : ncols-child->colstart;
  665. /*
  666. * To compute height: the widget's top will be positioned at
  667. * the largest y value so far reached in any of the columns it
  668. * crosses. Then it will go down by childheight plus padding;
  669. * and the point it reaches at the bottom is the new y value
  670. * in all those columns, and minus the padding it is also a
  671. * lower bound on our own height.
  672. */
  673. {
  674. int topy, boty;
  675. topy = 0;
  676. for (i = 0; i < colspan; i++) {
  677. if (topy < colypos[child->colstart+i])
  678. topy = colypos[child->colstart+i];
  679. }
  680. boty = topy + childheight + cols->spacing;
  681. for (i = 0; i < colspan; i++) {
  682. colypos[child->colstart+i] = boty;
  683. }
  684. if (retheight < boty - cols->spacing)
  685. retheight = boty - cols->spacing;
  686. }
  687. }
  688. retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
  689. g_free(colypos);
  690. return retheight;
  691. }
  692. static void columns_alloc_vert(Columns *cols, gint ourheight,
  693. widget_dim_fn_t get_height)
  694. {
  695. ColumnsChild *child;
  696. GList *children;
  697. gint i, ncols, colspan, *colypos, realheight, fakeheight;
  698. ncols = 1;
  699. /* As in size_request, colypos is the lowest y reached in each column. */
  700. colypos = g_new(gint, 1);
  701. colypos[0] = 0;
  702. for (children = cols->children;
  703. children && (child = children->data);
  704. children = children->next) {
  705. if (!child->widget) {
  706. /* Column reconfiguration. */
  707. for (i = 1; i < ncols; i++) {
  708. if (colypos[0] < colypos[i])
  709. colypos[0] = colypos[i];
  710. }
  711. ncols = child->ncols;
  712. colypos = g_renew(gint, colypos, ncols);
  713. for (i = 1; i < ncols; i++)
  714. colypos[i] = colypos[0];
  715. continue;
  716. }
  717. /* Only take visible widgets into account. */
  718. if (!gtk_widget_get_visible(child->widget))
  719. continue;
  720. realheight = fakeheight = get_height(child);
  721. if (child->same_height_as) {
  722. gint childheight2 = get_height(child->same_height_as);
  723. if (fakeheight < childheight2)
  724. fakeheight = childheight2;
  725. }
  726. colspan = child->colspan ? child->colspan : ncols-child->colstart;
  727. /*
  728. * To compute height: the widget's top will be positioned
  729. * at the largest y value so far reached in any of the
  730. * columns it crosses. Then it will go down by creq.height
  731. * plus padding; and the point it reaches at the bottom is
  732. * the new y value in all those columns.
  733. */
  734. {
  735. int topy, boty;
  736. topy = 0;
  737. for (i = 0; i < colspan; i++) {
  738. if (topy < colypos[child->colstart+i])
  739. topy = colypos[child->colstart+i];
  740. }
  741. child->y = topy + fakeheight/2 - realheight/2;
  742. child->h = realheight;
  743. boty = topy + fakeheight + cols->spacing;
  744. for (i = 0; i < colspan; i++) {
  745. colypos[child->colstart+i] = boty;
  746. }
  747. }
  748. }
  749. g_free(colypos);
  750. }
  751. /*
  752. * Now here comes the interesting bit. The actual layout part is
  753. * done in the following two functions:
  754. *
  755. * columns_size_request() examines the list of widgets held in the
  756. * Columns, and returns a requisition stating the absolute minimum
  757. * size it can bear to be.
  758. *
  759. * columns_size_allocate() is given an allocation telling it what
  760. * size the whole container is going to be, and it calls
  761. * gtk_widget_size_allocate() on all of its (visible) children to
  762. * set their size and position relative to the top left of the
  763. * container.
  764. */
  765. #if !GTK_CHECK_VERSION(3,0,0)
  766. static gint columns_gtk2_get_width(ColumnsChild *child)
  767. {
  768. GtkRequisition creq;
  769. gtk_widget_size_request(child->widget, &creq);
  770. return creq.width;
  771. }
  772. static gint columns_gtk2_get_height(ColumnsChild *child)
  773. {
  774. GtkRequisition creq;
  775. gtk_widget_size_request(child->widget, &creq);
  776. return creq.height;
  777. }
  778. static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
  779. {
  780. Columns *cols;
  781. g_return_if_fail(widget != NULL);
  782. g_return_if_fail(IS_COLUMNS(widget));
  783. g_return_if_fail(req != NULL);
  784. cols = COLUMNS(widget);
  785. req->width = columns_compute_width(cols, columns_gtk2_get_width);
  786. req->height = columns_compute_height(cols, columns_gtk2_get_height);
  787. }
  788. static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
  789. {
  790. Columns *cols;
  791. ColumnsChild *child;
  792. GList *children;
  793. gint border;
  794. g_return_if_fail(widget != NULL);
  795. g_return_if_fail(IS_COLUMNS(widget));
  796. g_return_if_fail(alloc != NULL);
  797. cols = COLUMNS(widget);
  798. gtk_widget_set_allocation(widget, alloc);
  799. border = gtk_container_get_border_width(GTK_CONTAINER(cols));
  800. columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
  801. columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
  802. for (children = cols->children;
  803. children && (child = children->data);
  804. children = children->next) {
  805. if (child->widget && gtk_widget_get_visible(child->widget)) {
  806. GtkAllocation call;
  807. call.x = alloc->x + border + child->x;
  808. call.y = alloc->y + border + child->y;
  809. call.width = child->w;
  810. call.height = child->h;
  811. gtk_widget_size_allocate(child->widget, &call);
  812. }
  813. }
  814. }
  815. #else /* GTK_CHECK_VERSION(3,0,0) */
  816. static gint columns_gtk3_get_min_width(ColumnsChild *child)
  817. {
  818. gint ret;
  819. gtk_widget_get_preferred_width(child->widget, &ret, NULL);
  820. return ret;
  821. }
  822. static gint columns_gtk3_get_nat_width(ColumnsChild *child)
  823. {
  824. gint ret;
  825. if ((GTK_IS_LABEL(child->widget) &&
  826. gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
  827. GTK_IS_ENTRY(child->widget)) {
  828. /*
  829. * We treat wrapping GtkLabels as a special case in this
  830. * layout class, because the whole point of those is that I
  831. * _don't_ want them to take up extra horizontal space for
  832. * long text, but instead to wrap it to whatever size is used
  833. * by the rest of the layout.
  834. *
  835. * GtkEntry gets similar treatment, because in OS X GTK I've
  836. * found that it requests a natural width regardless of the
  837. * output of gtk_entry_set_width_chars.
  838. */
  839. gtk_widget_get_preferred_width(child->widget, &ret, NULL);
  840. } else {
  841. gtk_widget_get_preferred_width(child->widget, NULL, &ret);
  842. }
  843. return ret;
  844. }
  845. static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
  846. {
  847. gint ret;
  848. gtk_widget_get_preferred_width_for_height(child->widget, child->h,
  849. &ret, NULL);
  850. return ret;
  851. }
  852. static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
  853. {
  854. gint ret;
  855. gtk_widget_get_preferred_width_for_height(child->widget, child->h,
  856. NULL, &ret);
  857. return ret;
  858. }
  859. static gint columns_gtk3_get_min_height(ColumnsChild *child)
  860. {
  861. gint ret;
  862. gtk_widget_get_preferred_height(child->widget, &ret, NULL);
  863. return ret;
  864. }
  865. static gint columns_gtk3_get_nat_height(ColumnsChild *child)
  866. {
  867. gint ret;
  868. gtk_widget_get_preferred_height(child->widget, NULL, &ret);
  869. return ret;
  870. }
  871. static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
  872. {
  873. gint ret;
  874. gtk_widget_get_preferred_height_for_width(child->widget, child->w,
  875. &ret, NULL);
  876. return ret;
  877. }
  878. static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
  879. {
  880. gint ret;
  881. gtk_widget_get_preferred_height_for_width(child->widget, child->w,
  882. NULL, &ret);
  883. return ret;
  884. }
  885. static void columns_get_preferred_width(GtkWidget *widget,
  886. gint *min, gint *nat)
  887. {
  888. Columns *cols;
  889. g_return_if_fail(widget != NULL);
  890. g_return_if_fail(IS_COLUMNS(widget));
  891. cols = COLUMNS(widget);
  892. if (min)
  893. *min = columns_compute_width(cols, columns_gtk3_get_min_width);
  894. if (nat)
  895. *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
  896. }
  897. static void columns_get_preferred_height(GtkWidget *widget,
  898. gint *min, gint *nat)
  899. {
  900. Columns *cols;
  901. g_return_if_fail(widget != NULL);
  902. g_return_if_fail(IS_COLUMNS(widget));
  903. cols = COLUMNS(widget);
  904. if (min)
  905. *min = columns_compute_height(cols, columns_gtk3_get_min_height);
  906. if (nat)
  907. *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
  908. }
  909. static void columns_get_preferred_width_for_height(GtkWidget *widget,
  910. gint height,
  911. gint *min, gint *nat)
  912. {
  913. Columns *cols;
  914. g_return_if_fail(widget != NULL);
  915. g_return_if_fail(IS_COLUMNS(widget));
  916. cols = COLUMNS(widget);
  917. /* FIXME: which one should the get-height function here be? */
  918. columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
  919. if (min)
  920. *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
  921. if (nat)
  922. *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
  923. }
  924. static void columns_get_preferred_height_for_width(GtkWidget *widget,
  925. gint width,
  926. gint *min, gint *nat)
  927. {
  928. Columns *cols;
  929. g_return_if_fail(widget != NULL);
  930. g_return_if_fail(IS_COLUMNS(widget));
  931. cols = COLUMNS(widget);
  932. /* FIXME: which one should the get-height function here be? */
  933. columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
  934. if (min)
  935. *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
  936. if (nat)
  937. *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
  938. }
  939. static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
  940. {
  941. Columns *cols;
  942. ColumnsChild *child;
  943. GList *children;
  944. gint border;
  945. g_return_if_fail(widget != NULL);
  946. g_return_if_fail(IS_COLUMNS(widget));
  947. g_return_if_fail(alloc != NULL);
  948. cols = COLUMNS(widget);
  949. gtk_widget_set_allocation(widget, alloc);
  950. border = gtk_container_get_border_width(GTK_CONTAINER(cols));
  951. columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
  952. columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
  953. for (children = cols->children;
  954. children && (child = children->data);
  955. children = children->next) {
  956. if (child->widget && gtk_widget_get_visible(child->widget)) {
  957. GtkAllocation call;
  958. call.x = alloc->x + border + child->x;
  959. call.y = alloc->y + border + child->y;
  960. call.width = child->w;
  961. call.height = child->h;
  962. gtk_widget_size_allocate(child->widget, &call);
  963. }
  964. }
  965. }
  966. #endif