#include <stdarg.h>
#include <ctype.h>
#include <time.h>
+
#include <gtk/gtk.h>
#if !GTK_CHECK_VERSION(3,0,0)
#include <gdk/gdkkeysyms.h>
#endif
+
+#include "putty.h"
+#include "gtkcompat.h"
+#include "gtkcols.h"
+#include "gtkfont.h"
+
#ifndef NOT_X_WINDOWS
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
-#include "gtkcompat.h"
-
-#include "gtkcols.h"
-#include "gtkfont.h"
-
#ifdef TESTMODE
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#endif
-#include "putty.h"
#include "storage.h"
#include "dialog.h"
#include "tree234.h"
#if !GTK_CHECK_VERSION(2,0,0)
GtkWidget *currtreeitem, **treeitems;
int ntreeitems;
+#else
+ int nselparams;
+ struct selparam *selparams;
#endif
int retval;
};
#if !GTK_CHECK_VERSION(2,4,0)
static void menuitem_activate(GtkMenuItem *item, gpointer data);
#endif
+#if GTK_CHECK_VERSION(3,0,0)
+static void colourchoose_response(GtkDialog *dialog,
+ gint response_id, gpointer data);
+#else
static void coloursel_ok(GtkButton *button, gpointer data);
static void coloursel_cancel(GtkButton *button, gpointer data);
+#endif
static void window_destroy(GtkWidget *widget, gpointer data);
int get_listitemheight(GtkWidget *widget);
set_transient_window_pos(dp->window, window);
gtk_widget_show(window);
gtk_main();
+ post_main();
}
/*
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
- GtkWidget *okbutton, *cancelbutton;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkWidget *coloursel =
+ gtk_color_chooser_dialog_new("Select a colour",
+ GTK_WINDOW(dp->window));
+ gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(coloursel), FALSE);
+#else
+ GtkWidget *okbutton, *cancelbutton;
GtkWidget *coloursel =
gtk_color_selection_dialog_new("Select a colour");
GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);
GtkColorSelection *cs = GTK_COLOR_SELECTION
(gtk_color_selection_dialog_get_color_selection(ccs));
+ gtk_color_selection_set_has_opacity_control(cs, FALSE);
+#endif
dp->coloursel_result.ok = FALSE;
gtk_window_set_modal(GTK_WINDOW(coloursel), TRUE);
- gtk_color_selection_set_has_opacity_control(cs, FALSE);
-#if GTK_CHECK_VERSION(2,0,0)
+#if GTK_CHECK_VERSION(3,0,0)
+ {
+ GdkRGBA rgba;
+ rgba.red = r / 255.0;
+ rgba.green = g / 255.0;
+ rgba.blue = b / 255.0;
+ rgba.alpha = 1.0; /* fully opaque! */
+ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(coloursel), &rgba);
+ }
+#elif GTK_CHECK_VERSION(2,0,0)
{
GdkColor col;
col.red = r * 0x0101;
}
#endif
+ g_object_set_data(G_OBJECT(coloursel), "user-data", (gpointer)uc);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(G_OBJECT(coloursel), "response",
+ G_CALLBACK(colourchoose_response), (gpointer)dp);
+#else
+
#if GTK_CHECK_VERSION(2,0,0)
g_object_get(G_OBJECT(ccs),
"ok-button", &okbutton,
(gpointer)coloursel);
g_object_set_data(G_OBJECT(cancelbutton), "user-data",
(gpointer)coloursel);
- g_object_set_data(G_OBJECT(coloursel), "user-data", (gpointer)uc);
g_signal_connect(G_OBJECT(okbutton), "clicked",
G_CALLBACK(coloursel_ok), (gpointer)dp);
g_signal_connect(G_OBJECT(cancelbutton), "clicked",
g_signal_connect_swapped(G_OBJECT(cancelbutton), "clicked",
G_CALLBACK(gtk_widget_destroy),
(gpointer)coloursel);
+#endif
gtk_widget_show(coloursel);
}
#endif
}
+#if GTK_CHECK_VERSION(3,0,0)
+
+static void colourchoose_response(GtkDialog *dialog,
+ gint response_id, gpointer data)
+{
+ struct dlgparam *dp = (struct dlgparam *)data;
+ struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data");
+
+ if (response_id == GTK_RESPONSE_OK) {
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &rgba);
+ dp->coloursel_result.r = (int) (255 * rgba.red);
+ dp->coloursel_result.g = (int) (255 * rgba.green);
+ dp->coloursel_result.b = (int) (255 * rgba.blue);
+ dp->coloursel_result.ok = TRUE;
+ } else {
+ dp->coloursel_result.ok = FALSE;
+ }
+
+ uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
+
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+#else /* GTK 1/2 coloursel response handlers */
+
static void coloursel_ok(GtkButton *button, gpointer data)
{
struct dlgparam *dp = (struct dlgparam *)data;
gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data");
struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data");
- GtkColorSelection *cs = GTK_COLOR_SELECTION
- (gtk_color_selection_dialog_get_color_selection
- (GTK_COLOR_SELECTION_DIALOG(coloursel)));
+
#if GTK_CHECK_VERSION(2,0,0)
{
+ GtkColorSelection *cs = GTK_COLOR_SELECTION
+ (gtk_color_selection_dialog_get_color_selection
+ (GTK_COLOR_SELECTION_DIALOG(coloursel)));
GdkColor col;
gtk_color_selection_get_current_color(cs, &col);
dp->coloursel_result.r = col.red / 0x0100;
}
#else
{
+ GtkColorSelection *cs = GTK_COLOR_SELECTION
+ (gtk_color_selection_dialog_get_color_selection
+ (GTK_COLOR_SELECTION_DIALOG(coloursel)));
gdouble cvals[4];
gtk_color_selection_get_color(cs, cvals);
dp->coloursel_result.r = (int) (255 * cvals[0]);
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
}
+#endif /* end of coloursel response handlers */
+
static void filefont_clicked(GtkButton *button, gpointer data)
{
struct dlgparam *dp = (struct dlgparam *)data;
(uc->ctrl->fileselect.for_writing ?
GTK_FILE_CHOOSER_ACTION_SAVE :
GTK_FILE_CHOOSER_ACTION_OPEN),
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL,
+ STANDARD_OPEN_LABEL, GTK_RESPONSE_ACCEPT,
(const gchar *)NULL);
gtk_window_set_modal(GTK_WINDOW(filechoose), TRUE);
g_object_set_data(G_OBJECT(filechoose), "user-data", (gpointer)uc);
}
}
+#if !GTK_CHECK_VERSION(3,0,0)
static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,
gpointer data)
{
gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label);
g_signal_handler_disconnect(G_OBJECT(uc->text), uc->textsig);
}
+#endif
/* ----------------------------------------------------------------------
* This function does the main layout work: it reads a controlset,
break;
case CTRL_EDITBOX:
{
- GtkRequisition req;
GtkWidget *signalobject;
if (ctrl->editbox.has_list) {
G_CALLBACK(editbox_lostfocus), dp);
g_signal_connect(G_OBJECT(signalobject), "focus_out_event",
G_CALLBACK(editbox_lostfocus), dp);
+
+#if !GTK_CHECK_VERSION(3,0,0)
/*
* Edit boxes, for some strange reason, have a minimum
* width of 150 in GTK 1.2. We don't want this - we'd
* rather the edit boxes acquired their natural width
* from the column layout of the rest of the box.
- *
- * Also, while we're here, we'll squirrel away the
- * edit box height so we can use that to centre its
- * label vertically beside it.
*/
- gtk_widget_size_request(w, &req);
- gtk_widget_set_size_request(w, 10, req.height);
+ {
+ GtkRequisition req;
+ gtk_widget_size_request(w, &req);
+ gtk_widget_set_size_request(w, 10, req.height);
+ }
+#else
+ /*
+ * In GTK 3, this is still true, but there's a special
+ * method for GtkEntry in particular to fix it.
+ */
+ if (GTK_IS_ENTRY(w))
+ gtk_entry_set_width_chars(GTK_ENTRY(w), 1);
+#endif
if (ctrl->generic.label) {
GtkWidget *label, *container;
columns_add(COLUMNS(container), label, 0, 1);
columns_force_left_align(COLUMNS(container), label);
columns_add(COLUMNS(container), w, 1, 1);
- /* Centre the label vertically. */
- gtk_widget_set_size_request(label, -1, req.height);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ columns_force_same_height(COLUMNS(container),
+ label, w);
}
gtk_widget_show(label);
gtk_widget_show(w);
case CTRL_FONTSELECT:
{
GtkWidget *ww;
- GtkRequisition req;
const char *browsebtn =
(ctrl->generic.type == CTRL_FILESELECT ?
"Browse..." : "Change...");
}
uc->entry = ww = gtk_entry_new();
- gtk_widget_size_request(ww, &req);
- gtk_widget_set_size_request(ww, 10, req.height);
+#if !GTK_CHECK_VERSION(3,0,0)
+ {
+ GtkRequisition req;
+ gtk_widget_size_request(ww, &req);
+ gtk_widget_set_size_request(ww, 10, req.height);
+ }
+#else
+ gtk_entry_set_width_chars(GTK_ENTRY(ww), 1);
+#endif
columns_add(COLUMNS(w), ww, 0, 1);
gtk_widget_show(ww);
columns_add(COLUMNS(w), ww, 1, 1);
gtk_widget_show(ww);
+ columns_force_same_height(COLUMNS(w), uc->entry, uc->button);
+
g_signal_connect(G_OBJECT(uc->entry), "key_press_event",
G_CALLBACK(editbox_key), dp);
uc->entrysig =
if (ctrl->generic.label) {
GtkWidget *label, *container;
- GtkRequisition req;
label = gtk_label_new(ctrl->generic.label);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_label_set_width_chars(GTK_LABEL(label), 3);
+#endif
shortcut_add(scs, label, ctrl->listbox.shortcut,
SHORTCUT_FOCUS, w);
columns_add(COLUMNS(container), label, 0, 1);
columns_force_left_align(COLUMNS(container), label);
columns_add(COLUMNS(container), w, 1, 1);
- /* Centre the label vertically. */
- gtk_widget_size_request(w, &req);
- gtk_widget_set_size_request(label, -1, req.height);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ columns_force_same_height(COLUMNS(container),
+ label, w);
}
gtk_widget_show(label);
gtk_widget_show(w);
break;
case CTRL_TEXT:
+#if !GTK_CHECK_VERSION(3,0,0)
/*
- * Wrapping text widgets don't sit well with the GTK
+ * Wrapping text widgets don't sit well with the GTK2
* layout model, in which widgets state a minimum size
* and the whole window then adjusts to the smallest
* size it can sensibly take given its contents. A
* than one line).
*/
uc->text = w = gtk_label_new("X");
- gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
- gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
uc->textsig =
g_signal_connect(G_OBJECT(w), "size-allocate",
G_CALLBACK(label_sizealloc), dp);
+#else
+ /*
+ * In GTK3, this is all fixed, because the main aim of the
+ * new 'height-for-width' geometry management is to make
+ * wrapping labels behave sensibly. So now we can just do
+ * the obvious thing.
+ */
+ uc->text = w = gtk_label_new(uc->ctrl->generic.label);
+#endif
+ gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
+ gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
break;
}
for (i = 0; currstr[i]; i++)
if (tolower((unsigned char)currstr[i]) == chr) {
- GtkRequisition req;
-
pattern = dupprintf("%*s_", i, "");
-
- gtk_widget_size_request(GTK_WIDGET(label), &req);
gtk_label_set_pattern(label, pattern);
- gtk_widget_set_size_request(GTK_WIDGET(label), -1, req.height);
-
sfree(pattern);
break;
}
#else
int height;
GtkCellRenderer *cr = gtk_cell_renderer_text_new();
+#if GTK_CHECK_VERSION(3,0,0)
+ {
+ GtkRequisition req;
+ /*
+ * Since none of my list items wraps in this GUI, no
+ * interesting width-for-height behaviour should be happening,
+ * so I don't think it should matter here whether I ask for
+ * the minimum or natural height.
+ */
+ gtk_cell_renderer_get_preferred_size(cr, w, &req, NULL);
+ height = req.height;
+ }
+#else
gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height);
+#endif
g_object_ref(G_OBJECT(cr));
g_object_ref_sink(G_OBJECT(cr));
g_object_unref(G_OBJECT(cr));
w, FALSE, TRUE, 0);
gtk_widget_show(w);
gtk_widget_hide(gtk_dialog_get_action_area(dlg));
+#if !GTK_CHECK_VERSION(3,0,0)
+ /* This cosmetic property is withdrawn in GTK 3's GtkDialog */
g_object_set(G_OBJECT(dlg), "has-separator", TRUE, (const char *)NULL);
#endif
+#endif
}
+#if GTK_CHECK_VERSION(2,0,0)
+void initial_treeview_collapse(struct dlgparam *dp, GtkWidget *tree)
+{
+ /*
+ * Collapse the deeper branches of the treeview into the state we
+ * like them to start off in. See comment below in do_config_box.
+ */
+ int i;
+ for (i = 0; i < dp->nselparams; i++)
+ if (dp->selparams[i].depth >= 2)
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),
+ dp->selparams[i].treepath);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+void treeview_map_event(GtkWidget *tree, gpointer data)
+{
+ struct dlgparam *dp = (struct dlgparam *)data;
+ GtkAllocation alloc;
+ gtk_widget_get_allocation(tree, &alloc);
+ gtk_widget_set_size_request(tree, alloc.width, -1);
+ initial_treeview_collapse(dp, tree);
+}
+#endif
+
int do_config_box(const char *title, Conf *conf, int midsession,
int protcfginfo)
{
}
#if GTK_CHECK_VERSION(2,0,0)
- {
- GtkRequisition req;
- int i;
+ /*
+ * We want our tree view to come up with all branches at depth 2
+ * or more collapsed. However, if we start off with those branches
+ * collapsed, then the tree view's size request will be calculated
+ * based on the width of the collapsed tree, and then when the
+ * collapsed branches are expanded later, the tree view will
+ * jarringly change size.
+ *
+ * So instead we start with everything expanded; then, once the
+ * tree view has computed its resulting width requirement, we
+ * collapse the relevant rows, but force the width to be the value
+ * we just retrieved. This arranges that the tree view is wide
+ * enough to have all branches expanded without further resizing.
+ */
- /*
- * We want our tree view to come up with all branches at
- * depth 2 or more collapsed. However, if we start off
- * with those branches collapsed, then the tree view's
- * size request will be calculated based on the width of
- * the collapsed tree. So instead we start with them all
- * expanded; then we ask for the current size request,
- * collapse the relevant rows, and force the width to the
- * value we just computed. This arranges that the tree
- * view is wide enough to have all branches expanded
- * safely.
- */
+ dp.nselparams = nselparams;
+ dp.selparams = selparams;
+#if !GTK_CHECK_VERSION(3,0,0)
+ {
+ /*
+ * In GTK2, we can just do the job right now.
+ */
+ GtkRequisition req;
gtk_widget_size_request(tree, &req);
-
- for (i = 0; i < nselparams; i++)
- if (selparams[i].depth >= 2)
- gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),
- selparams[i].treepath);
-
+ initial_treeview_collapse(&dp, tree);
gtk_widget_set_size_request(tree, req.width, -1);
}
-#endif
+#else
+ /*
+ * But in GTK3, we have to wait until the widget is about to be
+ * mapped, because the size computation won't have been done yet.
+ */
+ g_signal_connect(G_OBJECT(tree), "map",
+ G_CALLBACK(treeview_map_event), &dp);
+#endif /* GTK 2 vs 3 */
+#endif /* GTK 2+ vs 1 */
#if GTK_CHECK_VERSION(2,0,0)
g_signal_connect(G_OBJECT(treeselection), "changed",
#else
dp.ntreeitems = nselparams;
dp.treeitems = snewn(dp.ntreeitems, GtkWidget *);
-
for (index = 0; index < nselparams; index++) {
g_signal_connect(G_OBJECT(selparams[index].treeitem), "select",
G_CALLBACK(treeitem_sel),
G_CALLBACK(win_key_press), &dp);
gtk_main();
+ post_main();
dlg_cleanup(&dp);
sfree(selparams);
G_CALLBACK(win_key_press), &dp);
gtk_main();
+ post_main();
dlg_cleanup(&dp);
ctrl_free_box(ctrlbox);
int string_width(const char *text)
{
- GtkWidget *label = gtk_label_new(text);
- GtkRequisition req;
- gtk_widget_size_request(label, &req);
- g_object_ref_sink(G_OBJECT(label));
- return req.width;
+ int ret;
+ get_label_text_dimensions(text, &ret, NULL);
+ return ret;
}
int reallyclose(void *frontend)