gtkmisc.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Miscellaneous GTK helper functions.
  3. */
  4. #include <assert.h>
  5. #include <stdarg.h>
  6. #include <ctype.h>
  7. #include <time.h>
  8. #include <gtk/gtk.h>
  9. #if !GTK_CHECK_VERSION(3,0,0)
  10. #include <gdk/gdkkeysyms.h>
  11. #endif
  12. #include "putty.h"
  13. #include "gtkcompat.h"
  14. void get_label_text_dimensions(const char *text, int *width, int *height)
  15. {
  16. /*
  17. * Determine the dimensions of a piece of text in the standard
  18. * font used in GTK interface elements like labels. We do this by
  19. * instantiating an actual GtkLabel, and then querying its size.
  20. *
  21. * But GTK2 and GTK3 require us to query the size completely
  22. * differently. I'm sure there ought to be an easier approach than
  23. * the way I'm doing this in GTK3, too!
  24. */
  25. GtkWidget *label = gtk_label_new(text);
  26. #if GTK_CHECK_VERSION(3,0,0)
  27. PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(label));
  28. PangoRectangle logrect;
  29. pango_layout_get_extents(layout, NULL, &logrect);
  30. if (width)
  31. *width = logrect.width / PANGO_SCALE;
  32. if (height)
  33. *height = logrect.height / PANGO_SCALE;
  34. #else
  35. GtkRequisition req;
  36. gtk_widget_size_request(label, &req);
  37. if (width)
  38. *width = req.width;
  39. if (height)
  40. *height = req.height;
  41. #endif
  42. g_object_ref_sink(G_OBJECT(label));
  43. #if GTK_CHECK_VERSION(2,10,0)
  44. g_object_unref(label);
  45. #endif
  46. }
  47. int string_width(const char *text)
  48. {
  49. int ret;
  50. get_label_text_dimensions(text, &ret, NULL);
  51. return ret;
  52. }
  53. void align_label_left(GtkLabel *label)
  54. {
  55. #if GTK_CHECK_VERSION(3,16,0)
  56. gtk_label_set_xalign(label, 0.0);
  57. #elif GTK_CHECK_VERSION(3,14,0)
  58. gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START);
  59. #else
  60. gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
  61. #endif
  62. }
  63. /* ----------------------------------------------------------------------
  64. * Functions to arrange controls in a basically dialog-like window.
  65. *
  66. * The best method for doing this has varied wildly with versions of
  67. * GTK, hence the set of wrapper functions here.
  68. *
  69. * In GTK 1, a GtkDialog has an 'action_area' at the bottom, which is
  70. * a GtkHBox which stretches to cover the full width of the dialog. So
  71. * we can either add buttons or other widgets to that box directly, or
  72. * alternatively we can fill the hbox with some layout class of our
  73. * own such as a Columns widget.
  74. *
  75. * In GTK 2, the action area has become a GtkHButtonBox, and its
  76. * layout behaviour seems to be different and not what we want. So
  77. * instead we abandon the dialog's action area completely: we
  78. * gtk_widget_hide() it in the below code, and we also call
  79. * gtk_dialog_set_has_separator() to remove the separator above it. We
  80. * then insert our own action area into the end of the dialog's main
  81. * vbox, and add our own separator above that.
  82. *
  83. * In GTK 3, we typically don't even want to use GtkDialog at all,
  84. * because GTK 3 has become a lot more restrictive about what you can
  85. * sensibly use GtkDialog for - it deprecates direct access to the
  86. * action area in favour of making you provide nothing but
  87. * dialog-ending buttons in the form of (text, response code) pairs,
  88. * so you can't put any other kind of control in there, or fiddle with
  89. * alignment and positioning, or even have a button that _doesn't_ end
  90. * the dialog (e.g. 'View Licence' in our About box). So instead of
  91. * GtkDialog, we use a straight-up GtkWindow and have it contain a
  92. * vbox as its (unique) child widget; and we implement the action area
  93. * by adding a separator and another widget at the bottom of that
  94. * vbox.
  95. */
  96. GtkWidget *our_dialog_new(void)
  97. {
  98. #if GTK_CHECK_VERSION(3,0,0)
  99. /*
  100. * See comment in our_dialog_set_action_area(): in GTK 3, we use
  101. * GtkWindow in place of GtkDialog for most purposes.
  102. */
  103. GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  104. GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
  105. gtk_container_add(GTK_CONTAINER(w), vbox);
  106. gtk_widget_show(vbox);
  107. return w;
  108. #else
  109. return gtk_dialog_new();
  110. #endif
  111. }
  112. void our_dialog_set_action_area(GtkWindow *dlg, GtkWidget *w)
  113. {
  114. #if !GTK_CHECK_VERSION(2,0,0)
  115. gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area),
  116. w, TRUE, TRUE, 0);
  117. #elif !GTK_CHECK_VERSION(3,0,0)
  118. GtkWidget *align;
  119. align = gtk_alignment_new(0, 0, 1, 1);
  120. gtk_container_add(GTK_CONTAINER(align), w);
  121. /*
  122. * The purpose of this GtkAlignment is to provide padding
  123. * around the buttons. The padding we use is twice the padding
  124. * used in our GtkColumns, because we nest two GtkColumns most
  125. * of the time (one separating the tree view from the main
  126. * controls, and another for the main controls themselves).
  127. */
  128. #if GTK_CHECK_VERSION(2,4,0)
  129. gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8);
  130. #endif
  131. gtk_widget_show(align);
  132. gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
  133. align, FALSE, TRUE, 0);
  134. w = gtk_hseparator_new();
  135. gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
  136. w, FALSE, TRUE, 0);
  137. gtk_widget_show(w);
  138. gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(dlg)));
  139. g_object_set(G_OBJECT(dlg), "has-separator", TRUE, (const char *)NULL);
  140. #else /* GTK 3 */
  141. /* GtkWindow is a GtkBin, hence contains exactly one child, which
  142. * here we always expect to be a vbox */
  143. GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg)));
  144. GtkWidget *sep;
  145. g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
  146. gtk_box_pack_end(vbox, w, FALSE, TRUE, 0);
  147. sep = gtk_hseparator_new();
  148. gtk_box_pack_end(vbox, sep, FALSE, TRUE, 0);
  149. gtk_widget_show(sep);
  150. #endif
  151. }
  152. GtkBox *our_dialog_make_action_hbox(GtkWindow *dlg)
  153. {
  154. #if GTK_CHECK_VERSION(3,0,0)
  155. GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  156. our_dialog_set_action_area(dlg, hbox);
  157. g_object_set(G_OBJECT(hbox), "margin", 0, (const char *)NULL);
  158. g_object_set(G_OBJECT(hbox), "spacing", 8, (const char *)NULL);
  159. gtk_widget_show(hbox);
  160. return GTK_BOX(hbox);
  161. #else /* not GTK 3 */
  162. return GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dlg)));
  163. #endif
  164. }
  165. void our_dialog_add_to_content_area(GtkWindow *dlg, GtkWidget *w,
  166. gboolean expand, gboolean fill,
  167. guint padding)
  168. {
  169. #if GTK_CHECK_VERSION(3,0,0)
  170. /* GtkWindow is a GtkBin, hence contains exactly one child, which
  171. * here we always expect to be a vbox */
  172. GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg)));
  173. gtk_box_pack_start(vbox, w, expand, fill, padding);
  174. #else
  175. gtk_box_pack_start
  176. (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
  177. w, expand, fill, padding);
  178. #endif
  179. }