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