2003-03-13 19:52:28 +00:00
|
|
|
/*
|
|
|
|
* gtkdlg.c - GTK implementation of the PuTTY configuration box.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
2003-03-31 11:21:07 +00:00
|
|
|
#include <stdarg.h>
|
2003-03-16 12:37:48 +00:00
|
|
|
#include <ctype.h>
|
2003-04-09 18:46:45 +00:00
|
|
|
#include <time.h>
|
2015-08-31 12:05:51 +00:00
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
#include <gtk/gtk.h>
|
2015-08-08 14:06:15 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
2003-03-16 12:37:48 +00:00
|
|
|
#include <gdk/gdkkeysyms.h>
|
2015-08-08 14:06:15 +00:00
|
|
|
#endif
|
2015-08-31 12:05:51 +00:00
|
|
|
|
2015-08-31 14:45:18 +00:00
|
|
|
#define MAY_REFER_TO_GTK_IN_HEADERS
|
|
|
|
|
2015-08-31 12:05:51 +00:00
|
|
|
#include "putty.h"
|
|
|
|
#include "gtkcompat.h"
|
2021-04-23 05:19:05 +00:00
|
|
|
#include "columns.h"
|
|
|
|
#include "unifont.h"
|
2015-08-31 14:45:18 +00:00
|
|
|
#include "gtkmisc.h"
|
2015-08-31 12:05:51 +00:00
|
|
|
|
2015-08-08 15:35:19 +00:00
|
|
|
#ifndef NOT_X_WINDOWS
|
2003-03-18 19:06:51 +00:00
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
Basic support for running under GDK Wayland back end.
GTK 3 PuTTY/pterm has always assumed that if it was compiled with
_support_ for talking to the raw X11 layer underneath GTK and GDK,
then it was entitled to expect that raw X11 layer to exist at all
times, i.e. that GDK_DISPLAY_XDISPLAY would return a meaningful X
display that it could do useful things with. So if you ran it over the
GDK Wayland backend, it would immediately segfault.
Modern GTK applications need to cope with multiple GDK backends at run
time. It's fine for GTK PuTTY to _contain_ the code to find and use
underlying X11 primitives like the display and the X window id, but it
should be prepared to find that it's running on Wayland (or something
else again!) so those functions don't return anything useful - in
which case it should degrade gracefully to the subset of functionality
that can be accessed through backend-independent GTK calls.
Accordingly, I've centralised the use of GDK_DISPLAY_XDISPLAY into a
support function get_x_display() in gtkmisc.c, which starts by
checking that there actually is one first. All previous direct uses of
GDK_*_XDISPLAY now go via that function, and check the result for NULL
afterwards. (To save faffing about calling that function too many
times, I'm also caching the display pointer in more places, and
passing it as an extra argument to various subfunctions, mostly in
gtkfont.c.)
Similarly, the get_windowid() function that retrieves the window id to
put in the environment of pterm's child process has to be prepared for
there not to be a window id.
This isn't a complete fix for all Wayland-related problems. The other
one I'm currently aware of is that the default font is "server:fixed",
which is a bad default now that it won't be available on all backends.
And I expect that further problems will show up with more testing. But
it's a start.
2018-05-09 08:18:20 +00:00
|
|
|
#include "x11misc.h"
|
2015-08-08 15:35:19 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
#include "storage.h"
|
2003-03-13 19:52:28 +00:00
|
|
|
#include "dialog.h"
|
2003-03-15 15:50:42 +00:00
|
|
|
#include "tree234.h"
|
2015-12-22 12:43:31 +00:00
|
|
|
#include "licence.h"
|
2021-03-13 10:59:47 +00:00
|
|
|
#include "ssh.h"
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2015-08-08 16:34:25 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
/* Decide which of GtkFileChooserDialog and GtkFileSelection to use */
|
|
|
|
#define USE_GTK_FILE_CHOOSER_DIALOG
|
|
|
|
#endif
|
|
|
|
|
2003-03-16 12:37:48 +00:00
|
|
|
struct Shortcut {
|
|
|
|
GtkWidget *widget;
|
|
|
|
struct uctrl *uc;
|
|
|
|
int action;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Shortcuts {
|
|
|
|
struct Shortcut sc[128];
|
|
|
|
};
|
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam;
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl {
|
|
|
|
union control *ctrl;
|
|
|
|
GtkWidget *toplevel;
|
|
|
|
GtkWidget **buttons; int nbuttons; /* for radio buttons */
|
2008-04-02 14:48:06 +00:00
|
|
|
GtkWidget *entry; /* for editbox, filesel, fontsel */
|
2008-04-04 10:16:24 +00:00
|
|
|
GtkWidget *button; /* for filesel, fontsel */
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkWidget *list; /* for listbox (in GTK1), combobox (<=GTK2.3) */
|
|
|
|
GtkWidget *menu; /* for optionmenu (==droplist) */
|
|
|
|
GtkWidget *optmenu; /* also for optionmenu */
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
2008-04-02 14:48:06 +00:00
|
|
|
GtkWidget *combo; /* for combo box (either editable or not) */
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkWidget *treeview; /* for listbox (GTK2), droplist+combo (>=2.4) */
|
2008-04-02 14:48:06 +00:00
|
|
|
GtkListStore *listmodel; /* for all types of list box */
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkWidget *text; /* for text */
|
2006-08-28 14:29:02 +00:00
|
|
|
GtkWidget *label; /* for dlg_label_change */
|
2003-03-18 19:06:51 +00:00
|
|
|
GtkAdjustment *adj; /* for the scrollbar in a list box */
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam *sp; /* which switchable pane of the box we're in */
|
2007-01-25 19:47:36 +00:00
|
|
|
guint entrysig;
|
2003-03-22 17:00:06 +00:00
|
|
|
guint textsig;
|
2007-03-27 18:16:36 +00:00
|
|
|
int nclicks;
|
2003-03-15 15:50:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct dlgparam {
|
|
|
|
tree234 *byctrl, *bywidget;
|
|
|
|
void *data;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
struct {
|
|
|
|
unsigned char r, g, b; /* 0-255 */
|
|
|
|
bool ok;
|
|
|
|
} coloursel_result;
|
2003-03-15 15:50:42 +00:00
|
|
|
/* `flags' are set to indicate when a GTK signal handler is being called
|
|
|
|
* due to automatic processing and should not flag a user event. */
|
|
|
|
int flags;
|
2003-03-16 12:37:48 +00:00
|
|
|
struct Shortcuts *shortcuts;
|
2007-01-25 19:33:29 +00:00
|
|
|
GtkWidget *window, *cancelbutton;
|
2003-03-18 19:06:51 +00:00
|
|
|
union control *currfocus, *lastfocus;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkWidget *currtreeitem, **treeitems;
|
2003-03-18 19:06:51 +00:00
|
|
|
int ntreeitems;
|
2015-08-25 18:44:44 +00:00
|
|
|
#else
|
New array-growing macros: sgrowarray and sgrowarrayn.
The idea of these is that they centralise the common idiom along the
lines of
if (logical_array_len >= physical_array_size) {
physical_array_size = logical_array_len * 5 / 4 + 256;
array = sresize(array, physical_array_size, ElementType);
}
which happens at a zillion call sites throughout this code base, with
different random choices of the geometric factor and additive
constant, sometimes forgetting them completely, and generally doing a
lot of repeated work.
The new macro sgrowarray(array,size,n) has the semantics: here are the
array pointer and its physical size for you to modify, now please
ensure that the nth element exists, so I can write into it. And
sgrowarrayn(array,size,n,m) is the same except that it ensures that
the array has size at least n+m (so sgrowarray is just the special
case where m=1).
Now that this is a single centralised implementation that will be used
everywhere, I've also gone to more effort in the implementation, with
careful overflow checks that would have been painful to put at all the
previous call sites.
This commit also switches over every use of sresize(), apart from a
few where I really didn't think it would gain anything. A consequence
of that is that a lot of array-size variables have to have their types
changed to size_t, because the macros require that (they address-take
the size to pass to the underlying function).
2019-02-28 20:07:30 +00:00
|
|
|
size_t nselparams;
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam **selparams;
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam *curr_panel;
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
struct controlbox *ctrlbox;
|
2003-03-18 19:06:51 +00:00
|
|
|
int retval;
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
post_dialog_fn_t after;
|
|
|
|
void *afterctx;
|
2003-03-15 15:50:42 +00:00
|
|
|
};
|
|
|
|
#define FLAG_UPDATING_COMBO_LIST 1
|
2008-04-02 14:48:06 +00:00
|
|
|
#define FLAG_UPDATING_LISTBOX 2
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
enum { /* values for Shortcut.action */
|
|
|
|
SHORTCUT_EMPTY, /* no shortcut on this key */
|
|
|
|
SHORTCUT_TREE, /* focus a tree item */
|
|
|
|
SHORTCUT_FOCUS, /* focus the supplied widget */
|
|
|
|
SHORTCUT_UCTRL, /* do something sane with uctrl */
|
|
|
|
SHORTCUT_UCTRL_UP, /* uctrl is a draglist, move Up */
|
|
|
|
SHORTCUT_UCTRL_DOWN, /* uctrl is a draglist, move Down */
|
2003-03-16 12:37:48 +00:00
|
|
|
};
|
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
enum {
|
|
|
|
TREESTORE_PATH,
|
|
|
|
TREESTORE_PARAMS,
|
|
|
|
TREESTORE_NUM
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
/*
|
|
|
|
* Forward references.
|
|
|
|
*/
|
2003-03-18 19:06:51 +00:00
|
|
|
static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
|
|
|
|
gpointer data);
|
2003-03-16 12:37:48 +00:00
|
|
|
static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
2019-09-08 19:29:00 +00:00
|
|
|
int chr, int action, void *ptr);
|
2006-08-28 14:29:02 +00:00
|
|
|
static void shortcut_highlight(GtkWidget *label, int chr);
|
2008-04-04 10:56:26 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data);
|
2008-04-04 10:56:26 +00:00
|
|
|
static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data);
|
2008-04-04 10:56:26 +00:00
|
|
|
static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data);
|
2008-04-04 10:56:26 +00:00
|
|
|
static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data);
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
static void menuitem_activate(GtkMenuItem *item, gpointer data);
|
|
|
|
#endif
|
2015-08-22 13:49:02 +00:00
|
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
|
|
|
static void colourchoose_response(GtkDialog *dialog,
|
|
|
|
gint response_id, gpointer data);
|
|
|
|
#else
|
2003-03-15 15:50:42 +00:00
|
|
|
static void coloursel_ok(GtkButton *button, gpointer data);
|
|
|
|
static void coloursel_cancel(GtkButton *button, gpointer data);
|
2015-08-22 13:49:02 +00:00
|
|
|
#endif
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
static void dlgparam_destroy(GtkWidget *widget, gpointer data);
|
2018-11-03 08:25:28 +00:00
|
|
|
static int get_listitemheight(GtkWidget *widget);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
|
|
|
static int uctrl_cmp_byctrl(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct uctrl *a = (struct uctrl *)av;
|
|
|
|
struct uctrl *b = (struct uctrl *)bv;
|
|
|
|
if (a->ctrl < b->ctrl)
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1;
|
2003-03-15 15:50:42 +00:00
|
|
|
else if (a->ctrl > b->ctrl)
|
2019-09-08 19:29:00 +00:00
|
|
|
return +1;
|
2003-03-15 15:50:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uctrl_cmp_byctrl_find(void *av, void *bv)
|
|
|
|
{
|
|
|
|
union control *a = (union control *)av;
|
|
|
|
struct uctrl *b = (struct uctrl *)bv;
|
|
|
|
if (a < b->ctrl)
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1;
|
2003-03-15 15:50:42 +00:00
|
|
|
else if (a > b->ctrl)
|
2019-09-08 19:29:00 +00:00
|
|
|
return +1;
|
2003-03-15 15:50:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uctrl_cmp_bywidget(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct uctrl *a = (struct uctrl *)av;
|
|
|
|
struct uctrl *b = (struct uctrl *)bv;
|
|
|
|
if (a->toplevel < b->toplevel)
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1;
|
2003-03-15 15:50:42 +00:00
|
|
|
else if (a->toplevel > b->toplevel)
|
2019-09-08 19:29:00 +00:00
|
|
|
return +1;
|
2003-03-15 15:50:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uctrl_cmp_bywidget_find(void *av, void *bv)
|
|
|
|
{
|
|
|
|
GtkWidget *a = (GtkWidget *)av;
|
|
|
|
struct uctrl *b = (struct uctrl *)bv;
|
|
|
|
if (a < b->toplevel)
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1;
|
2003-03-15 15:50:42 +00:00
|
|
|
else if (a > b->toplevel)
|
2019-09-08 19:29:00 +00:00
|
|
|
return +1;
|
2003-03-15 15:50:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dlg_init(struct dlgparam *dp)
|
|
|
|
{
|
|
|
|
dp->byctrl = newtree234(uctrl_cmp_byctrl);
|
|
|
|
dp->bywidget = newtree234(uctrl_cmp_bywidget);
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = false;
|
2007-01-25 19:33:29 +00:00
|
|
|
dp->window = dp->cancelbutton = NULL;
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-31 11:21:07 +00:00
|
|
|
dp->treeitems = NULL;
|
2007-01-25 19:33:29 +00:00
|
|
|
dp->currtreeitem = NULL;
|
|
|
|
#endif
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
dp->curr_panel = NULL;
|
2004-01-17 14:25:36 +00:00
|
|
|
dp->flags = 0;
|
|
|
|
dp->currfocus = NULL;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dlg_cleanup(struct dlgparam *dp)
|
|
|
|
{
|
|
|
|
struct uctrl *uc;
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
freetree234(dp->byctrl); /* doesn't free the uctrls inside */
|
2003-04-09 18:46:45 +00:00
|
|
|
dp->byctrl = NULL;
|
2003-03-15 15:50:42 +00:00
|
|
|
while ( (uc = index234(dp->bywidget, 0)) != NULL) {
|
2019-09-08 19:29:00 +00:00
|
|
|
del234(dp->bywidget, uc);
|
|
|
|
sfree(uc->buttons);
|
|
|
|
sfree(uc);
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
freetree234(dp->bywidget);
|
2003-04-09 18:46:45 +00:00
|
|
|
dp->bywidget = NULL;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-18 19:06:51 +00:00
|
|
|
sfree(dp->treeitems);
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc)
|
|
|
|
{
|
|
|
|
add234(dp->byctrl, uc);
|
|
|
|
add234(dp->bywidget, uc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl)
|
|
|
|
{
|
2003-04-09 18:46:45 +00:00
|
|
|
if (!dp->byctrl)
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
2003-03-15 15:50:42 +00:00
|
|
|
return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)
|
|
|
|
{
|
|
|
|
struct uctrl *ret = NULL;
|
2003-04-09 18:46:45 +00:00
|
|
|
if (!dp->bywidget)
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
2003-03-15 15:50:42 +00:00
|
|
|
do {
|
2019-09-08 19:29:00 +00:00
|
|
|
ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
w = gtk_widget_get_parent(w);
|
2003-03-15 15:50:42 +00:00
|
|
|
} while (w);
|
|
|
|
return ret;
|
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
union control *dlg_last_focused(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-18 19:06:51 +00:00
|
|
|
if (dp->currfocus != ctrl)
|
|
|
|
return dp->currfocus;
|
|
|
|
else
|
|
|
|
return dp->lastfocus;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int which)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_RADIO);
|
|
|
|
assert(uc->buttons != NULL);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), true);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
int dlg_radiobutton_get(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_RADIO);
|
|
|
|
assert(uc->buttons != NULL);
|
|
|
|
for (i = 0; i < uc->nbuttons; i++)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i])))
|
|
|
|
return i;
|
|
|
|
return 0; /* got to return something */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool dlg_checkbox_get(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
|
|
|
|
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
2008-04-02 14:48:06 +00:00
|
|
|
GtkWidget *entry;
|
|
|
|
char *tmpstring;
|
2003-03-15 15:50:42 +00:00
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,4,0)
|
|
|
|
if (uc->combo)
|
2019-09-08 19:29:00 +00:00
|
|
|
entry = gtk_bin_get_child(GTK_BIN(uc->combo));
|
2008-05-31 19:23:45 +00:00
|
|
|
else
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2008-05-31 19:23:45 +00:00
|
|
|
entry = uc->entry;
|
|
|
|
|
|
|
|
assert(entry != NULL);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2007-01-25 19:47:36 +00:00
|
|
|
/*
|
|
|
|
* GTK 2 implements gtk_entry_set_text by means of two separate
|
|
|
|
* operations: first delete the previous text leaving the empty
|
|
|
|
* string, then insert the new text. This causes two calls to
|
|
|
|
* the "changed" signal.
|
2008-04-02 14:48:06 +00:00
|
|
|
*
|
2007-01-25 19:47:36 +00:00
|
|
|
* The first call to "changed", if allowed to proceed normally,
|
|
|
|
* will cause an EVENT_VALCHANGE event on the edit box, causing
|
|
|
|
* a call to dlg_editbox_get() which will read the empty string
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
* out of the GtkEntry - and promptly write it straight into the
|
|
|
|
* Conf structure, which is precisely where our `text' pointer
|
|
|
|
* is probably pointing, so the second editing operation will
|
|
|
|
* insert that instead of the string we originally asked for.
|
2008-04-02 14:48:06 +00:00
|
|
|
*
|
|
|
|
* Hence, we must take our own copy of the text before we do
|
|
|
|
* this.
|
2007-01-25 19:47:36 +00:00
|
|
|
*/
|
2008-04-02 14:48:06 +00:00
|
|
|
tmpstring = dupstr(text);
|
|
|
|
gtk_entry_set_text(GTK_ENTRY(entry), tmpstring);
|
|
|
|
sfree(tmpstring);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
char *dlg_editbox_get(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX);
|
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,4,0)
|
|
|
|
if (uc->combo) {
|
2019-09-08 19:29:00 +00:00
|
|
|
return dupstr(gtk_entry_get_text
|
|
|
|
(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))));
|
2008-04-02 14:48:06 +00:00
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2008-05-31 19:23:45 +00:00
|
|
|
|
|
|
|
if (uc->entry) {
|
2019-09-08 19:29:00 +00:00
|
|
|
return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry)));
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in editbox_get");
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
static void container_remove_and_destroy(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
GtkContainer *cont = GTK_CONTAINER(data);
|
|
|
|
/* gtk_container_remove will unref the widget for us; we need not. */
|
|
|
|
gtk_container_remove(cont, w);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
/* The `listbox' functions can also apply to combo boxes. */
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_listbox_clear(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
if (uc->menu) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_container_foreach(GTK_CONTAINER(uc->menu),
|
|
|
|
container_remove_and_destroy,
|
|
|
|
GTK_CONTAINER(uc->menu));
|
|
|
|
return;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
if (uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_list_clear_items(GTK_LIST(uc->list), 0, -1);
|
|
|
|
return;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2008-05-31 19:23:45 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->listmodel) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_list_store_clear(uc->listmodel);
|
|
|
|
return;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_clear");
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
if (uc->menu) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_container_remove
|
|
|
|
(GTK_CONTAINER(uc->menu),
|
|
|
|
g_list_nth_data(GTK_MENU_SHELL(uc->menu)->children, index));
|
|
|
|
return;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
if (uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);
|
|
|
|
return;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->listmodel) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreePath *path;
|
|
|
|
GtkTreeIter iter;
|
|
|
|
assert(uc->listmodel != NULL);
|
|
|
|
path = gtk_tree_path_new_from_indices(index, -1);
|
|
|
|
gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);
|
|
|
|
gtk_list_store_remove(uc->listmodel, &iter);
|
|
|
|
gtk_tree_path_free(path);
|
|
|
|
return;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_del");
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_listbox_addwithid(ctrl, dp, text, 0);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Each listbox entry may have a numeric id associated with it.
|
|
|
|
* Note that some front ends only permit a string to be stored at
|
|
|
|
* each position, which means that _if_ you put two identical
|
|
|
|
* strings in any listbox then you MUST not assign them different
|
|
|
|
* IDs and expect to get meaningful results back.
|
|
|
|
*/
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp,
|
2019-09-08 19:29:00 +00:00
|
|
|
char const *text, int id)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
/*
|
2008-04-04 10:56:26 +00:00
|
|
|
* This routine is long and complicated in both GTK 1 and 2,
|
|
|
|
* and completely different. Sigh.
|
2008-04-02 14:48:06 +00:00
|
|
|
*/
|
2008-04-04 10:56:26 +00:00
|
|
|
dp->flags |= FLAG_UPDATING_COMBO_LIST;
|
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
if (uc->menu) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* List item in a drop-down (but non-combo) list. Tabs are
|
|
|
|
* ignored; we just provide a standard menu item with the
|
|
|
|
* text.
|
|
|
|
*/
|
|
|
|
GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(uc->menu), menuitem);
|
|
|
|
gtk_widget_show(menuitem);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(menuitem), "user-data",
|
|
|
|
GINT_TO_POINTER(id));
|
|
|
|
g_signal_connect(G_OBJECT(menuitem), "activate",
|
|
|
|
G_CALLBACK(menuitem_activate), dp);
|
2019-09-08 19:29:00 +00:00
|
|
|
goto done;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
if (uc->list && uc->entry) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* List item in a combo-box list, which means the sensible
|
|
|
|
* thing to do is make it a perfectly normal label. Hence
|
|
|
|
* tabs are disregarded.
|
|
|
|
*/
|
|
|
|
GtkWidget *listitem = gtk_list_item_new_with_label(text);
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(uc->list), listitem);
|
|
|
|
gtk_widget_show(listitem);
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(listitem), "user-data",
|
|
|
|
GINT_TO_POINTER(id));
|
2019-09-08 19:29:00 +00:00
|
|
|
goto done;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* List item in a non-combo-box list box. We make all of
|
|
|
|
* these Columns containing GtkLabels. This allows us to do
|
|
|
|
* the nasty force_left hack irrespective of whether there
|
|
|
|
* are tabs in the thing.
|
|
|
|
*/
|
|
|
|
GtkWidget *listitem = gtk_list_item_new();
|
|
|
|
GtkWidget *cols = columns_new(10);
|
|
|
|
gint *percents;
|
|
|
|
int i, ncols;
|
|
|
|
|
|
|
|
/* Count the tabs in the text, and hence determine # of columns. */
|
|
|
|
ncols = 1;
|
|
|
|
for (i = 0; text[i]; i++)
|
|
|
|
if (text[i] == '\t')
|
|
|
|
ncols++;
|
|
|
|
|
|
|
|
assert(ncols <=
|
|
|
|
(uc->ctrl->listbox.ncols ? uc->ctrl->listbox.ncols : 1));
|
|
|
|
percents = snewn(ncols, gint);
|
|
|
|
percents[ncols-1] = 100;
|
|
|
|
for (i = 0; i < ncols-1; i++) {
|
|
|
|
percents[i] = uc->ctrl->listbox.percentages[i];
|
|
|
|
percents[ncols-1] -= percents[i];
|
|
|
|
}
|
|
|
|
columns_set_cols(COLUMNS(cols), ncols, percents);
|
|
|
|
sfree(percents);
|
|
|
|
|
|
|
|
for (i = 0; i < ncols; i++) {
|
|
|
|
int len = strcspn(text, "\t");
|
|
|
|
char *dup = dupprintf("%.*s", len, text);
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
text += len;
|
|
|
|
if (*text) text++;
|
|
|
|
label = gtk_label_new(dup);
|
|
|
|
sfree(dup);
|
|
|
|
|
|
|
|
columns_add(COLUMNS(cols), label, i, 1);
|
|
|
|
columns_force_left_align(COLUMNS(cols), label);
|
|
|
|
gtk_widget_show(label);
|
|
|
|
}
|
|
|
|
gtk_container_add(GTK_CONTAINER(listitem), cols);
|
|
|
|
gtk_widget_show(cols);
|
|
|
|
gtk_container_add(GTK_CONTAINER(uc->list), listitem);
|
|
|
|
gtk_widget_show(listitem);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
if (ctrl->listbox.multisel) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(listitem), "key_press_event",
|
|
|
|
G_CALLBACK(listitem_multi_key), uc->adj);
|
2008-04-04 10:56:26 +00:00
|
|
|
} else {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(listitem), "key_press_event",
|
|
|
|
G_CALLBACK(listitem_single_key), uc->adj);
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(listitem), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
|
|
|
g_signal_connect(G_OBJECT(listitem), "button_press_event",
|
|
|
|
G_CALLBACK(listitem_button_press), dp);
|
|
|
|
g_signal_connect(G_OBJECT(listitem), "button_release_event",
|
|
|
|
G_CALLBACK(listitem_button_release), dp);
|
|
|
|
g_object_set_data(G_OBJECT(listitem), "user-data",
|
|
|
|
GINT_TO_POINTER(id));
|
2019-09-08 19:29:00 +00:00
|
|
|
goto done;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
2008-05-31 19:23:45 +00:00
|
|
|
if (uc->listmodel) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeIter iter;
|
|
|
|
int i, cols;
|
|
|
|
|
|
|
|
dp->flags |= FLAG_UPDATING_LISTBOX;/* inhibit drag-list update */
|
|
|
|
gtk_list_store_append(uc->listmodel, &iter);
|
|
|
|
dp->flags &= ~FLAG_UPDATING_LISTBOX;
|
|
|
|
gtk_list_store_set(uc->listmodel, &iter, 0, id, -1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now go through text and divide it into columns at the tabs,
|
|
|
|
* as necessary.
|
|
|
|
*/
|
|
|
|
cols = (uc->ctrl->generic.type == CTRL_LISTBOX ? ctrl->listbox.ncols : 1);
|
|
|
|
cols = cols ? cols : 1;
|
|
|
|
for (i = 0; i < cols; i++) {
|
|
|
|
int collen = strcspn(text, "\t");
|
|
|
|
char *tmpstr = snewn(collen+1, char);
|
|
|
|
memcpy(tmpstr, text, collen);
|
|
|
|
tmpstr[collen] = '\0';
|
|
|
|
gtk_list_store_set(uc->listmodel, &iter, i+1, tmpstr, -1);
|
|
|
|
sfree(tmpstr);
|
|
|
|
text += collen;
|
|
|
|
if (*text) text++;
|
|
|
|
}
|
|
|
|
goto done;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_addwithid");
|
2008-05-31 19:23:45 +00:00
|
|
|
done:
|
|
|
|
dp->flags &= ~FLAG_UPDATING_COMBO_LIST;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
|
|
|
if (uc->menu || uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GList *children;
|
2015-08-08 16:29:02 +00:00
|
|
|
GObject *item;
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
|
|
|
|
uc->list));
|
2015-08-08 16:29:02 +00:00
|
|
|
item = G_OBJECT(g_list_nth_data(children, index));
|
2019-09-08 19:29:00 +00:00
|
|
|
g_list_free(children);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "user-data"));
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->listmodel) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreePath *path;
|
|
|
|
GtkTreeIter iter;
|
|
|
|
int ret;
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
path = gtk_tree_path_new_from_indices(index, -1);
|
|
|
|
gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);
|
|
|
|
gtk_tree_model_get(GTK_TREE_MODEL(uc->listmodel), &iter, 0, &ret, -1);
|
|
|
|
gtk_tree_path_free(path);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
return ret;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_getid");
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1; /* placate dataflow analysis */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* dlg_listbox_index returns <0 if no single element is selected. */
|
2018-09-13 11:58:44 +00:00
|
|
|
int dlg_listbox_index(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
|
|
|
if (uc->menu || uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GList *children;
|
|
|
|
GtkWidget *item, *activeitem;
|
|
|
|
int i;
|
|
|
|
int selected = -1;
|
|
|
|
|
|
|
|
if (uc->menu)
|
|
|
|
activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));
|
|
|
|
else
|
|
|
|
activeitem = NULL; /* unnecessarily placate gcc */
|
|
|
|
|
|
|
|
children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
|
|
|
|
uc->list));
|
|
|
|
for (i = 0; children!=NULL && (item = GTK_WIDGET(children->data))!=NULL;
|
|
|
|
i++, children = children->next) {
|
|
|
|
if (uc->menu ? activeitem == item :
|
|
|
|
GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED) {
|
|
|
|
if (selected == -1)
|
|
|
|
selected = i;
|
|
|
|
else
|
|
|
|
selected = -2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_list_free(children);
|
|
|
|
return selected < 0 ? -1 : selected;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#else
|
2008-04-02 14:48:06 +00:00
|
|
|
if (uc->combo) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* This API function already does the right thing in the
|
|
|
|
* case of no current selection.
|
|
|
|
*/
|
|
|
|
return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo));
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->treeview) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeSelection *treesel;
|
|
|
|
GtkTreePath *path;
|
|
|
|
GtkTreeModel *model;
|
|
|
|
GList *sellist;
|
|
|
|
gint *indices;
|
|
|
|
int ret;
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(uc->treeview != NULL);
|
|
|
|
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (gtk_tree_selection_count_selected_rows(treesel) != 1)
|
|
|
|
return -1;
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
sellist = gtk_tree_selection_get_selected_rows(treesel, &model);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(sellist && sellist->data);
|
|
|
|
path = sellist->data;
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (gtk_tree_path_get_depth(path) != 1) {
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
indices = gtk_tree_path_get_indices(path);
|
|
|
|
if (!indices) {
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
ret = indices[0];
|
|
|
|
}
|
|
|
|
}
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
g_list_foreach(sellist, (GFunc)gtk_tree_path_free, NULL);
|
|
|
|
g_list_free(sellist);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
return ret;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_index");
|
2019-09-08 19:29:00 +00:00
|
|
|
return -1; /* placate dataflow analysis */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
|
|
|
if (uc->menu || uc->list) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GList *children;
|
|
|
|
GtkWidget *item, *activeitem;
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
|
|
|
assert(uc->menu != NULL || uc->list != NULL);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
|
|
|
|
uc->list));
|
|
|
|
item = GTK_WIDGET(g_list_nth_data(children, index));
|
|
|
|
g_list_free(children);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (uc->menu) {
|
|
|
|
activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));
|
|
|
|
return item == activeitem;
|
|
|
|
} else {
|
|
|
|
return GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED;
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#else
|
2008-04-02 14:48:06 +00:00
|
|
|
if (uc->combo) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* This API function already does the right thing in the
|
|
|
|
* case of no current selection.
|
|
|
|
*/
|
|
|
|
return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo)) == index;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->treeview) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeSelection *treesel;
|
|
|
|
GtkTreePath *path;
|
|
|
|
bool ret;
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(uc->treeview != NULL);
|
|
|
|
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
path = gtk_tree_path_new_from_indices(index, -1);
|
|
|
|
ret = gtk_tree_selection_path_is_selected(treesel, path);
|
|
|
|
gtk_tree_path_free(path);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
return ret;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_issel");
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
return false; /* placate dataflow analysis */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.type == CTRL_LISTBOX);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
if (uc->optmenu) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_option_menu_set_history(GTK_OPTION_MENU(uc->optmenu), index);
|
|
|
|
return;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
if (uc->list) {
|
2008-04-04 10:56:26 +00:00
|
|
|
int nitems;
|
|
|
|
GList *items;
|
|
|
|
gdouble newtop, newbot;
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_list_select_item(GTK_LIST(uc->list), index);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Scroll the list box if necessary to ensure the newly
|
|
|
|
* selected item is visible.
|
|
|
|
*/
|
|
|
|
items = gtk_container_children(GTK_CONTAINER(uc->list));
|
|
|
|
nitems = g_list_length(items);
|
|
|
|
if (nitems > 0) {
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool modified = false;
|
2008-04-04 10:56:26 +00:00
|
|
|
g_list_free(items);
|
|
|
|
newtop = uc->adj->lower +
|
|
|
|
(uc->adj->upper - uc->adj->lower) * index / nitems;
|
|
|
|
newbot = uc->adj->lower +
|
|
|
|
(uc->adj->upper - uc->adj->lower) * (index+1) / nitems;
|
|
|
|
if (uc->adj->value > newtop) {
|
2018-10-29 19:50:29 +00:00
|
|
|
modified = true;
|
2008-04-04 10:56:26 +00:00
|
|
|
uc->adj->value = newtop;
|
|
|
|
} else if (uc->adj->value < newbot - uc->adj->page_size) {
|
2018-10-29 19:50:29 +00:00
|
|
|
modified = true;
|
2008-04-04 10:56:26 +00:00
|
|
|
uc->adj->value = newbot - uc->adj->page_size;
|
|
|
|
}
|
|
|
|
if (modified)
|
|
|
|
gtk_adjustment_value_changed(uc->adj);
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
#else
|
2008-04-02 14:48:06 +00:00
|
|
|
if (uc->combo) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(uc->combo), index);
|
|
|
|
return;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
if (uc->treeview) {
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeSelection *treesel;
|
|
|
|
GtkTreePath *path;
|
2005-09-21 17:09:07 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));
|
2005-09-21 17:09:07 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
path = gtk_tree_path_new_from_indices(index, -1);
|
|
|
|
gtk_tree_selection_select_path(treesel, path);
|
|
|
|
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(uc->treeview),
|
|
|
|
path, NULL, false, 0.0, 0.0);
|
|
|
|
gtk_tree_path_free(path);
|
|
|
|
return;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in listbox_select");
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_TEXT);
|
|
|
|
assert(uc->text != NULL);
|
|
|
|
|
|
|
|
gtk_label_set_text(GTK_LABEL(uc->text), text);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
|
2006-08-28 10:35:12 +00:00
|
|
|
{
|
2006-08-28 14:29:02 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
switch (uc->ctrl->generic.type) {
|
|
|
|
case CTRL_BUTTON:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
|
|
|
|
shortcut_highlight(uc->toplevel, ctrl->button.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_CHECKBOX:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
|
|
|
|
shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_RADIO:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
|
|
|
shortcut_highlight(uc->label, ctrl->radio.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_EDITBOX:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
|
|
|
shortcut_highlight(uc->label, ctrl->editbox.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_FILESELECT:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
|
|
|
shortcut_highlight(uc->label, ctrl->fileselect.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_FONTSELECT:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
|
|
|
shortcut_highlight(uc->label, ctrl->fontselect.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
case CTRL_LISTBOX:
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
|
|
|
shortcut_highlight(uc->label, ctrl->listbox.shortcut);
|
|
|
|
break;
|
2006-08-28 14:29:02 +00:00
|
|
|
default:
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in label_change");
|
2006-08-28 14:29:02 +00:00
|
|
|
}
|
2006-08-28 10:35:12 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
2012-04-13 18:02:30 +00:00
|
|
|
/* We must copy fn->path before passing it to gtk_entry_set_text.
|
|
|
|
* See comment in dlg_editbox_set() for the reasons. */
|
|
|
|
char *duppath = dupstr(fn->path);
|
2003-03-15 15:50:42 +00:00
|
|
|
assert(uc->ctrl->generic.type == CTRL_FILESELECT);
|
|
|
|
assert(uc->entry != NULL);
|
2012-04-13 18:02:30 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), duppath);
|
|
|
|
sfree(duppath);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_FILESELECT);
|
|
|
|
assert(uc->entry != NULL);
|
2011-10-02 11:01:57 +00:00
|
|
|
return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry)));
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
2012-04-13 18:02:30 +00:00
|
|
|
/* We must copy fs->name before passing it to gtk_entry_set_text.
|
|
|
|
* See comment in dlg_editbox_set() for the reasons. */
|
|
|
|
char *dupname = dupstr(fs->name);
|
2003-03-15 15:50:42 +00:00
|
|
|
assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
|
|
|
|
assert(uc->entry != NULL);
|
2012-04-13 18:02:30 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), dupname);
|
|
|
|
sfree(dupname);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
|
|
|
|
assert(uc->entry != NULL);
|
2011-10-01 17:38:59 +00:00
|
|
|
return fontspec_new(gtk_entry_get_text(GTK_ENTRY(uc->entry)));
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Bracketing a large set of updates in these two functions will
|
|
|
|
* cause the front end (if possible) to delay updating the screen
|
|
|
|
* until it's all complete, thus avoiding flicker.
|
|
|
|
*/
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_update_start(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
/*
|
|
|
|
* Apparently we can't do this at all in GTK. GtkCList supports
|
|
|
|
* freeze and thaw, but not GtkList. Bah.
|
|
|
|
*/
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_update_done(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
/*
|
|
|
|
* Apparently we can't do this at all in GTK. GtkCList supports
|
|
|
|
* freeze and thaw, but not GtkList. Bah.
|
|
|
|
*/
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_set_focus(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-18 19:06:51 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
|
|
|
switch (ctrl->generic.type) {
|
|
|
|
case CTRL_CHECKBOX:
|
|
|
|
case CTRL_BUTTON:
|
|
|
|
/* Check boxes and buttons get the focus _and_ get toggled. */
|
|
|
|
gtk_widget_grab_focus(uc->toplevel);
|
|
|
|
break;
|
|
|
|
case CTRL_FILESELECT:
|
|
|
|
case CTRL_FONTSELECT:
|
|
|
|
case CTRL_EDITBOX:
|
2019-09-08 19:29:00 +00:00
|
|
|
if (uc->entry) {
|
|
|
|
/* Anything containing an edit box gets that focused. */
|
|
|
|
gtk_widget_grab_focus(uc->entry);
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
else if (uc->combo) {
|
|
|
|
/* Failing that, there'll be a combo box. */
|
|
|
|
gtk_widget_grab_focus(uc->combo);
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2003-03-18 19:06:51 +00:00
|
|
|
break;
|
|
|
|
case CTRL_RADIO:
|
|
|
|
/*
|
|
|
|
* Radio buttons: we find the currently selected button and
|
|
|
|
* focus it.
|
|
|
|
*/
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
for (int i = 0; i < ctrl->radio.nbuttons; i++)
|
|
|
|
if (gtk_toggle_button_get_active
|
|
|
|
(GTK_TOGGLE_BUTTON(uc->buttons[i]))) {
|
|
|
|
gtk_widget_grab_focus(uc->buttons[i]);
|
|
|
|
}
|
2003-03-18 19:06:51 +00:00
|
|
|
break;
|
|
|
|
case CTRL_LISTBOX:
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2008-04-04 10:56:26 +00:00
|
|
|
if (uc->optmenu) {
|
|
|
|
gtk_widget_grab_focus(uc->optmenu);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
if (uc->combo) {
|
|
|
|
gtk_widget_grab_focus(uc->combo);
|
|
|
|
break;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (uc->list) {
|
|
|
|
/*
|
|
|
|
* For GTK-1 style list boxes, we tell it to focus one
|
|
|
|
* of its children, which appears to do the Right
|
|
|
|
* Thing.
|
|
|
|
*/
|
2008-04-04 10:56:26 +00:00
|
|
|
gtk_container_focus(GTK_CONTAINER(uc->list), GTK_DIR_TAB_FORWARD);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
if (uc->treeview) {
|
|
|
|
gtk_widget_grab_focus(uc->treeview);
|
|
|
|
break;
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad control type in set_focus");
|
2003-03-18 19:06:51 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* During event processing, you might well want to give an error
|
|
|
|
* indication to the user. dlg_beep() is a quick and easy generic
|
|
|
|
* error; dlg_error() puts up a message-box or equivalent.
|
|
|
|
*/
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_beep(dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2018-03-03 18:20:04 +00:00
|
|
|
gdk_display_beep(gdk_display_get_default());
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2003-04-10 08:53:43 +00:00
|
|
|
static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)
|
|
|
|
{
|
2015-08-08 17:02:01 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-04-10 08:53:43 +00:00
|
|
|
gint x, y, w, h, dx, dy;
|
|
|
|
GtkRequisition req;
|
|
|
|
gtk_window_set_position(GTK_WINDOW(child), GTK_WIN_POS_NONE);
|
|
|
|
gtk_widget_size_request(GTK_WIDGET(child), &req);
|
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(parent)), &x, &y);
|
|
|
|
gdk_window_get_size(gtk_widget_get_window(GTK_WIDGET(parent)), &w, &h);
|
2003-04-10 08:53:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* One corner of the transient will be offset inwards, by 1/4
|
|
|
|
* of the parent window's size, from the corresponding corner
|
|
|
|
* of the parent window. The corner will be chosen so as to
|
|
|
|
* place the transient closer to the centre of the screen; this
|
|
|
|
* should avoid transients going off the edge of the screen on
|
|
|
|
* a regular basis.
|
|
|
|
*/
|
|
|
|
if (x + w/2 < gdk_screen_width() / 2)
|
|
|
|
dx = x + w/4; /* work from left edges */
|
|
|
|
else
|
|
|
|
dx = x + 3*w/4 - req.width; /* work from right edges */
|
|
|
|
if (y + h/2 < gdk_screen_height() / 2)
|
|
|
|
dy = y + h/4; /* work from top edges */
|
|
|
|
else
|
|
|
|
dy = y + 3*h/4 - req.height; /* work from bottom edges */
|
|
|
|
gtk_widget_set_uposition(GTK_WIDGET(child), dx, dy);
|
2015-08-08 17:02:01 +00:00
|
|
|
#endif
|
2003-04-10 08:53:43 +00:00
|
|
|
}
|
|
|
|
|
2017-11-26 17:07:47 +00:00
|
|
|
void trivial_post_dialog_fn(void *vctx, int result)
|
2017-11-26 17:05:51 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_error_msg(dlgparam *dp, const char *msg)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2017-11-26 17:05:51 +00:00
|
|
|
create_message_box(
|
|
|
|
dp->window, "Error", msg,
|
|
|
|
string_width("Some sort of text about a config-box error message"),
|
2018-10-29 19:50:29 +00:00
|
|
|
false, &buttons_ok, trivial_post_dialog_fn, NULL);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function signals to the front end that the dialog's
|
|
|
|
* processing is completed, and passes an integer value (typically
|
|
|
|
* a success status).
|
|
|
|
*/
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_end(dlgparam *dp, int value)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-18 19:06:51 +00:00
|
|
|
dp->retval = value;
|
2003-03-29 19:52:50 +00:00
|
|
|
gtk_widget_destroy(dp->window);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_refresh(union control *ctrl, dlgparam *dp)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc;
|
|
|
|
|
|
|
|
if (ctrl) {
|
2019-09-08 19:29:00 +00:00
|
|
|
if (ctrl->generic.handler != NULL)
|
|
|
|
ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
|
2003-03-15 15:50:42 +00:00
|
|
|
} else {
|
2019-09-08 19:29:00 +00:00
|
|
|
int i;
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
for (i = 0; (uc = index234(dp->byctrl, i)) != NULL; i++) {
|
|
|
|
assert(uc->ctrl != NULL);
|
|
|
|
if (uc->ctrl->generic.handler != NULL)
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp,
|
|
|
|
dp->data, EVENT_REFRESH);
|
|
|
|
}
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
|
2015-08-22 13:49:02 +00:00
|
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
|
|
|
GtkWidget *coloursel =
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_color_chooser_dialog_new("Select a colour",
|
2015-08-22 13:49:02 +00:00
|
|
|
GTK_WINDOW(dp->window));
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(coloursel), false);
|
2015-08-22 13:49:02 +00:00
|
|
|
#else
|
|
|
|
GtkWidget *okbutton, *cancelbutton;
|
2003-03-15 15:50:42 +00:00
|
|
|
GtkWidget *coloursel =
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_color_selection_dialog_new("Select a colour");
|
2003-03-15 15:50:42 +00:00
|
|
|
GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);
|
2015-08-08 14:53:43 +00:00
|
|
|
GtkColorSelection *cs = GTK_COLOR_SELECTION
|
|
|
|
(gtk_color_selection_dialog_get_color_selection(ccs));
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_color_selection_set_has_opacity_control(cs, false);
|
2015-08-22 13:49:02 +00:00
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = false;
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_window_set_modal(GTK_WINDOW(coloursel), true);
|
2015-08-08 16:32:15 +00:00
|
|
|
|
2015-08-22 13:49:02 +00:00
|
|
|
#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)
|
2015-08-08 16:32:15 +00:00
|
|
|
{
|
|
|
|
GdkColor col;
|
|
|
|
col.red = r * 0x0101;
|
|
|
|
col.green = g * 0x0101;
|
|
|
|
col.blue = b * 0x0101;
|
|
|
|
gtk_color_selection_set_current_color(cs, &col);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
gdouble cvals[4];
|
|
|
|
cvals[0] = r / 255.0;
|
|
|
|
cvals[1] = g / 255.0;
|
|
|
|
cvals[2] = b / 255.0;
|
2019-09-08 19:29:00 +00:00
|
|
|
cvals[3] = 1.0; /* fully opaque! */
|
2015-08-08 16:32:15 +00:00
|
|
|
gtk_color_selection_set_color(cs, cvals);
|
|
|
|
}
|
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2015-08-22 13:49:02 +00:00
|
|
|
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
|
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
g_object_get(G_OBJECT(ccs),
|
|
|
|
"ok-button", &okbutton,
|
|
|
|
"cancel-button", &cancelbutton,
|
|
|
|
(const char *)NULL);
|
|
|
|
#else
|
|
|
|
okbutton = ccs->ok_button;
|
|
|
|
cancelbutton = ccs->cancel_button;
|
|
|
|
#endif
|
|
|
|
g_object_set_data(G_OBJECT(okbutton), "user-data",
|
2019-09-08 19:29:00 +00:00
|
|
|
(gpointer)coloursel);
|
2015-08-08 14:53:43 +00:00
|
|
|
g_object_set_data(G_OBJECT(cancelbutton), "user-data",
|
2019-09-08 19:29:00 +00:00
|
|
|
(gpointer)coloursel);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(okbutton), "clicked",
|
|
|
|
G_CALLBACK(coloursel_ok), (gpointer)dp);
|
|
|
|
g_signal_connect(G_OBJECT(cancelbutton), "clicked",
|
|
|
|
G_CALLBACK(coloursel_cancel), (gpointer)dp);
|
|
|
|
g_signal_connect_swapped(G_OBJECT(okbutton), "clicked",
|
|
|
|
G_CALLBACK(gtk_widget_destroy),
|
|
|
|
(gpointer)coloursel);
|
|
|
|
g_signal_connect_swapped(G_OBJECT(cancelbutton), "clicked",
|
|
|
|
G_CALLBACK(gtk_widget_destroy),
|
|
|
|
(gpointer)coloursel);
|
2015-08-22 13:49:02 +00:00
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
gtk_widget_show(coloursel);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool dlg_coloursel_results(union control *ctrl, dlgparam *dp,
|
|
|
|
int *r, int *g, int *b)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-15 15:50:42 +00:00
|
|
|
if (dp->coloursel_result.ok) {
|
2019-09-08 19:29:00 +00:00
|
|
|
*r = dp->coloursel_result.r;
|
|
|
|
*g = dp->coloursel_result.g;
|
|
|
|
*b = dp->coloursel_result.b;
|
|
|
|
return true;
|
2003-03-15 15:50:42 +00:00
|
|
|
} else
|
2019-09-08 19:29:00 +00:00
|
|
|
return false;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* Signal handlers while the dialog box is active.
|
|
|
|
*/
|
|
|
|
|
2003-03-18 19:06:51 +00:00
|
|
|
static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, widget);
|
|
|
|
union control *focus;
|
|
|
|
|
|
|
|
if (uc && uc->ctrl)
|
|
|
|
focus = uc->ctrl;
|
|
|
|
else
|
|
|
|
focus = NULL;
|
|
|
|
|
|
|
|
if (focus != dp->currfocus) {
|
|
|
|
dp->lastfocus = dp->currfocus;
|
|
|
|
dp->currfocus = focus;
|
|
|
|
}
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-18 19:06:51 +00:00
|
|
|
}
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
static void button_clicked(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void button_toggled(GtkToggleButton *tb, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tb));
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
|
|
|
|
}
|
|
|
|
|
2007-01-25 19:47:36 +00:00
|
|
|
static gboolean editbox_key(GtkWidget *widget, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2003-03-16 12:37:48 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* GtkEntry has a nasty habit of eating the Return key, which
|
|
|
|
* is unhelpful since it doesn't actually _do_ anything with it
|
|
|
|
* (it calls gtk_widget_activate, but our edit boxes never need
|
|
|
|
* activating). So I catch Return before GtkEntry sees it, and
|
|
|
|
* pass it straight on to the parent widget. Effect: hitting
|
|
|
|
* Return in an edit box will now activate the default button
|
|
|
|
* in the dialog just like it will everywhere else.
|
|
|
|
*/
|
2015-08-08 14:53:43 +00:00
|
|
|
GtkWidget *parent = gtk_widget_get_parent(widget);
|
2015-08-08 15:23:54 +00:00
|
|
|
if (event->keyval == GDK_KEY_Return && parent != NULL) {
|
2019-09-08 19:29:00 +00:00
|
|
|
gboolean return_val;
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
|
2019-09-08 19:29:00 +00:00
|
|
|
g_signal_emit_by_name(G_OBJECT(parent), "key_press_event",
|
2015-08-08 16:29:02 +00:00
|
|
|
event, &return_val);
|
2019-09-08 19:29:00 +00:00
|
|
|
return return_val;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
static void editbox_changed(GtkEditable *ed, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
if (!(dp->flags & FLAG_UPDATING_COMBO_LIST)) {
|
2019-09-08 19:29:00 +00:00
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-25 19:47:36 +00:00
|
|
|
static gboolean editbox_lostfocus(GtkWidget *ed, GdkEventFocus *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2003-03-15 15:50:42 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH);
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 10:56:26 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GTK 1 list box event handlers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static gboolean listitem_key(GtkWidget *item, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data, bool multiple)
|
2008-04-04 10:56:26 +00:00
|
|
|
{
|
|
|
|
GtkAdjustment *adj = GTK_ADJUSTMENT(data);
|
|
|
|
|
|
|
|
if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
|
|
|
|
event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
|
|
|
|
event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
|
|
|
|
event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down) {
|
|
|
|
/*
|
|
|
|
* Up, Down, PgUp or PgDn have been pressed on a ListItem
|
|
|
|
* in a list box. So, if the list box is single-selection:
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
2008-04-04 10:56:26 +00:00
|
|
|
* - if the list item in question isn't already selected,
|
|
|
|
* we simply select it.
|
|
|
|
* - otherwise, we find the next one (or next
|
|
|
|
* however-far-away) in whichever direction we're going,
|
|
|
|
* and select that.
|
|
|
|
* + in this case, we must also fiddle with the
|
|
|
|
* scrollbar to ensure the newly selected item is
|
|
|
|
* actually visible.
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
2008-04-04 10:56:26 +00:00
|
|
|
* If it's multiple-selection, we do all of the above
|
|
|
|
* except actually selecting anything, so we move the focus
|
|
|
|
* and fiddle the scrollbar to follow it.
|
|
|
|
*/
|
|
|
|
GtkWidget *list = item->parent;
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(item), "key_press_event");
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
if (!multiple &&
|
|
|
|
GTK_WIDGET_STATE(item) != GTK_STATE_SELECTED) {
|
|
|
|
gtk_list_select_child(GTK_LIST(list), item);
|
|
|
|
} else {
|
|
|
|
int direction =
|
|
|
|
(event->keyval==GDK_Up || event->keyval==GDK_KP_Up ||
|
|
|
|
event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up)
|
|
|
|
? -1 : +1;
|
|
|
|
int step =
|
2019-09-08 19:29:00 +00:00
|
|
|
(event->keyval==GDK_Page_Down ||
|
2008-04-04 10:56:26 +00:00
|
|
|
event->keyval==GDK_KP_Page_Down ||
|
|
|
|
event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up)
|
|
|
|
? 2 : 1;
|
|
|
|
int i, n;
|
|
|
|
GList *children, *chead;
|
|
|
|
|
|
|
|
chead = children = gtk_container_children(GTK_CONTAINER(list));
|
|
|
|
|
|
|
|
n = g_list_length(children);
|
|
|
|
|
|
|
|
if (step == 2) {
|
|
|
|
/*
|
|
|
|
* Figure out how many list items to a screenful,
|
|
|
|
* and adjust the step appropriately.
|
|
|
|
*/
|
|
|
|
step = 0.5 + adj->page_size * n / (adj->upper - adj->lower);
|
|
|
|
step--; /* go by one less than that */
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (children != NULL) {
|
|
|
|
if (item == children->data)
|
|
|
|
break;
|
|
|
|
children = children->next;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (step > 0) {
|
|
|
|
if (direction < 0 && i > 0)
|
|
|
|
children = children->prev, i--;
|
|
|
|
else if (direction > 0 && i < n-1)
|
|
|
|
children = children->next, i++;
|
|
|
|
step--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (children && children->data) {
|
|
|
|
if (!multiple)
|
|
|
|
gtk_list_select_child(GTK_LIST(list),
|
|
|
|
GTK_WIDGET(children->data));
|
|
|
|
gtk_widget_grab_focus(GTK_WIDGET(children->data));
|
|
|
|
gtk_adjustment_clamp_page
|
|
|
|
(adj,
|
|
|
|
adj->lower + (adj->upper-adj->lower) * i / n,
|
|
|
|
adj->lower + (adj->upper-adj->lower) * (i+1) / n);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free(chead);
|
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2008-04-04 10:56:26 +00:00
|
|
|
{
|
2018-10-29 19:50:29 +00:00
|
|
|
return listitem_key(item, event, data, false);
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2008-04-04 10:56:26 +00:00
|
|
|
{
|
2018-10-29 19:50:29 +00:00
|
|
|
return listitem_key(item, event, data, true);
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2008-04-04 10:56:26 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
|
|
|
|
switch (event->type) {
|
|
|
|
default:
|
|
|
|
case GDK_BUTTON_PRESS: uc->nclicks = 1; break;
|
|
|
|
case GDK_2BUTTON_PRESS: uc->nclicks = 2; break;
|
|
|
|
case GDK_3BUTTON_PRESS: uc->nclicks = 3; break;
|
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2008-04-04 10:56:26 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
|
|
|
|
if (uc->nclicks>1) {
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void list_selchange(GtkList *list, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(list));
|
|
|
|
if (!uc) return;
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draglist_move(struct dlgparam *dp, struct uctrl *uc, int direction)
|
|
|
|
{
|
|
|
|
int index = dlg_listbox_index(uc->ctrl, dp);
|
|
|
|
GList *children = gtk_container_children(GTK_CONTAINER(uc->list));
|
|
|
|
GtkWidget *child;
|
|
|
|
|
|
|
|
if ((index < 0) ||
|
2019-09-08 19:29:00 +00:00
|
|
|
(index == 0 && direction < 0) ||
|
|
|
|
(index == g_list_length(children)-1 && direction > 0)) {
|
|
|
|
gdk_display_beep(gdk_display_get_default());
|
|
|
|
return;
|
2008-04-04 10:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
child = g_list_nth_data(children, index);
|
|
|
|
gtk_widget_ref(child);
|
|
|
|
gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);
|
|
|
|
g_list_free(children);
|
|
|
|
|
|
|
|
children = NULL;
|
|
|
|
children = g_list_append(children, child);
|
|
|
|
gtk_list_insert_items(GTK_LIST(uc->list), children, index + direction);
|
|
|
|
gtk_list_select_item(GTK_LIST(uc->list), index + direction);
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draglist_up(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
|
|
|
|
draglist_move(dp, uc, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draglist_down(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
|
|
|
|
draglist_move(dp, uc, +1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !GTK_CHECK_VERSION(2,0,0) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GTK 2 list box event handlers.
|
|
|
|
*/
|
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
static void listbox_doubleclick(GtkTreeView *treeview, GtkTreePath *path,
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeViewColumn *column, gpointer data)
|
2003-03-15 15:50:42 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2008-04-02 14:48:06 +00:00
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(treeview));
|
|
|
|
if (uc)
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
static void listbox_selchange(GtkTreeSelection *treeselection,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2003-03-15 15:50:42 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2008-04-02 14:48:06 +00:00
|
|
|
GtkTreeView *tree = gtk_tree_selection_get_tree_view(treeselection);
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));
|
|
|
|
if (uc)
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
struct draglist_valchange_ctx {
|
|
|
|
struct uctrl *uc;
|
|
|
|
struct dlgparam *dp;
|
|
|
|
};
|
|
|
|
|
|
|
|
static gboolean draglist_valchange(gpointer data)
|
2003-03-15 15:50:42 +00:00
|
|
|
{
|
2008-04-02 14:48:06 +00:00
|
|
|
struct draglist_valchange_ctx *ctx =
|
2019-09-08 19:29:00 +00:00
|
|
|
(struct draglist_valchange_ctx *)data;
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
ctx->uc->ctrl->generic.handler(ctx->uc->ctrl, ctx->dp,
|
2019-09-08 19:29:00 +00:00
|
|
|
ctx->dp->data, EVENT_VALCHANGE);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
sfree(ctx);
|
2003-03-18 19:06:51 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
static void listbox_reorder(GtkTreeModel *treemodel, GtkTreePath *path,
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeIter *iter, gpointer data)
|
2003-03-15 15:50:42 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2008-04-02 14:48:06 +00:00
|
|
|
gpointer tree;
|
|
|
|
struct uctrl *uc;
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2008-04-02 14:48:06 +00:00
|
|
|
if (dp->flags & FLAG_UPDATING_LISTBOX)
|
2019-09-08 19:29:00 +00:00
|
|
|
return; /* not a user drag operation */
|
2008-04-02 14:48:06 +00:00
|
|
|
|
|
|
|
tree = g_object_get_data(G_OBJECT(treemodel), "user-data");
|
|
|
|
uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));
|
|
|
|
if (uc) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* We should cause EVENT_VALCHANGE on the list box, now
|
|
|
|
* that its rows have been reordered. However, the GTK 2
|
|
|
|
* docs say that at the point this signal is received the
|
|
|
|
* new row might not have actually been filled in yet.
|
|
|
|
*
|
|
|
|
* (So what smegging use is it then, eh? Don't suppose it
|
|
|
|
* occurred to you at any point that letting the
|
|
|
|
* application know _after_ the reordering was compelete
|
|
|
|
* might be helpful to someone?)
|
|
|
|
*
|
|
|
|
* To get round this, I schedule an idle function, which I
|
|
|
|
* hope won't be called until the main event loop is
|
|
|
|
* re-entered after the drag-and-drop handler has finished
|
|
|
|
* furtling with the list store.
|
|
|
|
*/
|
|
|
|
struct draglist_valchange_ctx *ctx =
|
|
|
|
snew(struct draglist_valchange_ctx);
|
|
|
|
ctx->uc = uc;
|
|
|
|
ctx->dp = dp;
|
|
|
|
g_idle_add(draglist_valchange, ctx);
|
2008-04-02 14:48:06 +00:00
|
|
|
}
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif /* !GTK_CHECK_VERSION(2,0,0) */
|
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
|
|
|
|
|
|
|
static void menuitem_activate(GtkMenuItem *item, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
GtkWidget *menushell = GTK_WIDGET(item)->parent;
|
2015-08-08 16:29:02 +00:00
|
|
|
gpointer optmenu = g_object_get_data(G_OBJECT(menushell), "user-data");
|
2008-05-31 19:23:45 +00:00
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(optmenu));
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static void droplist_selchange(GtkComboBox *combo, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(combo));
|
|
|
|
if (uc)
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
|
2008-05-31 19:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !GTK_CHECK_VERSION(2,4,0) */
|
|
|
|
|
2015-08-08 16:34:25 +00:00
|
|
|
#ifdef USE_GTK_FILE_CHOOSER_DIALOG
|
|
|
|
static void filechoose_response(GtkDialog *dialog, gint response,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
/* struct dlgparam *dp = (struct dlgparam *)data; */
|
|
|
|
struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data");
|
|
|
|
if (response == GTK_RESPONSE_ACCEPT) {
|
|
|
|
gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
|
|
|
|
g_free(name);
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
|
|
}
|
|
|
|
#else
|
2003-03-15 15:50:42 +00:00
|
|
|
static void filesel_ok(GtkButton *button, gpointer data)
|
|
|
|
{
|
2003-03-29 19:52:50 +00:00
|
|
|
/* struct dlgparam *dp = (struct dlgparam *)data; */
|
2015-08-08 16:29:02 +00:00
|
|
|
gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data");
|
|
|
|
struct uctrl *uc = g_object_get_data(G_OBJECT(filesel), "user-data");
|
2007-01-25 19:33:29 +00:00
|
|
|
const char *name = gtk_file_selection_get_filename
|
2019-09-08 19:29:00 +00:00
|
|
|
(GTK_FILE_SELECTION(filesel));
|
2003-03-15 15:50:42 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
|
|
|
|
}
|
2015-08-08 16:34:25 +00:00
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
|
|
|
|
static void fontsel_ok(GtkButton *button, gpointer data)
|
|
|
|
{
|
2003-03-29 19:52:50 +00:00
|
|
|
/* struct dlgparam *dp = (struct dlgparam *)data; */
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
gpointer fontsel = g_object_get_data(G_OBJECT(button), "user-data");
|
|
|
|
struct uctrl *uc = g_object_get_data(G_OBJECT(fontsel), "user-data");
|
2008-04-04 10:56:26 +00:00
|
|
|
const char *name = gtk_font_selection_dialog_get_font_name
|
2019-09-08 19:29:00 +00:00
|
|
|
(GTK_FONT_SELECTION_DIALOG(fontsel));
|
2008-04-04 10:56:26 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
unifontsel *fontsel = (unifontsel *)g_object_get_data
|
|
|
|
(G_OBJECT(button), "user-data");
|
2008-03-25 21:49:14 +00:00
|
|
|
struct uctrl *uc = (struct uctrl *)fontsel->user_data;
|
|
|
|
char *name = unifontsel_get_name(fontsel);
|
2008-03-29 14:21:25 +00:00
|
|
|
assert(name); /* should always be ok after OK pressed */
|
2003-03-15 15:50:42 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
|
2008-03-25 21:49:14 +00:00
|
|
|
sfree(name);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
2015-08-22 13:49:02 +00:00
|
|
|
#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);
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = true;
|
2015-08-22 13:49:02 +00:00
|
|
|
} else {
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = false;
|
2015-08-22 13:49:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
|
|
|
|
|
|
|
|
gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* GTK 1/2 coloursel response handlers */
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
static void coloursel_ok(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2015-08-08 16:29:02 +00:00
|
|
|
gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data");
|
|
|
|
struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data");
|
2015-08-22 13:49:02 +00:00
|
|
|
|
2015-08-08 16:32:15 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
{
|
2015-08-22 13:49:02 +00:00
|
|
|
GtkColorSelection *cs = GTK_COLOR_SELECTION
|
|
|
|
(gtk_color_selection_dialog_get_color_selection
|
|
|
|
(GTK_COLOR_SELECTION_DIALOG(coloursel)));
|
2015-08-08 16:32:15 +00:00
|
|
|
GdkColor col;
|
|
|
|
gtk_color_selection_get_current_color(cs, &col);
|
|
|
|
dp->coloursel_result.r = col.red / 0x0100;
|
|
|
|
dp->coloursel_result.g = col.green / 0x0100;
|
|
|
|
dp->coloursel_result.b = col.blue / 0x0100;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
{
|
2015-08-22 13:49:02 +00:00
|
|
|
GtkColorSelection *cs = GTK_COLOR_SELECTION
|
|
|
|
(gtk_color_selection_dialog_get_color_selection
|
|
|
|
(GTK_COLOR_SELECTION_DIALOG(coloursel)));
|
2015-08-08 16:32:15 +00:00
|
|
|
gdouble cvals[4];
|
|
|
|
gtk_color_selection_get_color(cs, cvals);
|
|
|
|
dp->coloursel_result.r = (int) (255 * cvals[0]);
|
|
|
|
dp->coloursel_result.g = (int) (255 * cvals[1]);
|
|
|
|
dp->coloursel_result.b = (int) (255 * cvals[2]);
|
|
|
|
}
|
|
|
|
#endif
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = true;
|
2003-03-15 15:50:42 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void coloursel_cancel(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2015-08-08 16:29:02 +00:00
|
|
|
gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data");
|
|
|
|
struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data");
|
2018-10-29 19:50:29 +00:00
|
|
|
dp->coloursel_result.ok = false;
|
2003-03-15 15:50:42 +00:00
|
|
|
uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
|
|
|
|
}
|
|
|
|
|
2015-08-22 13:49:02 +00:00
|
|
|
#endif /* end of coloursel response handlers */
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
static void filefont_clicked(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
|
|
|
|
|
|
|
|
if (uc->ctrl->generic.type == CTRL_FILESELECT) {
|
2015-08-08 16:34:25 +00:00
|
|
|
#ifdef USE_GTK_FILE_CHOOSER_DIALOG
|
|
|
|
GtkWidget *filechoose = gtk_file_chooser_dialog_new
|
|
|
|
(uc->ctrl->fileselect.title, GTK_WINDOW(dp->window),
|
|
|
|
(uc->ctrl->fileselect.for_writing ?
|
|
|
|
GTK_FILE_CHOOSER_ACTION_SAVE :
|
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN),
|
2015-08-22 12:50:56 +00:00
|
|
|
STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL,
|
|
|
|
STANDARD_OPEN_LABEL, GTK_RESPONSE_ACCEPT,
|
2015-08-08 16:34:25 +00:00
|
|
|
(const gchar *)NULL);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_modal(GTK_WINDOW(filechoose), true);
|
|
|
|
g_object_set_data(G_OBJECT(filechoose), "user-data", (gpointer)uc);
|
|
|
|
g_signal_connect(G_OBJECT(filechoose), "response",
|
2015-08-08 16:34:25 +00:00
|
|
|
G_CALLBACK(filechoose_response), (gpointer)dp);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_show(filechoose);
|
2015-08-08 16:34:25 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkWidget *filesel =
|
|
|
|
gtk_file_selection_new(uc->ctrl->fileselect.title);
|
|
|
|
gtk_window_set_modal(GTK_WINDOW(filesel), true);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data
|
|
|
|
(G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
|
2019-09-08 19:29:00 +00:00
|
|
|
(gpointer)filesel);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(filesel), "user-data", (gpointer)uc);
|
|
|
|
g_signal_connect
|
|
|
|
(G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
|
|
|
|
G_CALLBACK(filesel_ok), (gpointer)dp);
|
|
|
|
g_signal_connect_swapped
|
|
|
|
(G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
|
|
|
|
G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
|
|
|
|
g_signal_connect_swapped
|
|
|
|
(G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
|
|
|
|
G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_show(filesel);
|
2015-08-08 16:34:25 +00:00
|
|
|
#endif
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (uc->ctrl->generic.type == CTRL_FONTSELECT) {
|
2007-01-25 19:33:29 +00:00
|
|
|
const gchar *fontname = gtk_entry_get_text(GTK_ENTRY(uc->entry));
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Use the GTK 1 standard font selector.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gchar *spacings[] = { "c", "m", NULL };
|
|
|
|
GtkWidget *fontsel =
|
|
|
|
gtk_font_selection_dialog_new("Select a font");
|
|
|
|
gtk_window_set_modal(GTK_WINDOW(fontsel), true);
|
|
|
|
gtk_font_selection_dialog_set_filter
|
|
|
|
(GTK_FONT_SELECTION_DIALOG(fontsel),
|
|
|
|
GTK_FONT_FILTER_BASE, GTK_FONT_ALL,
|
|
|
|
NULL, NULL, NULL, NULL, spacings, NULL);
|
|
|
|
if (!gtk_font_selection_dialog_set_font_name
|
|
|
|
(GTK_FONT_SELECTION_DIALOG(fontsel), fontname)) {
|
2008-04-04 10:56:26 +00:00
|
|
|
/*
|
|
|
|
* If the font name wasn't found as it was, try opening
|
|
|
|
* it and extracting its FONT property. This should
|
|
|
|
* have the effect of mapping short aliases into true
|
|
|
|
* XLFDs.
|
|
|
|
*/
|
|
|
|
GdkFont *font = gdk_font_load(fontname);
|
|
|
|
if (font) {
|
|
|
|
XFontStruct *xfs = GDK_FONT_XFONT(font);
|
Basic support for running under GDK Wayland back end.
GTK 3 PuTTY/pterm has always assumed that if it was compiled with
_support_ for talking to the raw X11 layer underneath GTK and GDK,
then it was entitled to expect that raw X11 layer to exist at all
times, i.e. that GDK_DISPLAY_XDISPLAY would return a meaningful X
display that it could do useful things with. So if you ran it over the
GDK Wayland backend, it would immediately segfault.
Modern GTK applications need to cope with multiple GDK backends at run
time. It's fine for GTK PuTTY to _contain_ the code to find and use
underlying X11 primitives like the display and the X window id, but it
should be prepared to find that it's running on Wayland (or something
else again!) so those functions don't return anything useful - in
which case it should degrade gracefully to the subset of functionality
that can be accessed through backend-independent GTK calls.
Accordingly, I've centralised the use of GDK_DISPLAY_XDISPLAY into a
support function get_x_display() in gtkmisc.c, which starts by
checking that there actually is one first. All previous direct uses of
GDK_*_XDISPLAY now go via that function, and check the result for NULL
afterwards. (To save faffing about calling that function too many
times, I'm also caching the display pointer in more places, and
passing it as an extra argument to various subfunctions, mostly in
gtkfont.c.)
Similarly, the get_windowid() function that retrieves the window id to
put in the environment of pterm's child process has to be prepared for
there not to be a window id.
This isn't a complete fix for all Wayland-related problems. The other
one I'm currently aware of is that the default font is "server:fixed",
which is a bad default now that it won't be available on all backends.
And I expect that further problems will show up with more testing. But
it's a start.
2018-05-09 08:18:20 +00:00
|
|
|
Display *disp = get_x11_display();
|
2008-04-04 10:56:26 +00:00
|
|
|
Atom fontprop = XInternAtom(disp, "FONT", False);
|
|
|
|
unsigned long ret;
|
Basic support for running under GDK Wayland back end.
GTK 3 PuTTY/pterm has always assumed that if it was compiled with
_support_ for talking to the raw X11 layer underneath GTK and GDK,
then it was entitled to expect that raw X11 layer to exist at all
times, i.e. that GDK_DISPLAY_XDISPLAY would return a meaningful X
display that it could do useful things with. So if you ran it over the
GDK Wayland backend, it would immediately segfault.
Modern GTK applications need to cope with multiple GDK backends at run
time. It's fine for GTK PuTTY to _contain_ the code to find and use
underlying X11 primitives like the display and the X window id, but it
should be prepared to find that it's running on Wayland (or something
else again!) so those functions don't return anything useful - in
which case it should degrade gracefully to the subset of functionality
that can be accessed through backend-independent GTK calls.
Accordingly, I've centralised the use of GDK_DISPLAY_XDISPLAY into a
support function get_x_display() in gtkmisc.c, which starts by
checking that there actually is one first. All previous direct uses of
GDK_*_XDISPLAY now go via that function, and check the result for NULL
afterwards. (To save faffing about calling that function too many
times, I'm also caching the display pointer in more places, and
passing it as an extra argument to various subfunctions, mostly in
gtkfont.c.)
Similarly, the get_windowid() function that retrieves the window id to
put in the environment of pterm's child process has to be prepared for
there not to be a window id.
This isn't a complete fix for all Wayland-related problems. The other
one I'm currently aware of is that the default font is "server:fixed",
which is a bad default now that it won't be available on all backends.
And I expect that further problems will show up with more testing. But
it's a start.
2018-05-09 08:18:20 +00:00
|
|
|
|
|
|
|
assert(disp); /* this is GTK1! */
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gdk_font_ref(font);
|
2008-04-04 10:56:26 +00:00
|
|
|
if (XGetFontProperty(xfs, fontprop, &ret)) {
|
|
|
|
char *name = XGetAtomName(disp, (Atom)ret);
|
|
|
|
if (name)
|
|
|
|
gtk_font_selection_dialog_set_font_name
|
|
|
|
(GTK_FONT_SELECTION_DIALOG(fontsel), name);
|
|
|
|
}
|
|
|
|
gdk_font_unref(font);
|
|
|
|
}
|
|
|
|
}
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data
|
|
|
|
(G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
|
2019-09-08 19:29:00 +00:00
|
|
|
"user-data", (gpointer)fontsel);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(fontsel), "user-data", (gpointer)uc);
|
|
|
|
g_signal_connect
|
|
|
|
(G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
|
|
|
|
"clicked", G_CALLBACK(fontsel_ok), (gpointer)dp);
|
|
|
|
g_signal_connect_swapped
|
|
|
|
(G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
|
|
|
|
"clicked", G_CALLBACK(gtk_widget_destroy),
|
2019-09-08 19:29:00 +00:00
|
|
|
(gpointer)fontsel);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect_swapped
|
|
|
|
(G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button),
|
|
|
|
"clicked", G_CALLBACK(gtk_widget_destroy),
|
2019-09-08 19:29:00 +00:00
|
|
|
(gpointer)fontsel);
|
|
|
|
gtk_widget_show(fontsel);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
#else /* !GTK_CHECK_VERSION(2,0,0) */
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Use the unifontsel code provided in gtkfont.c.
|
|
|
|
*/
|
|
|
|
|
|
|
|
unifontsel *fontsel = unifontsel_new("Select a font");
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_modal(fontsel->window, true);
|
|
|
|
unifontsel_set_name(fontsel, fontname);
|
2008-03-25 21:49:14 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(fontsel->ok_button),
|
|
|
|
"user-data", (gpointer)fontsel);
|
2019-09-08 19:29:00 +00:00
|
|
|
fontsel->user_data = uc;
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(fontsel->ok_button), "clicked",
|
|
|
|
G_CALLBACK(fontsel_ok), (gpointer)dp);
|
|
|
|
g_signal_connect_swapped(G_OBJECT(fontsel->ok_button), "clicked",
|
|
|
|
G_CALLBACK(unifontsel_destroy),
|
|
|
|
(gpointer)fontsel);
|
|
|
|
g_signal_connect_swapped(G_OBJECT(fontsel->cancel_button),"clicked",
|
|
|
|
G_CALLBACK(unifontsel_destroy),
|
|
|
|
(gpointer)fontsel);
|
2008-03-25 21:49:14 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_show(GTK_WIDGET(fontsel->window));
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
#endif /* !GTK_CHECK_VERSION(2,0,0) */
|
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-23 14:01:07 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
2003-03-22 17:00:06 +00:00
|
|
|
static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2003-03-22 17:00:06 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
struct uctrl *uc = dlg_find_bywidget(dp, widget);
|
|
|
|
|
2015-08-08 17:02:01 +00:00
|
|
|
gtk_widget_set_size_request(uc->text, alloc->width, -1);
|
2003-03-22 17:00:06 +00:00
|
|
|
gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_handler_disconnect(G_OBJECT(uc->text), uc->textsig);
|
2003-03-22 17:00:06 +00:00
|
|
|
}
|
2015-08-23 14:01:07 +00:00
|
|
|
#endif
|
2003-03-22 17:00:06 +00:00
|
|
|
|
2003-03-15 15:50:42 +00:00
|
|
|
/* ----------------------------------------------------------------------
|
2003-03-13 19:52:28 +00:00
|
|
|
* This function does the main layout work: it reads a controlset,
|
|
|
|
* it creates the relevant GTK controls, and returns a GtkWidget
|
|
|
|
* containing the result. (This widget might be a title of some
|
|
|
|
* sort, it might be a Columns containing many controls, or it
|
|
|
|
* might be a GtkFrame containing a Columns; whatever it is, it's
|
|
|
|
* definitely a GtkWidget and should probably be added to a
|
|
|
|
* GtkVbox.)
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
2003-03-16 12:37:48 +00:00
|
|
|
* `win' is required for setting the default button. If it is
|
|
|
|
* non-NULL, all buttons created will be default-capable (so they
|
|
|
|
* have extra space round them for the default highlight).
|
2003-03-13 19:52:28 +00:00
|
|
|
*/
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
GtkWidget *layout_ctrls(
|
|
|
|
struct dlgparam *dp, struct selparam *sp, struct Shortcuts *scs,
|
|
|
|
struct controlset *s, GtkWindow *win)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
GtkWidget *ret;
|
|
|
|
int i;
|
|
|
|
|
2018-12-01 14:13:37 +00:00
|
|
|
if (!s->boxname) {
|
2003-03-13 19:52:28 +00:00
|
|
|
/* This controlset is a panel title. */
|
2018-12-01 14:13:37 +00:00
|
|
|
assert(s->boxtitle);
|
2003-03-13 19:52:28 +00:00
|
|
|
return gtk_label_new(s->boxtitle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, we expect to be laying out actual controls, so
|
|
|
|
* we'll start by creating a Columns for the purpose.
|
|
|
|
*/
|
|
|
|
cols = COLUMNS(columns_new(4));
|
|
|
|
ret = GTK_WIDGET(cols);
|
|
|
|
gtk_widget_show(ret);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a containing frame if we have a box name.
|
|
|
|
*/
|
|
|
|
if (*s->boxname) {
|
|
|
|
ret = gtk_frame_new(s->boxtitle); /* NULL is valid here */
|
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(cols), 4);
|
|
|
|
gtk_container_add(GTK_CONTAINER(ret), GTK_WIDGET(cols));
|
|
|
|
gtk_widget_show(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now iterate through the controls themselves, create them,
|
|
|
|
* and add them to the Columns.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < s->ncontrols; i++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
union control *ctrl = s->ctrls[i];
|
|
|
|
struct uctrl *uc;
|
|
|
|
bool left = false;
|
2003-03-13 19:52:28 +00:00
|
|
|
GtkWidget *w = NULL;
|
|
|
|
|
|
|
|
switch (ctrl->generic.type) {
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
case CTRL_COLUMNS: {
|
|
|
|
static const int simplecols[1] = { 100 };
|
|
|
|
columns_set_cols(cols, ctrl->columns.ncols,
|
|
|
|
(ctrl->columns.percentages ?
|
|
|
|
ctrl->columns.percentages : simplecols));
|
2003-03-13 19:52:28 +00:00
|
|
|
continue; /* no actual control created */
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
|
|
|
case CTRL_TABDELAY: {
|
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl);
|
|
|
|
if (uc)
|
|
|
|
columns_taborder_last(cols, uc->toplevel);
|
2003-03-13 19:52:28 +00:00
|
|
|
continue; /* no actual control created */
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2003-03-16 12:37:48 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
uc = snew(struct uctrl);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
uc->sp = sp;
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->ctrl = ctrl;
|
|
|
|
uc->buttons = NULL;
|
|
|
|
uc->entry = NULL;
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->list = uc->menu = uc->optmenu = NULL;
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->combo = NULL;
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->treeview = NULL;
|
|
|
|
uc->listmodel = NULL;
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->button = uc->text = NULL;
|
|
|
|
uc->label = NULL;
|
2007-03-27 18:16:36 +00:00
|
|
|
uc->nclicks = 0;
|
2003-03-16 12:37:48 +00:00
|
|
|
|
|
|
|
switch (ctrl->generic.type) {
|
2003-03-13 19:52:28 +00:00
|
|
|
case CTRL_BUTTON:
|
|
|
|
w = gtk_button_new_with_label(ctrl->generic.label);
|
2019-09-08 19:29:00 +00:00
|
|
|
if (win) {
|
|
|
|
gtk_widget_set_can_default(w, true);
|
|
|
|
if (ctrl->button.isdefault)
|
|
|
|
gtk_window_set_default(win, w);
|
|
|
|
if (ctrl->button.iscancel)
|
|
|
|
dp->cancelbutton = w;
|
|
|
|
}
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "clicked",
|
|
|
|
G_CALLBACK(button_clicked), dp);
|
|
|
|
g_signal_connect(G_OBJECT(w), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2015-08-08 14:53:43 +00:00
|
|
|
shortcut_add(scs, gtk_bin_get_child(GTK_BIN(w)),
|
|
|
|
ctrl->button.shortcut, SHORTCUT_UCTRL, uc);
|
2003-03-13 19:52:28 +00:00
|
|
|
break;
|
|
|
|
case CTRL_CHECKBOX:
|
|
|
|
w = gtk_check_button_new_with_label(ctrl->generic.label);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "toggled",
|
|
|
|
G_CALLBACK(button_toggled), dp);
|
|
|
|
g_signal_connect(G_OBJECT(w), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2015-08-08 14:53:43 +00:00
|
|
|
shortcut_add(scs, gtk_bin_get_child(GTK_BIN(w)),
|
|
|
|
ctrl->checkbox.shortcut, SHORTCUT_UCTRL, uc);
|
2019-09-08 19:29:00 +00:00
|
|
|
left = true;
|
2003-03-13 19:52:28 +00:00
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
case CTRL_RADIO: {
|
2003-03-13 19:52:28 +00:00
|
|
|
/*
|
|
|
|
* Radio buttons get to go inside their own Columns, no
|
|
|
|
* matter what.
|
|
|
|
*/
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
gint i, *percentages;
|
|
|
|
GSList *group;
|
|
|
|
|
|
|
|
w = columns_new(0);
|
|
|
|
if (ctrl->generic.label) {
|
|
|
|
GtkWidget *label = gtk_label_new(ctrl->generic.label);
|
|
|
|
columns_add(COLUMNS(w), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(w), label);
|
|
|
|
gtk_widget_show(label);
|
|
|
|
shortcut_add(scs, label, ctrl->radio.shortcut,
|
|
|
|
SHORTCUT_UCTRL, uc);
|
|
|
|
uc->label = label;
|
|
|
|
}
|
|
|
|
percentages = g_new(gint, ctrl->radio.ncolumns);
|
|
|
|
for (i = 0; i < ctrl->radio.ncolumns; i++) {
|
|
|
|
percentages[i] =
|
|
|
|
((100 * (i+1) / ctrl->radio.ncolumns) -
|
|
|
|
100 * i / ctrl->radio.ncolumns);
|
|
|
|
}
|
|
|
|
columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns,
|
|
|
|
percentages);
|
|
|
|
g_free(percentages);
|
|
|
|
group = NULL;
|
|
|
|
|
|
|
|
uc->nbuttons = ctrl->radio.nbuttons;
|
|
|
|
uc->buttons = snewn(uc->nbuttons, GtkWidget *);
|
|
|
|
|
|
|
|
for (i = 0; i < ctrl->radio.nbuttons; i++) {
|
|
|
|
GtkWidget *b;
|
|
|
|
gint colstart;
|
|
|
|
|
|
|
|
b = (gtk_radio_button_new_with_label
|
|
|
|
(group, ctrl->radio.buttons[i]));
|
|
|
|
uc->buttons[i] = b;
|
|
|
|
group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(b));
|
|
|
|
colstart = i % ctrl->radio.ncolumns;
|
|
|
|
columns_add(COLUMNS(w), b, colstart,
|
|
|
|
(i == ctrl->radio.nbuttons-1 ?
|
|
|
|
ctrl->radio.ncolumns - colstart : 1));
|
|
|
|
columns_force_left_align(COLUMNS(w), b);
|
|
|
|
gtk_widget_show(b);
|
|
|
|
g_signal_connect(G_OBJECT(b), "toggled",
|
|
|
|
G_CALLBACK(button_toggled), dp);
|
|
|
|
g_signal_connect(G_OBJECT(b), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
|
|
|
if (ctrl->radio.shortcuts) {
|
|
|
|
shortcut_add(scs, gtk_bin_get_child(GTK_BIN(b)),
|
|
|
|
ctrl->radio.shortcuts[i],
|
|
|
|
SHORTCUT_UCTRL, uc);
|
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
|
|
|
case CTRL_EDITBOX: {
|
|
|
|
GtkWidget *signalobject;
|
2003-03-22 17:00:06 +00:00
|
|
|
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
if (ctrl->editbox.has_list) {
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
/*
|
|
|
|
* GTK 1 combo box.
|
|
|
|
*/
|
|
|
|
w = gtk_combo_new();
|
|
|
|
gtk_combo_set_value_in_list(GTK_COMBO(w), false, true);
|
|
|
|
uc->entry = GTK_COMBO(w)->entry;
|
|
|
|
uc->list = GTK_COMBO(w)->list;
|
|
|
|
signalobject = uc->entry;
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
/*
|
|
|
|
* GTK 2 combo box.
|
|
|
|
*/
|
|
|
|
uc->listmodel = gtk_list_store_new(2, G_TYPE_INT,
|
|
|
|
G_TYPE_STRING);
|
|
|
|
w = gtk_combo_box_new_with_model_and_entry
|
|
|
|
(GTK_TREE_MODEL(uc->listmodel));
|
|
|
|
g_object_set(G_OBJECT(w), "entry-text-column", 1,
|
|
|
|
(const char *)NULL);
|
|
|
|
/* We cannot support password combo boxes. */
|
|
|
|
assert(!ctrl->editbox.password);
|
|
|
|
uc->combo = w;
|
|
|
|
signalobject = uc->combo;
|
2019-09-08 19:29:00 +00:00
|
|
|
#endif
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
} else {
|
|
|
|
w = gtk_entry_new();
|
|
|
|
if (ctrl->editbox.password)
|
|
|
|
gtk_entry_set_visibility(GTK_ENTRY(w), false);
|
|
|
|
uc->entry = w;
|
|
|
|
signalobject = w;
|
|
|
|
}
|
|
|
|
uc->entrysig =
|
|
|
|
g_signal_connect(G_OBJECT(signalobject), "changed",
|
|
|
|
G_CALLBACK(editbox_changed), dp);
|
|
|
|
g_signal_connect(G_OBJECT(signalobject), "key_press_event",
|
|
|
|
G_CALLBACK(editbox_key), dp);
|
|
|
|
g_signal_connect(G_OBJECT(signalobject), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
|
|
|
g_signal_connect(G_OBJECT(signalobject), "focus_out_event",
|
|
|
|
G_CALLBACK(editbox_lostfocus), dp);
|
|
|
|
g_signal_connect(G_OBJECT(signalobject), "focus_out_event",
|
|
|
|
G_CALLBACK(editbox_lostfocus), dp);
|
2015-08-22 09:37:48 +00:00
|
|
|
|
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
GtkRequisition req;
|
|
|
|
gtk_widget_size_request(w, &req);
|
|
|
|
gtk_widget_set_size_request(w, 10, req.height);
|
|
|
|
}
|
2015-08-22 09:37:48 +00:00
|
|
|
#else
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2015-08-22 09:37:48 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
if (ctrl->generic.label) {
|
|
|
|
GtkWidget *label, *container;
|
|
|
|
|
|
|
|
label = gtk_label_new(ctrl->generic.label);
|
|
|
|
|
|
|
|
shortcut_add(scs, label, ctrl->editbox.shortcut,
|
|
|
|
SHORTCUT_FOCUS, uc->entry);
|
|
|
|
|
|
|
|
container = columns_new(4);
|
|
|
|
if (ctrl->editbox.percentwidth == 100) {
|
|
|
|
columns_add(COLUMNS(container), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(container), label);
|
|
|
|
columns_add(COLUMNS(container), w, 0, 1);
|
|
|
|
} else {
|
|
|
|
gint percentages[2];
|
|
|
|
percentages[1] = ctrl->editbox.percentwidth;
|
|
|
|
percentages[0] = 100 - ctrl->editbox.percentwidth;
|
|
|
|
columns_set_cols(COLUMNS(container), 2, percentages);
|
|
|
|
columns_add(COLUMNS(container), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(container), label);
|
|
|
|
columns_add(COLUMNS(container), w, 1, 1);
|
|
|
|
columns_force_same_height(COLUMNS(container),
|
|
|
|
label, w);
|
|
|
|
}
|
|
|
|
gtk_widget_show(label);
|
|
|
|
gtk_widget_show(w);
|
|
|
|
|
|
|
|
w = container;
|
|
|
|
uc->label = label;
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2003-03-14 18:35:01 +00:00
|
|
|
case CTRL_FILESELECT:
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
case CTRL_FONTSELECT: {
|
|
|
|
GtkWidget *ww;
|
|
|
|
const char *browsebtn =
|
|
|
|
(ctrl->generic.type == CTRL_FILESELECT ?
|
|
|
|
"Browse..." : "Change...");
|
|
|
|
|
|
|
|
gint percentages[] = { 75, 25 };
|
|
|
|
w = columns_new(4);
|
|
|
|
columns_set_cols(COLUMNS(w), 2, percentages);
|
|
|
|
|
|
|
|
if (ctrl->generic.label) {
|
|
|
|
ww = gtk_label_new(ctrl->generic.label);
|
|
|
|
columns_add(COLUMNS(w), ww, 0, 2);
|
|
|
|
columns_force_left_align(COLUMNS(w), ww);
|
|
|
|
gtk_widget_show(ww);
|
|
|
|
shortcut_add(scs, ww,
|
|
|
|
(ctrl->generic.type == CTRL_FILESELECT ?
|
|
|
|
ctrl->fileselect.shortcut :
|
|
|
|
ctrl->fontselect.shortcut),
|
|
|
|
SHORTCUT_UCTRL, uc);
|
|
|
|
uc->label = ww;
|
|
|
|
}
|
2003-03-14 18:35:01 +00:00
|
|
|
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
uc->entry = ww = gtk_entry_new();
|
2015-08-24 18:04:37 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
{
|
|
|
|
GtkRequisition req;
|
|
|
|
gtk_widget_size_request(ww, &req);
|
|
|
|
gtk_widget_set_size_request(ww, 10, req.height);
|
|
|
|
}
|
2015-08-24 18:04:37 +00:00
|
|
|
#else
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
gtk_entry_set_width_chars(GTK_ENTRY(ww), 1);
|
2015-08-24 18:04:37 +00:00
|
|
|
#endif
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
columns_add(COLUMNS(w), ww, 0, 1);
|
|
|
|
gtk_widget_show(ww);
|
|
|
|
|
|
|
|
uc->button = ww = gtk_button_new_with_label(browsebtn);
|
|
|
|
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 =
|
|
|
|
g_signal_connect(G_OBJECT(uc->entry), "changed",
|
|
|
|
G_CALLBACK(editbox_changed), dp);
|
|
|
|
g_signal_connect(G_OBJECT(uc->entry), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
|
|
|
g_signal_connect(G_OBJECT(uc->button), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
|
|
|
g_signal_connect(G_OBJECT(ww), "clicked",
|
|
|
|
G_CALLBACK(filefont_clicked), dp);
|
2003-03-14 18:35:01 +00:00
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2003-03-14 18:35:01 +00:00
|
|
|
case CTRL_LISTBOX:
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* First construct the list data store, with the right
|
|
|
|
* number of columns.
|
|
|
|
*/
|
2008-05-31 19:23:45 +00:00
|
|
|
# if !GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
/* (For GTK 2.0 to 2.3, we do this for full listboxes only,
|
|
|
|
* because combo boxes are still done the old GTK1 way.) */
|
|
|
|
if (ctrl->listbox.height > 0)
|
2008-05-31 19:23:45 +00:00
|
|
|
# endif
|
2019-09-08 19:29:00 +00:00
|
|
|
{
|
|
|
|
GType *types;
|
|
|
|
int i;
|
|
|
|
int cols;
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
cols = ctrl->listbox.ncols;
|
|
|
|
cols = cols ? cols : 1;
|
|
|
|
types = snewn(1 + cols, GType);
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
types[0] = G_TYPE_INT;
|
|
|
|
for (i = 0; i < cols; i++)
|
|
|
|
types[i+1] = G_TYPE_STRING;
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->listmodel = gtk_list_store_newv(1 + cols, types);
|
2008-05-31 19:23:45 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
sfree(types);
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* See if it's a drop-down list (non-editable combo
|
|
|
|
* box).
|
|
|
|
*/
|
|
|
|
if (ctrl->listbox.height == 0) {
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* GTK1 and early-GTK2 option-menu style of
|
|
|
|
* drop-down list.
|
|
|
|
*/
|
2008-04-04 10:56:26 +00:00
|
|
|
uc->optmenu = w = gtk_option_menu_new();
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->menu = gtk_menu_new();
|
|
|
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(w), uc->menu);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set_data(G_OBJECT(uc->menu), "user-data",
|
|
|
|
(gpointer)uc->optmenu);
|
|
|
|
g_signal_connect(G_OBJECT(uc->optmenu), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2008-05-31 19:23:45 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Late-GTK2 style using a GtkComboBox.
|
|
|
|
*/
|
|
|
|
GtkCellRenderer *cr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a non-editable GtkComboBox (that is, not
|
|
|
|
* its subclass GtkComboBoxEntry).
|
|
|
|
*/
|
|
|
|
w = gtk_combo_box_new_with_model
|
|
|
|
(GTK_TREE_MODEL(uc->listmodel));
|
|
|
|
uc->combo = w;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell it how to render a list item (i.e. which
|
|
|
|
* column to look at in the list model).
|
|
|
|
*/
|
|
|
|
cr = gtk_cell_renderer_text_new();
|
|
|
|
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, true);
|
|
|
|
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,
|
|
|
|
"text", 1, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And tell it to notify us when the selection
|
|
|
|
* changes.
|
|
|
|
*/
|
|
|
|
g_signal_connect(G_OBJECT(w), "changed",
|
|
|
|
G_CALLBACK(droplist_selchange), dp);
|
2015-09-26 10:48:21 +00:00
|
|
|
|
|
|
|
g_signal_connect(G_OBJECT(w), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
2008-04-04 10:56:26 +00:00
|
|
|
} else {
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* GTK1-style full list box.
|
|
|
|
*/
|
2008-04-04 10:56:26 +00:00
|
|
|
uc->list = gtk_list_new();
|
|
|
|
if (ctrl->listbox.multisel == 2) {
|
|
|
|
gtk_list_set_selection_mode(GTK_LIST(uc->list),
|
|
|
|
GTK_SELECTION_EXTENDED);
|
|
|
|
} else if (ctrl->listbox.multisel == 1) {
|
|
|
|
gtk_list_set_selection_mode(GTK_LIST(uc->list),
|
|
|
|
GTK_SELECTION_MULTIPLE);
|
|
|
|
} else {
|
|
|
|
gtk_list_set_selection_mode(GTK_LIST(uc->list),
|
|
|
|
GTK_SELECTION_SINGLE);
|
|
|
|
}
|
|
|
|
w = gtk_scrolled_window_new(NULL, NULL);
|
|
|
|
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w),
|
|
|
|
uc->list);
|
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w),
|
|
|
|
GTK_POLICY_NEVER,
|
|
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
uc->adj = gtk_scrolled_window_get_vadjustment
|
|
|
|
(GTK_SCROLLED_WINDOW(w));
|
|
|
|
|
|
|
|
gtk_widget_show(uc->list);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(uc->list), "selection-changed",
|
|
|
|
G_CALLBACK(list_selchange), dp);
|
|
|
|
g_signal_connect(G_OBJECT(uc->list), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust the height of the scrolled window to the
|
|
|
|
* minimum given by the height parameter.
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
2008-04-04 10:56:26 +00:00
|
|
|
* This piece of guesswork is a horrid hack based
|
|
|
|
* on looking inside the GTK 1.2 sources
|
|
|
|
* (specifically gtkviewport.c, which appears to be
|
|
|
|
* the widget which provides the border around the
|
|
|
|
* scrolling area). Anyone lets me know how I can
|
|
|
|
* do this in a way which isn't at risk from GTK
|
|
|
|
* upgrades, I'd be grateful.
|
|
|
|
*/
|
2019-09-08 19:29:00 +00:00
|
|
|
{
|
|
|
|
int edge;
|
|
|
|
edge = GTK_WIDGET(uc->list)->style->klass->ythickness;
|
2015-08-08 17:02:01 +00:00
|
|
|
gtk_widget_set_size_request(w, 10,
|
2008-04-04 10:56:26 +00:00
|
|
|
2*edge + (ctrl->listbox.height *
|
2019-09-08 19:29:00 +00:00
|
|
|
get_listitemheight(w)));
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
if (ctrl->listbox.draglist) {
|
|
|
|
/*
|
|
|
|
* GTK doesn't appear to make it easy to
|
|
|
|
* implement a proper draggable list; so
|
|
|
|
* instead I'm just going to have to put an Up
|
|
|
|
* and a Down button to the right of the actual
|
|
|
|
* list box. Ah well.
|
|
|
|
*/
|
|
|
|
GtkWidget *cols, *button;
|
|
|
|
static const gint percentages[2] = { 80, 20 };
|
|
|
|
|
|
|
|
cols = columns_new(4);
|
|
|
|
columns_set_cols(COLUMNS(cols), 2, percentages);
|
|
|
|
columns_add(COLUMNS(cols), w, 0, 1);
|
|
|
|
gtk_widget_show(w);
|
|
|
|
button = gtk_button_new_with_label("Up");
|
|
|
|
columns_add(COLUMNS(cols), button, 1, 1);
|
|
|
|
gtk_widget_show(button);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "clicked",
|
|
|
|
G_CALLBACK(draglist_up), dp);
|
|
|
|
g_signal_connect(G_OBJECT(button), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2008-04-04 10:56:26 +00:00
|
|
|
button = gtk_button_new_with_label("Down");
|
|
|
|
columns_add(COLUMNS(cols), button, 1, 1);
|
|
|
|
gtk_widget_show(button);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "clicked",
|
|
|
|
G_CALLBACK(draglist_down), dp);
|
|
|
|
g_signal_connect(G_OBJECT(button), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2008-04-04 10:56:26 +00:00
|
|
|
|
|
|
|
w = cols;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* GTK2 treeview-based full list box.
|
|
|
|
*/
|
|
|
|
GtkTreeSelection *sel;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the list box itself, its columns, and
|
|
|
|
* its containing scrolled window.
|
|
|
|
*/
|
|
|
|
w = gtk_tree_view_new_with_model
|
|
|
|
(GTK_TREE_MODEL(uc->listmodel));
|
|
|
|
g_object_set_data(G_OBJECT(uc->listmodel), "user-data",
|
|
|
|
(gpointer)w);
|
|
|
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), false);
|
|
|
|
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
|
|
|
|
gtk_tree_selection_set_mode
|
|
|
|
(sel, ctrl->listbox.multisel ? GTK_SELECTION_MULTIPLE :
|
|
|
|
GTK_SELECTION_SINGLE);
|
|
|
|
uc->treeview = w;
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "row-activated",
|
|
|
|
G_CALLBACK(listbox_doubleclick), dp);
|
2015-09-26 10:48:21 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "focus_in_event",
|
|
|
|
G_CALLBACK(widget_focus), dp);
|
2019-09-08 19:29:00 +00:00
|
|
|
g_signal_connect(G_OBJECT(sel), "changed",
|
|
|
|
G_CALLBACK(listbox_selchange), dp);
|
|
|
|
|
|
|
|
if (ctrl->listbox.draglist) {
|
|
|
|
gtk_tree_view_set_reorderable(GTK_TREE_VIEW(w), true);
|
|
|
|
g_signal_connect(G_OBJECT(uc->listmodel), "row-inserted",
|
|
|
|
G_CALLBACK(listbox_reorder), dp);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int cols;
|
|
|
|
|
|
|
|
cols = ctrl->listbox.ncols;
|
|
|
|
cols = cols ? cols : 1;
|
|
|
|
for (i = 0; i < cols; i++) {
|
|
|
|
GtkTreeViewColumn *column;
|
2014-09-09 11:46:14 +00:00
|
|
|
GtkCellRenderer *cellrend;
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* It appears that GTK 2 doesn't leave us any
|
|
|
|
* particularly sensible way to honour the
|
|
|
|
* "percentages" specification in the ctrl
|
|
|
|
* structure.
|
|
|
|
*/
|
2014-09-09 11:46:14 +00:00
|
|
|
cellrend = gtk_cell_renderer_text_new();
|
|
|
|
if (!ctrl->listbox.hscroll) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_set(G_OBJECT(cellrend),
|
|
|
|
"ellipsize", PANGO_ELLIPSIZE_END,
|
2018-10-29 19:50:29 +00:00
|
|
|
"ellipsize-set", true,
|
2015-08-31 13:36:12 +00:00
|
|
|
(const char *)NULL);
|
2014-09-09 11:46:14 +00:00
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
column = gtk_tree_view_column_new_with_attributes
|
|
|
|
("heading", cellrend, "text", i+1, (char *)NULL);
|
|
|
|
gtk_tree_view_column_set_sizing
|
|
|
|
(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
|
|
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
GtkWidget *scroll;
|
|
|
|
|
|
|
|
scroll = gtk_scrolled_window_new(NULL, NULL);
|
|
|
|
gtk_scrolled_window_set_shadow_type
|
|
|
|
(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
|
|
|
|
gtk_widget_show(w);
|
|
|
|
gtk_container_add(GTK_CONTAINER(scroll), w);
|
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
|
|
|
|
GTK_POLICY_AUTOMATIC,
|
|
|
|
GTK_POLICY_ALWAYS);
|
|
|
|
gtk_widget_set_size_request
|
|
|
|
(scroll, -1,
|
|
|
|
ctrl->listbox.height * get_listitemheight(w));
|
|
|
|
|
|
|
|
w = scroll;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
}
|
2003-03-14 18:35:01 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (ctrl->generic.label) {
|
|
|
|
GtkWidget *label, *container;
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
label = gtk_label_new(ctrl->generic.label);
|
2015-08-25 18:50:23 +00:00
|
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
2015-08-23 14:01:07 +00:00
|
|
|
gtk_label_set_width_chars(GTK_LABEL(label), 3);
|
2015-08-25 18:50:23 +00:00
|
|
|
#endif
|
2003-03-14 18:35:01 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
shortcut_add(scs, label, ctrl->listbox.shortcut,
|
|
|
|
SHORTCUT_UCTRL, uc);
|
|
|
|
|
|
|
|
container = columns_new(4);
|
|
|
|
if (ctrl->listbox.percentwidth == 100) {
|
|
|
|
columns_add(COLUMNS(container), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(container), label);
|
|
|
|
columns_add(COLUMNS(container), w, 0, 1);
|
|
|
|
} else {
|
|
|
|
gint percentages[2];
|
|
|
|
percentages[1] = ctrl->listbox.percentwidth;
|
|
|
|
percentages[0] = 100 - ctrl->listbox.percentwidth;
|
|
|
|
columns_set_cols(COLUMNS(container), 2, percentages);
|
|
|
|
columns_add(COLUMNS(container), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(container), label);
|
|
|
|
columns_add(COLUMNS(container), w, 1, 1);
|
2015-08-24 18:23:38 +00:00
|
|
|
columns_force_same_height(COLUMNS(container),
|
|
|
|
label, w);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
gtk_widget_show(label);
|
|
|
|
gtk_widget_show(w);
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
w = container;
|
|
|
|
uc->label = label;
|
|
|
|
}
|
2008-04-02 14:48:06 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2003-03-13 19:52:28 +00:00
|
|
|
case CTRL_TEXT:
|
2015-08-23 14:01:07 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* wrapping text widget _has_ no clear minimum size;
|
|
|
|
* instead it has a range of possibilities. It can be
|
|
|
|
* one line deep but 2000 wide, or two lines deep and
|
|
|
|
* 1000 pixels, or three by 867, or four by 500 and so
|
|
|
|
* on. It can be as short as you like provided you
|
|
|
|
* don't mind it being wide, or as narrow as you like
|
|
|
|
* provided you don't mind it being tall.
|
|
|
|
*
|
|
|
|
* Therefore, it fits very badly into the layout model.
|
|
|
|
* Hence the only thing to do is pick a width and let
|
|
|
|
* it choose its own number of lines. To do this I'm
|
|
|
|
* going to cheat a little. All new wrapping text
|
|
|
|
* widgets will be created with a minimal text content
|
|
|
|
* "X"; then, after the rest of the dialog box is set
|
|
|
|
* up and its size calculated, the text widgets will be
|
|
|
|
* told their width and given their real text, which
|
|
|
|
* will cause the size to be recomputed in the y
|
|
|
|
* direction (because many of them will expand to more
|
|
|
|
* than one line).
|
|
|
|
*/
|
2003-03-22 17:00:06 +00:00
|
|
|
uc->text = w = gtk_label_new("X");
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->textsig =
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "size-allocate",
|
|
|
|
G_CALLBACK(label_sizealloc), dp);
|
2015-08-23 14:01:07 +00:00
|
|
|
#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
|
2015-08-31 12:57:34 +00:00
|
|
|
align_label_left(GTK_LABEL(w));
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_label_set_line_wrap(GTK_LABEL(w), true);
|
2003-03-13 19:52:28 +00:00
|
|
|
break;
|
|
|
|
}
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(w != NULL);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
columns_add(cols, w,
|
|
|
|
COLUMN_START(ctrl->generic.column),
|
|
|
|
COLUMN_SPAN(ctrl->generic.column));
|
|
|
|
if (left)
|
|
|
|
columns_force_left_align(cols, w);
|
2021-04-03 16:45:31 +00:00
|
|
|
if (ctrl->generic.align_next_to) {
|
|
|
|
/*
|
|
|
|
* Implement align_next_to by simply forcing the two
|
|
|
|
* controls to have the same height of size allocation. At
|
|
|
|
* least for the controls we're currently doing this with,
|
|
|
|
* the GTK layout system will automatically vertically
|
|
|
|
* centre each control within its allocation, which will
|
|
|
|
* get the two controls aligned alongside each other
|
|
|
|
* reasonably well.
|
|
|
|
*/
|
|
|
|
struct uctrl *uc2 = dlg_find_byctrl(
|
|
|
|
dp, ctrl->generic.align_next_to);
|
|
|
|
assert(uc2);
|
|
|
|
columns_force_same_height(cols, w, uc2->toplevel);
|
|
|
|
|
|
|
|
#if GTK_CHECK_VERSION(3, 10, 0)
|
|
|
|
/* Slightly nicer to align baselines than just vertically
|
|
|
|
* centring, where the option is available */
|
|
|
|
gtk_widget_set_valign(w, GTK_ALIGN_BASELINE);
|
|
|
|
gtk_widget_set_valign(uc2->toplevel, GTK_ALIGN_BASELINE);
|
|
|
|
#endif
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_show(w);
|
2003-03-16 12:37:48 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
uc->toplevel = w;
|
|
|
|
dlg_add_uctrl(dp, uc);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct selparam {
|
2003-03-16 12:37:48 +00:00
|
|
|
struct dlgparam *dp;
|
2006-12-29 14:35:34 +00:00
|
|
|
GtkNotebook *panels;
|
2007-01-25 19:33:29 +00:00
|
|
|
GtkWidget *panel;
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkWidget *treeitem;
|
2008-04-04 11:37:06 +00:00
|
|
|
#else
|
|
|
|
int depth;
|
|
|
|
GtkTreePath *treepath;
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-16 12:37:48 +00:00
|
|
|
struct Shortcuts shortcuts;
|
2003-03-13 19:52:28 +00:00
|
|
|
};
|
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
static void treeselection_changed(GtkTreeSelection *treeselection,
|
2019-09-08 19:29:00 +00:00
|
|
|
gpointer data)
|
2007-01-25 19:33:29 +00:00
|
|
|
{
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam **sps = (struct selparam **)data, *sp;
|
2007-01-25 19:33:29 +00:00
|
|
|
GtkTreeModel *treemodel;
|
|
|
|
GtkTreeIter treeiter;
|
|
|
|
gint spindex;
|
|
|
|
gint page_num;
|
|
|
|
|
|
|
|
if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2007-01-25 19:33:29 +00:00
|
|
|
|
|
|
|
gtk_tree_model_get(treemodel, &treeiter, TREESTORE_PARAMS, &spindex, -1);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
sp = sps[spindex];
|
2007-01-25 19:33:29 +00:00
|
|
|
|
|
|
|
page_num = gtk_notebook_page_num(sp->panels, sp->panel);
|
2015-08-08 17:02:01 +00:00
|
|
|
gtk_notebook_set_current_page(sp->panels, page_num);
|
2007-01-25 19:33:29 +00:00
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
sp->dp->curr_panel = sp;
|
2007-01-25 19:33:29 +00:00
|
|
|
dlg_refresh(NULL, sp->dp);
|
|
|
|
|
|
|
|
sp->dp->shortcuts = &sp->shortcuts;
|
|
|
|
}
|
|
|
|
#else
|
2003-03-13 19:52:28 +00:00
|
|
|
static void treeitem_sel(GtkItem *item, gpointer data)
|
|
|
|
{
|
|
|
|
struct selparam *sp = (struct selparam *)data;
|
2006-12-29 14:35:34 +00:00
|
|
|
gint page_num;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2006-12-29 14:35:34 +00:00
|
|
|
page_num = gtk_notebook_page_num(sp->panels, sp->panel);
|
|
|
|
gtk_notebook_set_page(sp->panels, page_num);
|
2003-03-16 12:37:48 +00:00
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
sp->dp->curr_panel = sp;
|
2006-08-28 14:29:02 +00:00
|
|
|
dlg_refresh(NULL, sp->dp);
|
|
|
|
|
2003-03-16 12:37:48 +00:00
|
|
|
sp->dp->shortcuts = &sp->shortcuts;
|
2003-03-18 19:06:51 +00:00
|
|
|
sp->dp->currtreeitem = sp->treeitem;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
bool dlg_is_visible(union control *ctrl, dlgparam *dp)
|
|
|
|
{
|
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
|
|
|
/*
|
|
|
|
* A control is visible if it belongs to _no_ notebook page (i.e.
|
|
|
|
* it's one of the config-box-global buttons like Load or About),
|
|
|
|
* or if it belongs to the currently selected page.
|
|
|
|
*/
|
|
|
|
return uc->sp == NULL || uc->sp == dp->curr_panel;
|
|
|
|
}
|
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
static bool tree_grab_focus(struct dlgparam *dp)
|
2003-03-22 17:00:06 +00:00
|
|
|
{
|
|
|
|
int i, f;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if any of the treeitems has the focus.
|
|
|
|
*/
|
|
|
|
f = -1;
|
|
|
|
for (i = 0; i < dp->ntreeitems; i++)
|
|
|
|
if (GTK_WIDGET_HAS_FOCUS(dp->treeitems[i])) {
|
|
|
|
f = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f >= 0)
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-22 17:00:06 +00:00
|
|
|
else {
|
|
|
|
gtk_widget_grab_focus(dp->currtreeitem);
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2003-03-22 17:00:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gint tree_focus(GtkContainer *container, GtkDirectionType direction,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(container), "focus");
|
2003-03-22 17:00:06 +00:00
|
|
|
/*
|
2018-10-29 19:50:29 +00:00
|
|
|
* If there's a focused treeitem, we return false to cause the
|
2003-03-22 17:00:06 +00:00
|
|
|
* focus to move on to some totally other control. If not, we
|
|
|
|
* focus the selected one.
|
|
|
|
*/
|
|
|
|
return tree_grab_focus(dp);
|
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-22 17:00:06 +00:00
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
gint win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
2003-03-16 12:37:48 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
|
2015-08-08 15:23:54 +00:00
|
|
|
if (event->keyval == GDK_KEY_Escape && dp->cancelbutton) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_emit_by_name(G_OBJECT(dp->cancelbutton), "clicked");
|
2019-09-08 19:29:00 +00:00
|
|
|
return true;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((event->state & GDK_MOD1_MASK) &&
|
2019-09-08 19:29:00 +00:00
|
|
|
(unsigned char)event->string[0] > 0 &&
|
|
|
|
(unsigned char)event->string[0] <= 127) {
|
|
|
|
int schr = (unsigned char)event->string[0];
|
|
|
|
struct Shortcut *sc = &dp->shortcuts->sc[schr];
|
2003-03-16 12:37:48 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
switch (sc->action) {
|
|
|
|
case SHORTCUT_TREE:
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_grab_focus(sc->widget);
|
2007-01-25 19:33:29 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
tree_grab_focus(dp);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case SHORTCUT_FOCUS:
|
|
|
|
gtk_widget_grab_focus(sc->widget);
|
|
|
|
break;
|
|
|
|
case SHORTCUT_UCTRL:
|
|
|
|
/*
|
|
|
|
* We must do something sensible with a uctrl.
|
|
|
|
* Precisely what this is depends on the type of
|
|
|
|
* control.
|
|
|
|
*/
|
|
|
|
switch (sc->uc->ctrl->generic.type) {
|
|
|
|
case CTRL_CHECKBOX:
|
|
|
|
case CTRL_BUTTON:
|
|
|
|
/* Check boxes and buttons get the focus _and_ get toggled. */
|
|
|
|
gtk_widget_grab_focus(sc->uc->toplevel);
|
|
|
|
g_signal_emit_by_name(G_OBJECT(sc->uc->toplevel), "clicked");
|
|
|
|
break;
|
|
|
|
case CTRL_FILESELECT:
|
|
|
|
case CTRL_FONTSELECT:
|
|
|
|
/* File/font selectors have their buttons pressed (ooer),
|
|
|
|
* and focus transferred to the edit box. */
|
|
|
|
g_signal_emit_by_name(G_OBJECT(sc->uc->button), "clicked");
|
|
|
|
gtk_widget_grab_focus(sc->uc->entry);
|
|
|
|
break;
|
|
|
|
case CTRL_RADIO:
|
|
|
|
/*
|
|
|
|
* Radio buttons are fun, because they have
|
|
|
|
* multiple shortcuts. We must find whether the
|
|
|
|
* activated shortcut is the shortcut for the whole
|
|
|
|
* group, or for a particular button. In the former
|
|
|
|
* case, we find the currently selected button and
|
|
|
|
* focus it; in the latter, we focus-and-click the
|
|
|
|
* button whose shortcut was pressed.
|
|
|
|
*/
|
|
|
|
if (schr == sc->uc->ctrl->radio.shortcut) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
|
|
|
|
if (gtk_toggle_button_get_active
|
|
|
|
(GTK_TOGGLE_BUTTON(sc->uc->buttons[i]))) {
|
|
|
|
gtk_widget_grab_focus(sc->uc->buttons[i]);
|
|
|
|
}
|
|
|
|
} else if (sc->uc->ctrl->radio.shortcuts) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
|
|
|
|
if (schr == sc->uc->ctrl->radio.shortcuts[i]) {
|
|
|
|
gtk_widget_grab_focus(sc->uc->buttons[i]);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_emit_by_name
|
|
|
|
(G_OBJECT(sc->uc->buttons[i]), "clicked");
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTRL_LISTBOX:
|
2008-04-04 10:56:26 +00:00
|
|
|
|
2008-05-31 19:23:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,4,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (sc->uc->optmenu) {
|
|
|
|
GdkEventButton bev;
|
|
|
|
gint returnval;
|
|
|
|
|
|
|
|
gtk_widget_grab_focus(sc->uc->optmenu);
|
|
|
|
/* Option menus don't work using the "clicked" signal.
|
|
|
|
* We need to manufacture a button press event :-/ */
|
|
|
|
bev.type = GDK_BUTTON_PRESS;
|
|
|
|
bev.button = 1;
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_emit_by_name(G_OBJECT(sc->uc->optmenu),
|
|
|
|
"button_press_event",
|
|
|
|
&bev, &returnval);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
if (sc->uc->combo) {
|
|
|
|
gtk_widget_grab_focus(sc->uc->combo);
|
|
|
|
gtk_combo_box_popup(GTK_COMBO_BOX(sc->uc->combo));
|
|
|
|
break;
|
|
|
|
}
|
2008-05-31 19:23:45 +00:00
|
|
|
#endif
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (sc->uc->list) {
|
|
|
|
/*
|
|
|
|
* For GTK-1 style list boxes, we tell it to
|
|
|
|
* focus one of its children, which appears to
|
|
|
|
* do the Right Thing.
|
|
|
|
*/
|
2008-04-04 10:56:26 +00:00
|
|
|
gtk_container_focus(GTK_CONTAINER(sc->uc->list),
|
|
|
|
GTK_DIR_TAB_FORWARD);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
if (sc->uc->treeview) {
|
|
|
|
gtk_widget_grab_focus(sc->uc->treeview);
|
|
|
|
break;
|
|
|
|
}
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2019-09-09 18:07:55 +00:00
|
|
|
unreachable("bad listbox type in win_key_press");
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
gint tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
2003-03-18 19:06:51 +00:00
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
|
|
|
|
|
|
|
if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
|
|
|
|
event->keyval == GDK_Down || event->keyval == GDK_KP_Down) {
|
2003-03-22 17:00:06 +00:00
|
|
|
int dir, i, j = -1;
|
2003-03-18 19:06:51 +00:00
|
|
|
for (i = 0; i < dp->ntreeitems; i++)
|
2003-03-22 17:00:06 +00:00
|
|
|
if (widget == dp->treeitems[i])
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
if (i < dp->ntreeitems) {
|
|
|
|
if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
|
|
|
|
dir = -1;
|
|
|
|
else
|
|
|
|
dir = +1;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
i += dir;
|
|
|
|
if (i < 0 || i >= dp->ntreeitems)
|
|
|
|
break; /* nothing in that dir to select */
|
|
|
|
/*
|
|
|
|
* Determine if this tree item is visible.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
GtkWidget *w = dp->treeitems[i];
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool vis = true;
|
2019-09-08 19:29:00 +00:00
|
|
|
while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) {
|
|
|
|
if (!GTK_WIDGET_VISIBLE(w)) {
|
|
|
|
vis = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w = w->parent;
|
|
|
|
}
|
|
|
|
if (vis) {
|
|
|
|
j = i; /* got one */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
|
2003-03-18 19:06:51 +00:00
|
|
|
if (j >= 0) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_emit_by_name(G_OBJECT(dp->treeitems[j]), "toggle");
|
2003-03-18 19:06:51 +00:00
|
|
|
gtk_widget_grab_focus(dp->treeitems[j]);
|
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2003-03-18 19:06:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-03-22 17:00:06 +00:00
|
|
|
* It's nice for Left and Right to expand and collapse tree
|
|
|
|
* branches.
|
2003-03-18 19:06:51 +00:00
|
|
|
*/
|
2003-03-22 17:00:06 +00:00
|
|
|
if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_tree_item_collapse(GTK_TREE_ITEM(widget));
|
|
|
|
return true;
|
2003-03-22 17:00:06 +00:00
|
|
|
}
|
|
|
|
if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) {
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_tree_item_expand(GTK_TREE_ITEM(widget));
|
|
|
|
return true;
|
2003-03-18 19:06:51 +00:00
|
|
|
}
|
2003-03-22 17:00:06 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-18 19:06:51 +00:00
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-18 19:06:51 +00:00
|
|
|
|
2006-08-28 14:29:02 +00:00
|
|
|
static void shortcut_highlight(GtkWidget *labelw, int chr)
|
2003-03-16 12:37:48 +00:00
|
|
|
{
|
|
|
|
GtkLabel *label = GTK_LABEL(labelw);
|
2015-08-08 17:02:01 +00:00
|
|
|
const gchar *currstr;
|
|
|
|
gchar *pattern;
|
2003-03-16 12:37:48 +00:00
|
|
|
int i;
|
|
|
|
|
2015-08-08 17:02:01 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
{
|
|
|
|
gchar *currstr_nonconst;
|
|
|
|
gtk_label_get(label, &currstr_nonconst);
|
|
|
|
currstr = currstr_nonconst;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
currstr = gtk_label_get_text(label);
|
|
|
|
#endif
|
|
|
|
|
2006-08-28 14:29:02 +00:00
|
|
|
for (i = 0; currstr[i]; i++)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (tolower((unsigned char)currstr[i]) == chr) {
|
|
|
|
pattern = dupprintf("%*s_", i, "");
|
|
|
|
gtk_label_set_pattern(label, pattern);
|
|
|
|
sfree(pattern);
|
|
|
|
break;
|
|
|
|
}
|
2006-08-28 14:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
2019-09-08 19:29:00 +00:00
|
|
|
int chr, int action, void *ptr)
|
2006-08-28 14:29:02 +00:00
|
|
|
{
|
2003-03-16 12:37:48 +00:00
|
|
|
if (chr == NO_SHORTCUT)
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2003-03-16 12:37:48 +00:00
|
|
|
|
|
|
|
chr = tolower((unsigned char)chr);
|
|
|
|
|
|
|
|
assert(scs->sc[chr].action == SHORTCUT_EMPTY);
|
|
|
|
|
|
|
|
scs->sc[chr].action = action;
|
|
|
|
|
2015-09-26 12:12:25 +00:00
|
|
|
if (action == SHORTCUT_FOCUS || action == SHORTCUT_TREE) {
|
2019-09-08 19:29:00 +00:00
|
|
|
scs->sc[chr].uc = NULL;
|
|
|
|
scs->sc[chr].widget = (GtkWidget *)ptr;
|
2003-03-16 12:37:48 +00:00
|
|
|
} else {
|
2019-09-08 19:29:00 +00:00
|
|
|
scs->sc[chr].widget = NULL;
|
|
|
|
scs->sc[chr].uc = (struct uctrl *)ptr;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2006-08-28 14:29:02 +00:00
|
|
|
shortcut_highlight(labelw, chr);
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 08:25:28 +00:00
|
|
|
static int get_listitemheight(GtkWidget *w)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
2008-04-04 10:56:26 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkWidget *listitem = gtk_list_item_new_with_label("foo");
|
|
|
|
GtkRequisition req;
|
|
|
|
gtk_widget_size_request(listitem, &req);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_ref_sink(G_OBJECT(listitem));
|
2008-04-04 10:56:26 +00:00
|
|
|
return req.height;
|
|
|
|
#else
|
2008-04-02 14:48:06 +00:00
|
|
|
int height;
|
|
|
|
GtkCellRenderer *cr = gtk_cell_renderer_text_new();
|
2015-08-22 12:53:29 +00:00
|
|
|
#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
|
2008-04-02 14:48:06 +00:00
|
|
|
gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height);
|
2015-08-22 12:53:29 +00:00
|
|
|
#endif
|
2008-04-02 14:48:06 +00:00
|
|
|
g_object_ref(G_OBJECT(cr));
|
2015-08-08 16:29:02 +00:00
|
|
|
g_object_ref_sink(G_OBJECT(cr));
|
2008-04-02 14:48:06 +00:00
|
|
|
g_object_unref(G_OBJECT(cr));
|
|
|
|
return height;
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-25 18:44:44 +00:00
|
|
|
#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++)
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
if (dp->selparams[i]->depth >= 2)
|
2015-08-25 18:44:44 +00:00
|
|
|
gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
dp->selparams[i]->treepath);
|
2015-08-25 18:44:44 +00:00
|
|
|
}
|
|
|
|
#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
|
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
GtkWidget *create_config_box(const char *title, Conf *conf,
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool midsession, int protcfginfo,
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
post_dialog_fn_t after, void *afterctx)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
GtkWidget *window, *hbox, *vbox, *cols, *label,
|
2019-09-08 19:29:00 +00:00
|
|
|
*tree, *treescroll, *panels, *panelvbox;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
int index, level, protocol;
|
2003-03-13 19:52:28 +00:00
|
|
|
char *path;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkTreeStore *treestore;
|
|
|
|
GtkCellRenderer *treerenderer;
|
|
|
|
GtkTreeViewColumn *treecolumn;
|
|
|
|
GtkTreeSelection *treeselection;
|
|
|
|
GtkTreeIter treeiterlevels[8];
|
|
|
|
#else
|
2003-03-13 19:52:28 +00:00
|
|
|
GtkTreeItem *treeitemlevels[8];
|
|
|
|
GtkTree *treelevels[8];
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
struct dlgparam *dp;
|
2003-03-16 12:37:48 +00:00
|
|
|
struct Shortcuts scs;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
struct selparam **selparams = NULL;
|
New array-growing macros: sgrowarray and sgrowarrayn.
The idea of these is that they centralise the common idiom along the
lines of
if (logical_array_len >= physical_array_size) {
physical_array_size = logical_array_len * 5 / 4 + 256;
array = sresize(array, physical_array_size, ElementType);
}
which happens at a zillion call sites throughout this code base, with
different random choices of the geometric factor and additive
constant, sometimes forgetting them completely, and generally doing a
lot of repeated work.
The new macro sgrowarray(array,size,n) has the semantics: here are the
array pointer and its physical size for you to modify, now please
ensure that the nth element exists, so I can write into it. And
sgrowarrayn(array,size,n,m) is the same except that it ensures that
the array has size at least n+m (so sgrowarray is just the special
case where m=1).
Now that this is a single centralised implementation that will be used
everywhere, I've also gone to more effort in the implementation, with
careful overflow checks that would have been painful to put at all the
previous call sites.
This commit also switches over every use of sresize(), apart from a
few where I really didn't think it would gain anything. A consequence
of that is that a lot of array-size variables have to have their types
changed to size_t, because the macros require that (they address-take
the size to pass to the underlying function).
2019-02-28 20:07:30 +00:00
|
|
|
size_t nselparams = 0, selparamsize = 0;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp = snew(struct dlgparam);
|
|
|
|
dp->after = after;
|
|
|
|
dp->afterctx = afterctx;
|
|
|
|
|
|
|
|
dlg_init(dp);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
2003-03-16 12:37:48 +00:00
|
|
|
for (index = 0; index < lenof(scs.sc); index++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
scs.sc[index].action = SHORTCUT_EMPTY;
|
2003-03-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2015-08-31 14:45:18 +00:00
|
|
|
window = our_dialog_new();
|
2003-04-10 08:53:43 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->ctrlbox = ctrl_new_box();
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
protocol = conf_get_int(conf, CONF_protocol);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
setup_config_box(dp->ctrlbox, midsession, protocol, protcfginfo);
|
|
|
|
unix_setup_config_box(dp->ctrlbox, midsession, protocol);
|
|
|
|
gtk_setup_config_box(dp->ctrlbox, midsession, window);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2003-03-22 17:00:06 +00:00
|
|
|
gtk_window_set_title(GTK_WINDOW(window), title);
|
2018-10-29 19:50:29 +00:00
|
|
|
hbox = gtk_hbox_new(false, 4);
|
|
|
|
our_dialog_add_to_content_area(GTK_WINDOW(window), hbox, true, true, 0);
|
2003-03-14 18:35:01 +00:00
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(hbox);
|
2018-10-29 19:50:29 +00:00
|
|
|
vbox = gtk_vbox_new(false, 4);
|
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), vbox, false, false, 0);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(vbox);
|
|
|
|
cols = columns_new(4);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), cols, false, false, 0);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(cols);
|
|
|
|
label = gtk_label_new("Category:");
|
|
|
|
columns_add(COLUMNS(cols), label, 0, 1);
|
|
|
|
columns_force_left_align(COLUMNS(cols), label);
|
|
|
|
gtk_widget_show(label);
|
|
|
|
treescroll = gtk_scrolled_window_new(NULL, NULL);
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
treestore = gtk_tree_store_new
|
2019-09-08 19:29:00 +00:00
|
|
|
(TREESTORE_NUM, G_TYPE_STRING, G_TYPE_INT);
|
2007-01-25 19:33:29 +00:00
|
|
|
tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), false);
|
2007-01-25 19:33:29 +00:00
|
|
|
treerenderer = gtk_cell_renderer_text_new();
|
|
|
|
treecolumn = gtk_tree_view_column_new_with_attributes
|
2019-09-08 19:29:00 +00:00
|
|
|
("Label", treerenderer, "text", 0, NULL);
|
2007-01-25 19:33:29 +00:00
|
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(tree), treecolumn);
|
|
|
|
treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
|
|
|
|
gtk_tree_selection_set_mode(treeselection, GTK_SELECTION_BROWSE);
|
|
|
|
gtk_container_add(GTK_CONTAINER(treescroll), tree);
|
|
|
|
#else
|
2003-03-13 19:52:28 +00:00
|
|
|
tree = gtk_tree_new();
|
|
|
|
gtk_tree_set_view_mode(GTK_TREE(tree), GTK_TREE_VIEW_ITEM);
|
2003-03-14 18:35:01 +00:00
|
|
|
gtk_tree_set_selection_mode(GTK_TREE(tree), GTK_SELECTION_BROWSE);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(tree), "focus", G_CALLBACK(tree_focus), dp);
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(tree), "focus_in_event",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(widget_focus), dp);
|
2007-01-25 19:33:29 +00:00
|
|
|
shortcut_add(&scs, label, 'g', SHORTCUT_TREE, tree);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(treescroll);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), treescroll, true, true, 0);
|
2006-12-29 14:35:34 +00:00
|
|
|
panels = gtk_notebook_new();
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(panels), false);
|
|
|
|
gtk_notebook_set_show_border(GTK_NOTEBOOK(panels), false);
|
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), panels, true, true, 0);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(panels);
|
|
|
|
|
|
|
|
panelvbox = NULL;
|
|
|
|
path = NULL;
|
|
|
|
level = 0;
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
for (index = 0; index < dp->ctrlbox->nctrlsets; index++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
struct controlset *s = dp->ctrlbox->ctrlsets[index];
|
|
|
|
GtkWidget *w;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!*s->pathname) {
|
|
|
|
w = layout_ctrls(dp, NULL, &scs, s, GTK_WINDOW(window));
|
2007-01-25 19:45:50 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
our_dialog_set_action_area(GTK_WINDOW(window), w);
|
|
|
|
} else {
|
|
|
|
int j = path ? ctrl_path_compare(s->pathname, path) : 0;
|
|
|
|
if (j != INT_MAX) { /* add to treeview, start new panel */
|
|
|
|
char *c;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkTreeIter treeiter;
|
2007-01-25 19:33:29 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkWidget *treeitem;
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2019-09-08 19:29:00 +00:00
|
|
|
bool first;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* We expect never to find an implicit path
|
|
|
|
* component. For example, we expect never to see
|
|
|
|
* A/B/C followed by A/D/E, because that would
|
|
|
|
* _implicitly_ create A/D. All our path prefixes
|
|
|
|
* are expected to contain actual controls and be
|
|
|
|
* selectable in the treeview; so we would expect
|
|
|
|
* to see A/D _explicitly_ before encountering
|
|
|
|
* A/D/E.
|
|
|
|
*/
|
|
|
|
assert(j == ctrl_path_elements(s->pathname) - 1);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
c = strrchr(s->pathname, '/');
|
|
|
|
if (!c)
|
|
|
|
c = s->pathname;
|
|
|
|
else
|
|
|
|
c++;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
path = s->pathname;
|
2007-01-25 19:33:29 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
first = (panelvbox == NULL);
|
2007-01-25 19:33:29 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
panelvbox = gtk_vbox_new(false, 4);
|
|
|
|
gtk_widget_show(panelvbox);
|
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(panels), panelvbox,
|
|
|
|
NULL);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
|
|
|
|
struct selparam *sp = snew(struct selparam);
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (first) {
|
|
|
|
gint page_num;
|
2007-01-25 19:33:29 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
page_num = gtk_notebook_page_num(GTK_NOTEBOOK(panels),
|
|
|
|
panelvbox);
|
|
|
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(panels),
|
2015-08-08 17:02:01 +00:00
|
|
|
page_num);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
|
|
|
|
dp->curr_panel = sp;
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
|
New array-growing macros: sgrowarray and sgrowarrayn.
The idea of these is that they centralise the common idiom along the
lines of
if (logical_array_len >= physical_array_size) {
physical_array_size = logical_array_len * 5 / 4 + 256;
array = sresize(array, physical_array_size, ElementType);
}
which happens at a zillion call sites throughout this code base, with
different random choices of the geometric factor and additive
constant, sometimes forgetting them completely, and generally doing a
lot of repeated work.
The new macro sgrowarray(array,size,n) has the semantics: here are the
array pointer and its physical size for you to modify, now please
ensure that the nth element exists, so I can write into it. And
sgrowarrayn(array,size,n,m) is the same except that it ensures that
the array has size at least n+m (so sgrowarray is just the special
case where m=1).
Now that this is a single centralised implementation that will be used
everywhere, I've also gone to more effort in the implementation, with
careful overflow checks that would have been painful to put at all the
previous call sites.
This commit also switches over every use of sresize(), apart from a
few where I really didn't think it would gain anything. A consequence
of that is that a lot of array-size variables have to have their types
changed to size_t, because the macros require that (they address-take
the size to pass to the underlying function).
2019-02-28 20:07:30 +00:00
|
|
|
sgrowarray(selparams, selparamsize, nselparams);
|
2019-09-08 19:29:00 +00:00
|
|
|
selparams[nselparams] = sp;
|
|
|
|
sp->dp = dp;
|
|
|
|
sp->panels = GTK_NOTEBOOK(panels);
|
|
|
|
sp->panel = panelvbox;
|
|
|
|
sp->shortcuts = scs; /* structure copy */
|
2007-01-25 19:33:29 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
assert(j-1 < level);
|
2007-01-25 19:33:29 +00:00
|
|
|
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2019-09-08 19:29:00 +00:00
|
|
|
if (j > 0)
|
|
|
|
/* treeiterlevels[j-1] will always be valid because we
|
|
|
|
* don't allow implicit path components; see above.
|
|
|
|
*/
|
|
|
|
gtk_tree_store_append(treestore, &treeiter,
|
|
|
|
&treeiterlevels[j-1]);
|
|
|
|
else
|
|
|
|
gtk_tree_store_append(treestore, &treeiter, NULL);
|
|
|
|
gtk_tree_store_set(treestore, &treeiter,
|
|
|
|
TREESTORE_PATH, c,
|
|
|
|
TREESTORE_PARAMS, nselparams,
|
|
|
|
-1);
|
|
|
|
treeiterlevels[j] = treeiter;
|
|
|
|
|
|
|
|
sp->depth = j;
|
|
|
|
if (j > 0) {
|
|
|
|
sp->treepath = gtk_tree_model_get_path(
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
GTK_TREE_MODEL(treestore), &treeiterlevels[j-1]);
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* We are going to collapse all tree branches
|
|
|
|
* at depth greater than 2, but not _yet_; see
|
|
|
|
* the comment at the call to
|
|
|
|
* gtk_tree_view_collapse_row below.
|
|
|
|
*/
|
|
|
|
gtk_tree_view_expand_row(GTK_TREE_VIEW(tree),
|
|
|
|
sp->treepath, false);
|
|
|
|
} else {
|
|
|
|
sp->treepath = NULL;
|
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#else
|
2019-09-08 19:29:00 +00:00
|
|
|
treeitem = gtk_tree_item_new_with_label(c);
|
|
|
|
if (j > 0) {
|
|
|
|
if (!treelevels[j-1]) {
|
|
|
|
treelevels[j-1] = GTK_TREE(gtk_tree_new());
|
|
|
|
gtk_tree_item_set_subtree
|
|
|
|
(treeitemlevels[j-1],
|
|
|
|
GTK_WIDGET(treelevels[j-1]));
|
2007-01-16 18:48:47 +00:00
|
|
|
if (j < 2)
|
|
|
|
gtk_tree_item_expand(treeitemlevels[j-1]);
|
|
|
|
else
|
|
|
|
gtk_tree_item_collapse(treeitemlevels[j-1]);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
gtk_tree_append(treelevels[j-1], treeitem);
|
|
|
|
} else {
|
|
|
|
gtk_tree_append(GTK_TREE(tree), treeitem);
|
|
|
|
}
|
|
|
|
treeitemlevels[j] = GTK_TREE_ITEM(treeitem);
|
|
|
|
treelevels[j] = NULL;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(treeitem), "key_press_event",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(tree_key_press), dp);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(treeitem), "focus_in_event",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(widget_focus), dp);
|
2003-03-18 19:06:51 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_show(treeitem);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (first)
|
|
|
|
gtk_tree_select_child(GTK_TREE(tree), treeitem);
|
|
|
|
sp->treeitem = treeitem;
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
level = j+1;
|
|
|
|
nselparams++;
|
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
w = layout_ctrls(dp, selparams[nselparams-1],
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
&selparams[nselparams-1]->shortcuts, s, NULL);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(panelvbox), w, false, false, 0);
|
2003-03-14 18:35:01 +00:00
|
|
|
gtk_widget_show(w);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 11:37:06 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2015-08-25 18:44:44 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-04-04 11:37:06 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->nselparams = nselparams;
|
|
|
|
dp->selparams = selparams;
|
2008-04-04 11:37:06 +00:00
|
|
|
|
2015-08-25 18:44:44 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In GTK2, we can just do the job right now.
|
|
|
|
*/
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkRequisition req;
|
|
|
|
gtk_widget_size_request(tree, &req);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
initial_treeview_collapse(dp, tree);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_set_size_request(tree, req.width, -1);
|
2008-04-04 11:37:06 +00:00
|
|
|
}
|
2015-08-25 18:44:44 +00:00
|
|
|
#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",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(treeview_map_event), dp);
|
2015-08-25 18:44:44 +00:00
|
|
|
#endif /* GTK 2 vs 3 */
|
|
|
|
#endif /* GTK 2+ vs 1 */
|
2008-04-04 11:37:06 +00:00
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
g_signal_connect(G_OBJECT(treeselection), "changed",
|
2019-09-08 19:29:00 +00:00
|
|
|
G_CALLBACK(treeselection_changed), selparams);
|
2007-01-25 19:33:29 +00:00
|
|
|
#else
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->ntreeitems = nselparams;
|
|
|
|
dp->treeitems = snewn(dp->ntreeitems, GtkWidget *);
|
2003-03-13 19:52:28 +00:00
|
|
|
for (index = 0; index < nselparams; index++) {
|
2019-10-15 19:11:30 +00:00
|
|
|
g_signal_connect(G_OBJECT(selparams[index]->treeitem), "select",
|
2015-08-08 16:29:02 +00:00
|
|
|
G_CALLBACK(treeitem_sel),
|
2019-11-02 08:37:30 +00:00
|
|
|
selparams[index]);
|
2019-10-15 19:11:30 +00:00
|
|
|
dp->treeitems[index] = selparams[index]->treeitem;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->data = conf;
|
|
|
|
dlg_refresh(NULL, dp);
|
2003-03-15 15:50:42 +00:00
|
|
|
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
dp->shortcuts = &selparams[0]->shortcuts;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->currtreeitem = dp->treeitems[0];
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dp->lastfocus = NULL;
|
|
|
|
dp->retval = -1;
|
|
|
|
dp->window = window;
|
2003-03-16 12:37:48 +00:00
|
|
|
|
2018-11-03 10:06:33 +00:00
|
|
|
set_window_icon(window, cfg_icon, n_cfg_icon);
|
2007-01-06 18:27:00 +00:00
|
|
|
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2007-01-16 18:48:47 +00:00
|
|
|
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll),
|
2019-09-08 19:29:00 +00:00
|
|
|
tree);
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2007-01-16 18:48:47 +00:00
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll),
|
2019-09-08 19:29:00 +00:00
|
|
|
GTK_POLICY_NEVER,
|
|
|
|
GTK_POLICY_AUTOMATIC);
|
2007-01-16 18:48:47 +00:00
|
|
|
gtk_widget_show(tree);
|
|
|
|
|
2003-03-18 19:47:28 +00:00
|
|
|
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_show(window);
|
|
|
|
|
2003-03-18 19:06:51 +00:00
|
|
|
/*
|
|
|
|
* Set focus into the first available control.
|
|
|
|
*/
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
for (index = 0; index < dp->ctrlbox->nctrlsets; index++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
struct controlset *s = dp->ctrlbox->ctrlsets[index];
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool done = false;
|
2003-03-18 19:06:51 +00:00
|
|
|
int j;
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (*s->pathname) {
|
2003-03-18 19:06:51 +00:00
|
|
|
for (j = 0; j < s->ncontrols; j++)
|
|
|
|
if (s->ctrls[j]->generic.type != CTRL_TABDELAY &&
|
|
|
|
s->ctrls[j]->generic.type != CTRL_COLUMNS &&
|
|
|
|
s->ctrls[j]->generic.type != CTRL_TEXT) {
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dlg_set_focus(s->ctrls[j], dp);
|
|
|
|
dp->lastfocus = s->ctrls[j];
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
done = true;
|
2003-03-18 19:06:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(window), "destroy",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(dlgparam_destroy), dp);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(window), "key_press_event",
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
G_CALLBACK(win_key_press), dp);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
return window;
|
|
|
|
}
|
2003-03-18 19:06:51 +00:00
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
static void dlgparam_destroy(GtkWidget *widget, gpointer data)
|
|
|
|
{
|
|
|
|
struct dlgparam *dp = (struct dlgparam *)data;
|
2017-11-26 14:37:38 +00:00
|
|
|
dp->after(dp->afterctx, dp->retval);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
dlg_cleanup(dp);
|
|
|
|
ctrl_free_box(dp->ctrlbox);
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2017-11-26 14:29:31 +00:00
|
|
|
if (dp->selparams) {
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
for (size_t i = 0; i < dp->nselparams; i++) {
|
|
|
|
if (dp->selparams[i]->treepath)
|
|
|
|
gtk_tree_path_free(dp->selparams[i]->treepath);
|
|
|
|
sfree(dp->selparams[i]);
|
|
|
|
}
|
2017-11-26 14:29:31 +00:00
|
|
|
sfree(dp->selparams);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
sfree(dp);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
static void messagebox_handler(union control *ctrl, dlgparam *dp,
|
2019-09-08 19:29:00 +00:00
|
|
|
void *data, int event)
|
2003-03-31 11:21:07 +00:00
|
|
|
{
|
|
|
|
if (event == EVENT_ACTION)
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_end(dp, ctrl->generic.context.i);
|
2003-03-31 11:21:07 +00:00
|
|
|
}
|
2017-11-26 14:05:02 +00:00
|
|
|
|
2020-01-29 06:22:01 +00:00
|
|
|
static const struct message_box_button button_array_yn[] = {
|
2017-11-26 14:05:02 +00:00
|
|
|
{"Yes", 'y', +1, 1},
|
|
|
|
{"No", 'n', -1, 0},
|
|
|
|
};
|
|
|
|
const struct message_box_buttons buttons_yn = {
|
|
|
|
button_array_yn, lenof(button_array_yn),
|
|
|
|
};
|
2020-01-29 06:22:01 +00:00
|
|
|
static const struct message_box_button button_array_ok[] = {
|
2017-11-26 14:05:02 +00:00
|
|
|
{"OK", 'o', 1, 1},
|
|
|
|
};
|
|
|
|
const struct message_box_buttons buttons_ok = {
|
|
|
|
button_array_ok, lenof(button_array_ok),
|
|
|
|
};
|
|
|
|
|
2021-03-13 11:05:39 +00:00
|
|
|
static GtkWidget *create_message_box_general(
|
2017-11-26 14:05:02 +00:00
|
|
|
GtkWidget *parentwin, const char *title, const char *msg, int minwid,
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool selectable, const struct message_box_buttons *buttons,
|
2021-03-13 11:05:39 +00:00
|
|
|
post_dialog_fn_t after, void *afterctx,
|
|
|
|
GtkWidget *(*action_postproc)(GtkWidget *, void *), void *postproc_ctx)
|
2003-03-31 11:21:07 +00:00
|
|
|
{
|
|
|
|
GtkWidget *window, *w0, *w1;
|
|
|
|
struct controlset *s0, *s1;
|
2015-12-22 12:32:48 +00:00
|
|
|
union control *c, *textctrl;
|
2017-11-26 14:29:31 +00:00
|
|
|
struct dlgparam *dp;
|
2003-03-31 11:21:07 +00:00
|
|
|
struct Shortcuts scs;
|
2017-11-26 14:05:02 +00:00
|
|
|
int i, index, ncols, min_type;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 14:29:31 +00:00
|
|
|
dp = snew(struct dlgparam);
|
|
|
|
dp->after = after;
|
|
|
|
dp->afterctx = afterctx;
|
|
|
|
|
|
|
|
dlg_init(dp);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
for (index = 0; index < lenof(scs.sc); index++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
scs.sc[index].action = SHORTCUT_EMPTY;
|
2003-03-31 11:21:07 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 14:29:31 +00:00
|
|
|
dp->ctrlbox = ctrl_new_box();
|
2003-03-22 17:00:06 +00:00
|
|
|
|
2015-09-26 12:57:12 +00:00
|
|
|
/*
|
2017-11-26 14:05:02 +00:00
|
|
|
* Count up the number of buttons and find out what kinds there
|
|
|
|
* are.
|
2015-09-26 12:57:12 +00:00
|
|
|
*/
|
2003-03-31 11:21:07 +00:00
|
|
|
ncols = 0;
|
2015-09-26 12:57:12 +00:00
|
|
|
min_type = +1;
|
2017-11-26 14:05:02 +00:00
|
|
|
for (i = 0; i < buttons->nbuttons; i++) {
|
|
|
|
const struct message_box_button *button = &buttons->buttons[i];
|
2019-09-08 19:29:00 +00:00
|
|
|
ncols++;
|
2017-11-26 14:05:02 +00:00
|
|
|
if (min_type > button->type)
|
|
|
|
min_type = button->type;
|
2017-11-26 14:29:31 +00:00
|
|
|
assert(button->value >= 0); /* <0 means no return value available */
|
2003-03-31 11:21:07 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 14:29:31 +00:00
|
|
|
s0 = ctrl_getset(dp->ctrlbox, "", "", "");
|
2003-03-31 11:21:07 +00:00
|
|
|
c = ctrl_columns(s0, 2, 50, 50);
|
|
|
|
c->columns.ncols = s0->ncolumns = ncols;
|
|
|
|
c->columns.percentages = sresize(c->columns.percentages, ncols, int);
|
|
|
|
for (index = 0; index < ncols; index++)
|
2019-09-08 19:29:00 +00:00
|
|
|
c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols;
|
2003-03-31 11:21:07 +00:00
|
|
|
index = 0;
|
2017-11-26 14:05:02 +00:00
|
|
|
for (i = 0; i < buttons->nbuttons; i++) {
|
|
|
|
const struct message_box_button *button = &buttons->buttons[i];
|
2019-09-08 19:29:00 +00:00
|
|
|
c = ctrl_pushbutton(s0, button->title, button->shortcut,
|
2017-11-26 14:05:02 +00:00
|
|
|
HELPCTX(no_help), messagebox_handler,
|
|
|
|
I(button->value));
|
2019-09-08 19:29:00 +00:00
|
|
|
c->generic.column = index++;
|
|
|
|
if (button->type > 0)
|
|
|
|
c->button.isdefault = true;
|
2015-09-26 12:57:12 +00:00
|
|
|
|
|
|
|
/* We always arrange that _some_ button is labelled as
|
|
|
|
* 'iscancel', so that pressing Escape will always cause
|
|
|
|
* win_key_press to do something. The button we choose is
|
|
|
|
* whichever has the smallest type value: this means that real
|
|
|
|
* cancel buttons (labelled -1) will be picked if one is
|
|
|
|
* there, or in cases where the options are yes/no (1,0) then
|
|
|
|
* no will be picked, and if there's only one option (a box
|
|
|
|
* that really is just showing a _message_ and not even asking
|
|
|
|
* a question) then that will be picked. */
|
2019-09-08 19:29:00 +00:00
|
|
|
if (button->type == min_type)
|
|
|
|
c->button.iscancel = true;
|
2003-03-31 11:21:07 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 14:29:31 +00:00
|
|
|
s1 = ctrl_getset(dp->ctrlbox, "x", "", "");
|
2015-12-22 12:32:48 +00:00
|
|
|
textctrl = ctrl_text(s1, msg, HELPCTX(no_help));
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-08-31 14:45:18 +00:00
|
|
|
window = our_dialog_new();
|
2003-03-31 11:21:07 +00:00
|
|
|
gtk_window_set_title(GTK_WINDOW(window), title);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
w0 = layout_ctrls(dp, NULL, &scs, s0, GTK_WINDOW(window));
|
2021-03-13 11:05:39 +00:00
|
|
|
if (action_postproc)
|
|
|
|
w0 = action_postproc(w0, postproc_ctx);
|
2015-08-31 14:45:18 +00:00
|
|
|
our_dialog_set_action_area(GTK_WINDOW(window), w0);
|
2003-03-31 11:21:07 +00:00
|
|
|
gtk_widget_show(w0);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
w1 = layout_ctrls(dp, NULL, &scs, s1, GTK_WINDOW(window));
|
2003-03-31 11:21:07 +00:00
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
|
2015-08-08 17:02:01 +00:00
|
|
|
gtk_widget_set_size_request(w1, minwid+20, -1);
|
2018-10-29 19:50:29 +00:00
|
|
|
our_dialog_add_to_content_area(GTK_WINDOW(window), w1, true, true, 0);
|
2003-03-31 11:21:07 +00:00
|
|
|
gtk_widget_show(w1);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 14:29:31 +00:00
|
|
|
dp->shortcuts = &scs;
|
|
|
|
dp->lastfocus = NULL;
|
|
|
|
dp->retval = 0;
|
|
|
|
dp->window = window;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-12-22 12:32:48 +00:00
|
|
|
if (selectable) {
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2017-11-26 14:29:31 +00:00
|
|
|
struct uctrl *uc = dlg_find_byctrl(dp, textctrl);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_label_set_selectable(GTK_LABEL(uc->text), true);
|
2015-12-22 12:32:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* GTK selectable labels have a habit of selecting their
|
|
|
|
* entire contents when they gain focus. It's ugly to have
|
|
|
|
* text in a message box start up all selected, so we suppress
|
|
|
|
* this by manually selecting none of it - but we must do this
|
|
|
|
* when the widget _already has_ focus, otherwise our work
|
|
|
|
* will be undone when it gains it shortly.
|
|
|
|
*/
|
|
|
|
gtk_widget_grab_focus(uc->text);
|
|
|
|
gtk_label_select_region(GTK_LABEL(uc->text), 0, 0);
|
|
|
|
#else
|
|
|
|
(void)textctrl; /* placate warning */
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
if (parentwin) {
|
2003-04-10 08:53:43 +00:00
|
|
|
set_transient_window_pos(parentwin, window);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_transient_for(GTK_WINDOW(window),
|
|
|
|
GTK_WINDOW(parentwin));
|
2003-03-31 11:21:07 +00:00
|
|
|
} else
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
2015-12-22 12:32:48 +00:00
|
|
|
gtk_container_set_focus_child(GTK_CONTAINER(window), NULL);
|
2003-03-31 11:21:07 +00:00
|
|
|
gtk_widget_show(window);
|
2015-12-22 12:32:48 +00:00
|
|
|
gtk_window_set_focus(GTK_WINDOW(window), NULL);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-03-03 18:18:54 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
dp->currtreeitem = NULL;
|
|
|
|
dp->treeitems = NULL;
|
|
|
|
#else
|
2017-11-26 14:29:31 +00:00
|
|
|
dp->selparams = NULL;
|
2018-03-03 18:18:54 +00:00
|
|
|
#endif
|
2017-11-26 14:29:31 +00:00
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(window), "destroy",
|
2017-11-26 14:29:31 +00:00
|
|
|
G_CALLBACK(dlgparam_destroy), dp);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(window), "key_press_event",
|
2017-11-26 14:29:31 +00:00
|
|
|
G_CALLBACK(win_key_press), dp);
|
|
|
|
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:05:39 +00:00
|
|
|
GtkWidget *create_message_box(
|
|
|
|
GtkWidget *parentwin, const char *title, const char *msg, int minwid,
|
|
|
|
bool selectable, const struct message_box_buttons *buttons,
|
|
|
|
post_dialog_fn_t after, void *afterctx)
|
|
|
|
{
|
|
|
|
return create_message_box_general(
|
|
|
|
parentwin, title, msg, minwid, selectable, buttons, after, afterctx,
|
|
|
|
NULL /* action_postproc */, NULL /* postproc_ctx */);
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:06:32 +00:00
|
|
|
struct verify_ssh_host_key_dialog_ctx {
|
2017-11-26 14:29:42 +00:00
|
|
|
char *host;
|
|
|
|
int port;
|
|
|
|
char *keytype;
|
|
|
|
char *keystr;
|
2021-03-13 11:06:32 +00:00
|
|
|
char *more_info;
|
2017-11-26 14:29:42 +00:00
|
|
|
void (*callback)(void *callback_ctx, int result);
|
|
|
|
void *callback_ctx;
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
Seat *seat;
|
2021-03-13 11:06:32 +00:00
|
|
|
|
|
|
|
GtkWidget *main_dialog;
|
|
|
|
GtkWidget *more_info_dialog;
|
2017-11-26 14:29:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void verify_ssh_host_key_result_callback(void *vctx, int result)
|
|
|
|
{
|
2021-03-13 11:06:32 +00:00
|
|
|
struct verify_ssh_host_key_dialog_ctx *ctx =
|
|
|
|
(struct verify_ssh_host_key_dialog_ctx *)vctx;
|
2017-11-26 14:29:42 +00:00
|
|
|
|
|
|
|
if (result >= 0) {
|
|
|
|
int logical_result;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the dialog-box return value (one of three
|
|
|
|
* possibilities) into the return value we pass back to the SSH
|
|
|
|
* code (one of only two possibilities, because the SSH code
|
|
|
|
* doesn't care whether we saved the host key or not).
|
|
|
|
*/
|
|
|
|
if (result == 2) {
|
|
|
|
store_host_key(ctx->host, ctx->port, ctx->keytype, ctx->keystr);
|
|
|
|
logical_result = 1; /* continue with connection */
|
|
|
|
} else if (result == 1) {
|
|
|
|
logical_result = 1; /* continue with connection */
|
|
|
|
} else {
|
|
|
|
logical_result = 0; /* do not continue with connection */
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->callback(ctx->callback_ctx, logical_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up this context structure, whether or not a result was
|
|
|
|
* ever actually delivered from the dialog box.
|
|
|
|
*/
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
unregister_dialog(ctx->seat, DIALOG_SLOT_NETWORK_PROMPT);
|
2017-11-26 14:29:42 +00:00
|
|
|
|
2021-03-13 11:06:32 +00:00
|
|
|
if (ctx->more_info_dialog)
|
|
|
|
gtk_widget_destroy(ctx->more_info_dialog);
|
|
|
|
|
2017-11-26 14:29:42 +00:00
|
|
|
sfree(ctx->host);
|
|
|
|
sfree(ctx->keytype);
|
|
|
|
sfree(ctx->keystr);
|
2021-03-13 11:06:32 +00:00
|
|
|
sfree(ctx->more_info);
|
2017-11-26 14:29:42 +00:00
|
|
|
sfree(ctx);
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:06:32 +00:00
|
|
|
static GtkWidget *add_more_info_button(GtkWidget *w, void *vctx)
|
|
|
|
{
|
|
|
|
GtkWidget *box = gtk_hbox_new(false, 10);
|
|
|
|
gtk_widget_show(box);
|
|
|
|
gtk_box_pack_end(GTK_BOX(box), w, false, true, 0);
|
|
|
|
GtkWidget *button = gtk_button_new_with_label("More info...");
|
|
|
|
gtk_widget_show(button);
|
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, false, true, 0);
|
|
|
|
*(GtkWidget **)vctx = button;
|
|
|
|
return box;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void more_info_closed(void *vctx, int result)
|
|
|
|
{
|
|
|
|
struct verify_ssh_host_key_dialog_ctx *ctx =
|
|
|
|
(struct verify_ssh_host_key_dialog_ctx *)vctx;
|
|
|
|
|
|
|
|
ctx->more_info_dialog = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void more_info_button_clicked(GtkButton *button, gpointer vctx)
|
|
|
|
{
|
|
|
|
struct verify_ssh_host_key_dialog_ctx *ctx =
|
|
|
|
(struct verify_ssh_host_key_dialog_ctx *)vctx;
|
|
|
|
|
|
|
|
if (ctx->more_info_dialog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ctx->more_info_dialog = create_message_box(
|
|
|
|
ctx->main_dialog, "Host key information", ctx->more_info,
|
|
|
|
string_width("SHA256 fingerprint: ecdsa-sha2-nistp521 521 "
|
|
|
|
"abcdefghkmnopqrsuvwxyzABCDEFGHJKLMNOPQRSTUW"), true,
|
|
|
|
&buttons_ok, more_info_closed, ctx);
|
|
|
|
}
|
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
int gtk_seat_verify_ssh_host_key(
|
2021-03-13 10:59:47 +00:00
|
|
|
Seat *seat, const char *host, int port, const char *keytype,
|
|
|
|
char *keystr, const char *keydisp, char **fingerprints,
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
void (*callback)(void *ctx, int result), void *ctx)
|
2003-03-31 11:21:07 +00:00
|
|
|
{
|
|
|
|
static const char absenttxt[] =
|
2021-09-15 13:41:00 +00:00
|
|
|
"The host key is not cached for this server:\n\n"
|
|
|
|
"%s (port %d)\n\n"
|
|
|
|
"You have no guarantee that the server is the computer "
|
|
|
|
"you think it is.\n"
|
|
|
|
"The server's %s key fingerprint is:\n\n"
|
|
|
|
"%s\n\n"
|
2019-09-08 19:29:00 +00:00
|
|
|
"If you trust this host, press \"Accept\" to add the key to "
|
|
|
|
"PuTTY's cache and carry on connecting.\n"
|
|
|
|
"If you want to carry on connecting just once, without "
|
|
|
|
"adding the key to the cache, press \"Connect Once\".\n"
|
|
|
|
"If you do not trust this host, press \"Cancel\" to abandon the "
|
|
|
|
"connection.";
|
2003-03-31 11:21:07 +00:00
|
|
|
static const char wrongtxt[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
2021-09-15 13:41:00 +00:00
|
|
|
"The host key does not match the one PuTTY has cached "
|
|
|
|
"for this server:\n\n"
|
|
|
|
"%s (port %d)\n\n"
|
|
|
|
"This means that either the server administrator has "
|
|
|
|
"changed the host key, or you have actually connected "
|
2019-09-08 19:29:00 +00:00
|
|
|
"to another computer pretending to be the server.\n"
|
2021-09-15 13:41:00 +00:00
|
|
|
"The new %s key fingerprint is:\n\n"
|
|
|
|
"%s\n\n"
|
2019-09-08 19:29:00 +00:00
|
|
|
"If you were expecting this change and trust the new key, "
|
|
|
|
"press \"Accept\" to update PuTTY's cache and continue connecting.\n"
|
|
|
|
"If you want to carry on connecting but without updating "
|
|
|
|
"the cache, press \"Connect Once\".\n"
|
|
|
|
"If you want to abandon the connection completely, press "
|
|
|
|
"\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "
|
|
|
|
"safe choice.";
|
2017-11-26 14:05:02 +00:00
|
|
|
static const struct message_box_button button_array_hostkey[] = {
|
|
|
|
{"Accept", 'a', 0, 2},
|
|
|
|
{"Connect Once", 'o', 0, 1},
|
|
|
|
{"Cancel", 'c', -1, 0},
|
|
|
|
};
|
|
|
|
static const struct message_box_buttons buttons_hostkey = {
|
|
|
|
button_array_hostkey, lenof(button_array_hostkey),
|
|
|
|
};
|
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
char *text;
|
|
|
|
int ret;
|
2021-03-13 11:06:32 +00:00
|
|
|
struct verify_ssh_host_key_dialog_ctx *result_ctx;
|
2017-11-26 14:29:42 +00:00
|
|
|
GtkWidget *mainwin, *msgbox;
|
2003-03-31 11:21:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify the key.
|
|
|
|
*/
|
|
|
|
ret = verify_host_key(host, port, keytype, keystr);
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (ret == 0) /* success - key matched OK */
|
|
|
|
return 1;
|
2003-03-31 11:21:07 +00:00
|
|
|
|
2021-03-13 10:59:47 +00:00
|
|
|
FingerprintType fptype_default =
|
|
|
|
ssh2_pick_default_fingerprint(fingerprints);
|
|
|
|
|
2021-09-15 13:41:00 +00:00
|
|
|
text = dupprintf((ret == 2 ? wrongtxt : absenttxt), host, port,
|
|
|
|
keytype, fingerprints[fptype_default]);
|
2003-03-31 11:21:07 +00:00
|
|
|
|
2021-03-13 11:06:32 +00:00
|
|
|
result_ctx = snew(struct verify_ssh_host_key_dialog_ctx);
|
2017-11-26 14:29:42 +00:00
|
|
|
result_ctx->callback = callback;
|
|
|
|
result_ctx->callback_ctx = ctx;
|
|
|
|
result_ctx->host = dupstr(host);
|
|
|
|
result_ctx->port = port;
|
|
|
|
result_ctx->keytype = dupstr(keytype);
|
|
|
|
result_ctx->keystr = dupstr(keystr);
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
result_ctx->seat = seat;
|
2017-11-26 14:29:42 +00:00
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
mainwin = GTK_WIDGET(gtk_seat_get_window(seat));
|
2021-03-13 11:06:32 +00:00
|
|
|
GtkWidget *more_info_button = NULL;
|
|
|
|
msgbox = create_message_box_general(
|
2021-03-13 10:59:47 +00:00
|
|
|
mainwin, "PuTTY Security Alert", text,
|
|
|
|
string_width(fingerprints[fptype_default]), true,
|
2021-03-13 11:06:32 +00:00
|
|
|
&buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx,
|
|
|
|
add_more_info_button, &more_info_button);
|
|
|
|
|
|
|
|
result_ctx->main_dialog = msgbox;
|
|
|
|
result_ctx->more_info_dialog = NULL;
|
|
|
|
|
|
|
|
strbuf *sb = strbuf_new();
|
|
|
|
if (fingerprints[SSH_FPTYPE_SHA256])
|
|
|
|
strbuf_catf(sb, "SHA256 fingerprint: %s\n",
|
|
|
|
fingerprints[SSH_FPTYPE_SHA256]);
|
|
|
|
if (fingerprints[SSH_FPTYPE_MD5])
|
|
|
|
strbuf_catf(sb, "MD5 fingerprint: %s\n",
|
|
|
|
fingerprints[SSH_FPTYPE_MD5]);
|
|
|
|
strbuf_catf(sb, "Full text of host's public key:");
|
|
|
|
/* We have to manually wrap the public key, or else the GtkLabel
|
|
|
|
* will resize itself to accommodate the longest word, which will
|
|
|
|
* lead to a hilariously wide message box. */
|
|
|
|
for (const char *p = keydisp, *q = p + strlen(p); p < q ;) {
|
|
|
|
size_t linelen = q-p;
|
|
|
|
if (linelen > 72)
|
|
|
|
linelen = 72;
|
|
|
|
put_byte(sb, '\n');
|
|
|
|
put_data(sb, p, linelen);
|
|
|
|
p += linelen;
|
|
|
|
}
|
|
|
|
result_ctx->more_info = strbuf_to_str(sb);
|
|
|
|
|
|
|
|
g_signal_connect(G_OBJECT(more_info_button), "clicked",
|
|
|
|
G_CALLBACK(more_info_button_clicked), result_ctx);
|
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
register_dialog(seat, DIALOG_SLOT_NETWORK_PROMPT, msgbox);
|
2003-03-31 11:21:07 +00:00
|
|
|
|
|
|
|
sfree(text);
|
|
|
|
|
2017-11-26 14:29:42 +00:00
|
|
|
return -1; /* dialog still in progress */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
struct simple_prompt_result_ctx {
|
2017-11-26 15:13:08 +00:00
|
|
|
void (*callback)(void *callback_ctx, int result);
|
|
|
|
void *callback_ctx;
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
Seat *seat;
|
2017-11-26 16:56:03 +00:00
|
|
|
enum DialogSlot dialog_slot;
|
2017-11-26 15:13:08 +00:00
|
|
|
};
|
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
static void simple_prompt_result_callback(void *vctx, int result)
|
2017-11-26 15:13:08 +00:00
|
|
|
{
|
2017-11-26 16:56:03 +00:00
|
|
|
struct simple_prompt_result_ctx *ctx =
|
|
|
|
(struct simple_prompt_result_ctx *)vctx;
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2019-01-13 17:14:08 +00:00
|
|
|
unregister_dialog(ctx->seat, ctx->dialog_slot);
|
|
|
|
|
2017-11-26 15:13:08 +00:00
|
|
|
if (result >= 0)
|
|
|
|
ctx->callback(ctx->callback_ctx, result);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up this context structure, whether or not a result was
|
|
|
|
* ever actually delivered from the dialog box.
|
|
|
|
*/
|
|
|
|
sfree(ctx);
|
|
|
|
}
|
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
/*
|
2004-12-23 02:24:07 +00:00
|
|
|
* Ask whether the selected algorithm is acceptable (since it was
|
2003-03-31 11:21:07 +00:00
|
|
|
* below the configured 'warn' threshold).
|
|
|
|
*/
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
int gtk_seat_confirm_weak_crypto_primitive(
|
|
|
|
Seat *seat, const char *algtype, const char *algname,
|
|
|
|
void (*callback)(void *ctx, int result), void *ctx)
|
2003-03-31 11:21:07 +00:00
|
|
|
{
|
|
|
|
static const char msg[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"The first %s supported by the server is "
|
|
|
|
"%s, which is below the configured warning threshold.\n"
|
|
|
|
"Continue with connection?";
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
char *text;
|
2017-11-26 16:56:03 +00:00
|
|
|
struct simple_prompt_result_ctx *result_ctx;
|
2017-11-26 15:13:08 +00:00
|
|
|
GtkWidget *mainwin, *msgbox;
|
2003-03-31 11:21:07 +00:00
|
|
|
|
2004-12-23 02:24:07 +00:00
|
|
|
text = dupprintf(msg, algtype, algname);
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx = snew(struct simple_prompt_result_ctx);
|
2017-11-26 15:13:08 +00:00
|
|
|
result_ctx->callback = callback;
|
|
|
|
result_ctx->callback_ctx = ctx;
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
result_ctx->seat = seat;
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT;
|
2017-11-26 15:13:08 +00:00
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
mainwin = GTK_WIDGET(gtk_seat_get_window(seat));
|
2017-11-26 15:13:08 +00:00
|
|
|
msgbox = create_message_box(
|
|
|
|
mainwin, "PuTTY Security Alert", text,
|
|
|
|
string_width("Reasonably long line of text as a width template"),
|
2018-10-29 19:50:29 +00:00
|
|
|
false, &buttons_yn, simple_prompt_result_callback, result_ctx);
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
register_dialog(seat, result_ctx->dialog_slot, msgbox);
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2016-03-27 17:08:49 +00:00
|
|
|
sfree(text);
|
|
|
|
|
2017-11-26 15:13:08 +00:00
|
|
|
return -1; /* dialog still in progress */
|
2016-03-27 17:08:49 +00:00
|
|
|
}
|
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
int gtk_seat_confirm_weak_cached_hostkey(
|
|
|
|
Seat *seat, const char *algname, const char *betteralgs,
|
|
|
|
void (*callback)(void *ctx, int result), void *ctx)
|
2016-03-27 17:08:49 +00:00
|
|
|
{
|
|
|
|
static const char msg[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"The first host key type we have stored for this server\n"
|
|
|
|
"is %s, which is below the configured warning threshold.\n"
|
|
|
|
"The server also provides the following types of host key\n"
|
2016-03-27 17:08:49 +00:00
|
|
|
"above the threshold, which we do not have stored:\n"
|
|
|
|
"%s\n"
|
2019-09-08 19:29:00 +00:00
|
|
|
"Continue with connection?";
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2016-03-27 17:08:49 +00:00
|
|
|
char *text;
|
2017-11-26 16:56:03 +00:00
|
|
|
struct simple_prompt_result_ctx *result_ctx;
|
2017-11-26 15:13:08 +00:00
|
|
|
GtkWidget *mainwin, *msgbox;
|
2016-03-27 17:08:49 +00:00
|
|
|
|
|
|
|
text = dupprintf(msg, algname, betteralgs);
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx = snew(struct simple_prompt_result_ctx);
|
2017-11-26 15:13:08 +00:00
|
|
|
result_ctx->callback = callback;
|
|
|
|
result_ctx->callback_ctx = ctx;
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
result_ctx->seat = seat;
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT;
|
2017-11-26 15:13:08 +00:00
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
mainwin = GTK_WIDGET(gtk_seat_get_window(seat));
|
2017-11-26 15:13:08 +00:00
|
|
|
msgbox = create_message_box(
|
|
|
|
mainwin, "PuTTY Security Alert", text,
|
|
|
|
string_width("is ecdsa-nistp521, which is below the configured"
|
|
|
|
" warning threshold."),
|
2018-10-29 19:50:29 +00:00
|
|
|
false, &buttons_yn, simple_prompt_result_callback, result_ctx);
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
register_dialog(seat, result_ctx->dialog_slot, msgbox);
|
2017-11-26 15:13:08 +00:00
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
sfree(text);
|
|
|
|
|
2017-11-26 15:13:08 +00:00
|
|
|
return -1; /* dialog still in progress */
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2003-03-31 11:21:07 +00:00
|
|
|
void old_keyfile_warning(void)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2003-03-31 11:21:07 +00:00
|
|
|
/*
|
|
|
|
* This should never happen on Unix. We hope.
|
|
|
|
*/
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 10:15:42 +00:00
|
|
|
void nonfatal_message_box(void *window, const char *msg)
|
2013-07-19 17:44:28 +00:00
|
|
|
{
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
char *title = dupcat(appname, " Error");
|
2017-11-26 17:55:00 +00:00
|
|
|
create_message_box(
|
|
|
|
window, title, msg,
|
|
|
|
string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"),
|
2018-10-29 19:50:29 +00:00
|
|
|
false, &buttons_ok, trivial_post_dialog_fn, NULL);
|
2017-11-26 17:55:00 +00:00
|
|
|
sfree(title);
|
2013-07-19 17:44:28 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 10:15:42 +00:00
|
|
|
void nonfatal(const char *p, ...)
|
2013-07-19 17:44:28 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char *msg;
|
|
|
|
va_start(ap, p);
|
|
|
|
msg = dupvprintf(p, ap);
|
|
|
|
va_end(ap);
|
2013-09-23 14:35:08 +00:00
|
|
|
nonfatal_message_box(NULL, msg);
|
2013-07-19 17:44:28 +00:00
|
|
|
sfree(msg);
|
|
|
|
}
|
|
|
|
|
2003-04-05 16:05:00 +00:00
|
|
|
static GtkWidget *aboutbox = NULL;
|
|
|
|
|
2021-04-05 16:50:02 +00:00
|
|
|
static void about_window_destroyed(GtkWidget *widget, gpointer data)
|
|
|
|
{
|
|
|
|
aboutbox = NULL;
|
|
|
|
}
|
|
|
|
|
2003-04-05 16:05:00 +00:00
|
|
|
static void about_close_clicked(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
gtk_widget_destroy(aboutbox);
|
|
|
|
aboutbox = NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-26 13:09:27 +00:00
|
|
|
static void about_key_press(GtkWidget *widget, GdkEventKey *event,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
if (event->keyval == GDK_KEY_Escape && aboutbox) {
|
|
|
|
gtk_widget_destroy(aboutbox);
|
|
|
|
aboutbox = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-04-05 16:05:00 +00:00
|
|
|
static void licence_clicked(GtkButton *button, gpointer data)
|
|
|
|
{
|
|
|
|
char *title;
|
|
|
|
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
title = dupcat(appname, " Licence");
|
2003-04-05 16:05:00 +00:00
|
|
|
assert(aboutbox != NULL);
|
2017-11-26 17:32:01 +00:00
|
|
|
create_message_box(aboutbox, title, LICENCE_TEXT("\n\n"),
|
|
|
|
string_width("LONGISH LINE OF TEXT SO THE LICENCE"
|
|
|
|
" BOX ISN'T EXCESSIVELY TALL AND THIN"),
|
2018-10-29 19:50:29 +00:00
|
|
|
true, &buttons_ok, trivial_post_dialog_fn, NULL);
|
2003-04-05 16:05:00 +00:00
|
|
|
sfree(title);
|
|
|
|
}
|
|
|
|
|
2003-04-10 08:53:43 +00:00
|
|
|
void about_box(void *window)
|
2003-04-05 16:05:00 +00:00
|
|
|
{
|
|
|
|
GtkWidget *w;
|
2015-08-31 14:45:18 +00:00
|
|
|
GtkBox *action_area;
|
2003-04-05 16:05:00 +00:00
|
|
|
char *title;
|
|
|
|
|
|
|
|
if (aboutbox) {
|
|
|
|
gtk_widget_grab_focus(aboutbox);
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2003-04-05 16:05:00 +00:00
|
|
|
}
|
|
|
|
|
2015-08-31 14:45:18 +00:00
|
|
|
aboutbox = our_dialog_new();
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10);
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
title = dupcat("About ", appname);
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_window_set_title(GTK_WINDOW(aboutbox), title);
|
|
|
|
sfree(title);
|
|
|
|
|
2021-04-05 16:50:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(aboutbox), "destroy",
|
|
|
|
G_CALLBACK(about_window_destroyed), NULL);
|
|
|
|
|
2003-04-05 16:05:00 +00:00
|
|
|
w = gtk_button_new_with_label("Close");
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_widget_set_can_default(w, true);
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_window_set_default(GTK_WINDOW(aboutbox), w);
|
2015-08-31 14:45:18 +00:00
|
|
|
action_area = our_dialog_make_action_hbox(GTK_WINDOW(aboutbox));
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_box_pack_end(action_area, w, false, false, 0);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "clicked",
|
|
|
|
G_CALLBACK(about_close_clicked), NULL);
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_widget_show(w);
|
|
|
|
|
|
|
|
w = gtk_button_new_with_label("View Licence");
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_widget_set_can_default(w, true);
|
|
|
|
gtk_box_pack_end(action_area, w, false, false, 0);
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(w), "clicked",
|
|
|
|
G_CALLBACK(licence_clicked), NULL);
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_widget_show(w);
|
|
|
|
|
2015-12-22 12:32:48 +00:00
|
|
|
{
|
2017-01-21 14:55:53 +00:00
|
|
|
char *buildinfo_text = buildinfo("\n");
|
2015-12-22 12:32:48 +00:00
|
|
|
char *label_text = dupprintf
|
2017-01-21 14:55:53 +00:00
|
|
|
("%s\n\n%s\n\n%s\n\n%s",
|
|
|
|
appname, ver, buildinfo_text,
|
2015-12-22 12:43:31 +00:00
|
|
|
"Copyright " SHORT_COPYRIGHT_DETAILS ". All rights reserved");
|
2015-12-22 12:32:48 +00:00
|
|
|
w = gtk_label_new(label_text);
|
|
|
|
gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_CENTER);
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_label_set_selectable(GTK_LABEL(w), true);
|
2015-12-22 12:32:48 +00:00
|
|
|
#endif
|
|
|
|
sfree(label_text);
|
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
our_dialog_add_to_content_area(GTK_WINDOW(aboutbox), w, false, false, 0);
|
2015-12-22 12:32:48 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
/*
|
2017-11-26 20:01:16 +00:00
|
|
|
* Same precautions against initial select-all as in
|
|
|
|
* create_message_box().
|
2015-12-22 12:32:48 +00:00
|
|
|
*/
|
|
|
|
gtk_widget_grab_focus(w);
|
|
|
|
gtk_label_select_region(GTK_LABEL(w), 0, 0);
|
|
|
|
#endif
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_widget_show(w);
|
|
|
|
|
2015-09-26 13:09:27 +00:00
|
|
|
g_signal_connect(G_OBJECT(aboutbox), "key_press_event",
|
|
|
|
G_CALLBACK(about_key_press), NULL);
|
|
|
|
|
2003-04-10 08:53:43 +00:00
|
|
|
set_transient_window_pos(GTK_WIDGET(window), aboutbox);
|
2017-12-18 11:46:48 +00:00
|
|
|
if (window)
|
|
|
|
gtk_window_set_transient_for(GTK_WINDOW(aboutbox),
|
|
|
|
GTK_WINDOW(window));
|
2015-12-22 12:32:48 +00:00
|
|
|
gtk_container_set_focus_child(GTK_CONTAINER(aboutbox), NULL);
|
2003-04-05 16:05:00 +00:00
|
|
|
gtk_widget_show(aboutbox);
|
2015-12-22 12:32:48 +00:00
|
|
|
gtk_window_set_focus(GTK_WINDOW(aboutbox), NULL);
|
2003-04-05 16:05:00 +00:00
|
|
|
}
|
2003-04-09 18:46:45 +00:00
|
|
|
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
#define LOGEVENT_INITIAL_MAX 128
|
|
|
|
#define LOGEVENT_CIRCULAR_MAX 128
|
|
|
|
|
2003-04-09 18:46:45 +00:00
|
|
|
struct eventlog_stuff {
|
|
|
|
GtkWidget *parentwin, *window;
|
|
|
|
struct controlbox *eventbox;
|
|
|
|
struct Shortcuts scs;
|
|
|
|
struct dlgparam dp;
|
|
|
|
union control *listctrl;
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
char **events_initial;
|
|
|
|
char **events_circular;
|
|
|
|
int ninitial, ncircular, circular_first;
|
2018-12-01 09:35:52 +00:00
|
|
|
strbuf *seldata;
|
2003-04-11 17:40:52 +00:00
|
|
|
int sellen;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool ignore_selchange;
|
2003-04-09 18:46:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void eventlog_destroy(GtkWidget *widget, gpointer data)
|
|
|
|
{
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *es = (eventlog_stuff *)data;
|
2003-04-09 18:46:45 +00:00
|
|
|
|
|
|
|
es->window = NULL;
|
|
|
|
dlg_cleanup(&es->dp);
|
|
|
|
ctrl_free_box(es->eventbox);
|
|
|
|
}
|
2018-09-13 11:58:44 +00:00
|
|
|
static void eventlog_ok_handler(union control *ctrl, dlgparam *dp,
|
2019-09-08 19:29:00 +00:00
|
|
|
void *data, int event)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
|
|
|
if (event == EVENT_ACTION)
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_end(dp, 0);
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
2018-09-13 11:58:44 +00:00
|
|
|
static void eventlog_list_handler(union control *ctrl, dlgparam *dp,
|
2019-09-08 19:29:00 +00:00
|
|
|
void *data, int event)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *es = (eventlog_stuff *)data;
|
2003-04-09 18:46:45 +00:00
|
|
|
|
|
|
|
if (event == EVENT_REFRESH) {
|
2019-09-08 19:29:00 +00:00
|
|
|
int i;
|
2003-04-09 18:46:45 +00:00
|
|
|
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_update_start(ctrl, dp);
|
|
|
|
dlg_listbox_clear(ctrl, dp);
|
2019-09-08 19:29:00 +00:00
|
|
|
for (i = 0; i < es->ninitial; i++) {
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_listbox_add(ctrl, dp, es->events_initial[i]);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < es->ncircular; i++) {
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_listbox_add(ctrl, dp, es->events_circular[(es->circular_first + i) % LOGEVENT_CIRCULAR_MAX]);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2018-09-13 11:58:44 +00:00
|
|
|
dlg_update_done(ctrl, dp);
|
2003-04-11 17:40:52 +00:00
|
|
|
} else if (event == EVENT_SELCHANGE) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this SELCHANGE event is happening as a result of
|
|
|
|
* deliberate deselection because someone else has grabbed
|
|
|
|
* the selection, the last thing we want to do is pre-empt
|
|
|
|
* them.
|
|
|
|
*/
|
|
|
|
if (es->ignore_selchange)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct the data to use as the selection.
|
|
|
|
*/
|
2020-01-21 20:16:28 +00:00
|
|
|
strbuf_clear(es->seldata);
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
for (i = 0; i < es->ninitial; i++) {
|
2018-12-01 09:35:52 +00:00
|
|
|
if (dlg_listbox_issel(ctrl, dp, i))
|
|
|
|
strbuf_catf(es->seldata, "%s\n", es->events_initial[i]);
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < es->ncircular; i++) {
|
2018-09-13 11:58:44 +00:00
|
|
|
if (dlg_listbox_issel(ctrl, dp, es->ninitial + i)) {
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
int j = (es->circular_first + i) % LOGEVENT_CIRCULAR_MAX;
|
2018-12-01 09:35:52 +00:00
|
|
|
strbuf_catf(es->seldata, "%s\n", es->events_circular[j]);
|
2003-04-11 17:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gtk_selection_owner_set(es->window, GDK_SELECTION_PRIMARY,
|
|
|
|
GDK_CURRENT_TIME)) {
|
|
|
|
gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,
|
|
|
|
GDK_SELECTION_TYPE_STRING, 1);
|
|
|
|
gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,
|
|
|
|
compound_text_atom, 1);
|
|
|
|
}
|
|
|
|
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
}
|
2003-04-11 17:40:52 +00:00
|
|
|
|
|
|
|
void eventlog_selection_get(GtkWidget *widget, GtkSelectionData *seldata,
|
|
|
|
guint info, guint time_stamp, gpointer data)
|
|
|
|
{
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *es = (eventlog_stuff *)data;
|
2003-04-11 17:40:52 +00:00
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
gtk_selection_data_set(seldata, gtk_selection_data_get_target(seldata), 8,
|
2018-12-01 09:35:52 +00:00
|
|
|
es->seldata->u, es->seldata->len);
|
2003-04-11 17:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gint eventlog_selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *es = (eventlog_stuff *)data;
|
2003-04-11 17:40:52 +00:00
|
|
|
struct uctrl *uc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deselect everything in the list box.
|
|
|
|
*/
|
|
|
|
uc = dlg_find_byctrl(&es->dp, es->listctrl);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
es->ignore_selchange = true;
|
2008-04-04 10:56:26 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
assert(uc->list);
|
|
|
|
gtk_list_unselect_all(GTK_LIST(uc->list));
|
|
|
|
#else
|
2008-04-04 10:16:24 +00:00
|
|
|
assert(uc->treeview);
|
2008-04-02 14:48:06 +00:00
|
|
|
gtk_tree_selection_unselect_all
|
2019-09-08 19:29:00 +00:00
|
|
|
(gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview)));
|
2008-04-04 10:56:26 +00:00
|
|
|
#endif
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
es->ignore_selchange = false;
|
2003-04-11 17:40:52 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
return true;
|
2003-04-11 17:40:52 +00:00
|
|
|
}
|
|
|
|
|
2018-10-08 18:30:01 +00:00
|
|
|
void showeventlog(eventlog_stuff *es, void *parentwin)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
|
|
|
GtkWidget *window, *w0, *w1;
|
|
|
|
GtkWidget *parent = GTK_WIDGET(parentwin);
|
|
|
|
struct controlset *s0, *s1;
|
|
|
|
union control *c;
|
2008-04-02 14:48:06 +00:00
|
|
|
int index;
|
2003-04-09 18:46:45 +00:00
|
|
|
char *title;
|
|
|
|
|
|
|
|
if (es->window) {
|
|
|
|
gtk_widget_grab_focus(es->window);
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dlg_init(&es->dp);
|
|
|
|
|
|
|
|
for (index = 0; index < lenof(es->scs.sc); index++) {
|
2019-09-08 19:29:00 +00:00
|
|
|
es->scs.sc[index].action = SHORTCUT_EMPTY;
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
es->eventbox = ctrl_new_box();
|
|
|
|
|
|
|
|
s0 = ctrl_getset(es->eventbox, "", "", "");
|
|
|
|
ctrl_columns(s0, 3, 33, 34, 33);
|
|
|
|
c = ctrl_pushbutton(s0, "Close", 'c', HELPCTX(no_help),
|
2019-09-08 19:29:00 +00:00
|
|
|
eventlog_ok_handler, P(NULL));
|
2003-04-09 18:46:45 +00:00
|
|
|
c->button.column = 1;
|
2018-10-29 19:50:29 +00:00
|
|
|
c->button.isdefault = true;
|
2003-04-09 18:46:45 +00:00
|
|
|
|
|
|
|
s1 = ctrl_getset(es->eventbox, "x", "", "");
|
|
|
|
es->listctrl = c = ctrl_listbox(s1, NULL, NO_SHORTCUT, HELPCTX(no_help),
|
2019-09-08 19:29:00 +00:00
|
|
|
eventlog_list_handler, P(es));
|
2003-04-09 18:46:45 +00:00
|
|
|
c->listbox.height = 10;
|
|
|
|
c->listbox.multisel = 2;
|
|
|
|
c->listbox.ncols = 3;
|
|
|
|
c->listbox.percentages = snewn(3, int);
|
|
|
|
c->listbox.percentages[0] = 25;
|
|
|
|
c->listbox.percentages[1] = 10;
|
|
|
|
c->listbox.percentages[2] = 65;
|
|
|
|
|
2015-08-31 14:45:18 +00:00
|
|
|
es->window = window = our_dialog_new();
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
title = dupcat(appname, " Event Log");
|
2003-04-09 18:46:45 +00:00
|
|
|
gtk_window_set_title(GTK_WINDOW(window), title);
|
|
|
|
sfree(title);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
w0 = layout_ctrls(&es->dp, NULL, &es->scs, s0, GTK_WINDOW(window));
|
2015-08-31 14:45:18 +00:00
|
|
|
our_dialog_set_action_area(GTK_WINDOW(window), w0);
|
2003-04-09 18:46:45 +00:00
|
|
|
gtk_widget_show(w0);
|
Don't implicitly load a session if Session pane not active.
If you select an entry in the saved sessions list box, but without
double-clicking to actually load it, and then you hit OK, the config-
box code will automatically load it. That just saves one click in a
common situation.
But in order to load that session, the config-box system first has to
ask the list-box control _which_ session is selected. On Windows, this
causes an assertion failure if the user has switched away from the
Session panel to some other panel of the config box, because when the
list box isn't on screen, its Windows control object is actually
destroyed.
I think a sensible answer is that we shouldn't be doing that implicit
load behaviour in any case if the list box isn't _visible_: silently
loading and launching a session someone selected a lot of UI actions
ago wasn't really the point. So now I make that behaviour only happen
when the list box (i.e. the Session panel) _is_ visible. That should
prevent the assertion failure on Windows, but the UI effect is cross-
platform, applying even on GTK where the control objects for invisible
panels persist and so the assertion failure didn't happen. I think
it's a reasonable UI change to make globally.
In order to implement it, I've had to invent a new query function so
that config.c can tell whether a given control is visible. In order to
do that on GTK, I had to give each control a pointer to the 'selparam'
structure describing its config-box pane, so that query function could
check it against the current one - and in order to do _that_, I had to
first arrange that those 'selparam' structures have stable addresses
from the moment they're first created, which meant adding a layer of
indirection so that the array of them in the top-level dlgparam
structure is now an array of _pointers_ rather than of actual structs.
(That way, realloc half way through config box creation can't
invalidate the important pointer values.)
2019-06-29 16:12:47 +00:00
|
|
|
w1 = layout_ctrls(&es->dp, NULL, &es->scs, s1, GTK_WINDOW(window));
|
2003-04-09 18:46:45 +00:00
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
|
2015-08-08 17:02:01 +00:00
|
|
|
gtk_widget_set_size_request(w1, 20 + string_width
|
|
|
|
("LINE OF TEXT GIVING WIDTH OF EVENT LOG IS "
|
|
|
|
"QUITE LONG 'COS SSH LOG ENTRIES ARE WIDE"),
|
|
|
|
-1);
|
2018-10-29 19:50:29 +00:00
|
|
|
our_dialog_add_to_content_area(GTK_WINDOW(window), w1, true, true, 0);
|
2003-04-09 18:46:45 +00:00
|
|
|
gtk_widget_show(w1);
|
|
|
|
|
|
|
|
es->dp.data = es;
|
|
|
|
es->dp.shortcuts = &es->scs;
|
|
|
|
es->dp.lastfocus = NULL;
|
|
|
|
es->dp.retval = 0;
|
|
|
|
es->dp.window = window;
|
|
|
|
|
|
|
|
dlg_refresh(NULL, &es->dp);
|
|
|
|
|
|
|
|
if (parent) {
|
2003-04-10 08:53:43 +00:00
|
|
|
set_transient_window_pos(parent, window);
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_transient_for(GTK_WINDOW(window),
|
|
|
|
GTK_WINDOW(parent));
|
2003-04-09 18:46:45 +00:00
|
|
|
} else
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
2003-04-09 18:46:45 +00:00
|
|
|
gtk_widget_show(window);
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
g_signal_connect(G_OBJECT(window), "destroy",
|
|
|
|
G_CALLBACK(eventlog_destroy), es);
|
|
|
|
g_signal_connect(G_OBJECT(window), "key_press_event",
|
|
|
|
G_CALLBACK(win_key_press), &es->dp);
|
|
|
|
g_signal_connect(G_OBJECT(window), "selection_get",
|
|
|
|
G_CALLBACK(eventlog_selection_get), es);
|
|
|
|
g_signal_connect(G_OBJECT(window), "selection_clear_event",
|
|
|
|
G_CALLBACK(eventlog_selection_clear), es);
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *eventlogstuff_new(void)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
2018-10-08 18:30:01 +00:00
|
|
|
eventlog_stuff *es = snew(eventlog_stuff);
|
2003-04-09 18:46:45 +00:00
|
|
|
memset(es, 0, sizeof(*es));
|
2018-12-01 09:35:52 +00:00
|
|
|
es->seldata = strbuf_new();
|
2003-04-09 18:46:45 +00:00
|
|
|
return es;
|
|
|
|
}
|
|
|
|
|
2018-10-08 18:30:01 +00:00
|
|
|
void eventlogstuff_free(eventlog_stuff *es)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (es->events_initial) {
|
|
|
|
for (i = 0; i < LOGEVENT_INITIAL_MAX; i++)
|
|
|
|
sfree(es->events_initial[i]);
|
|
|
|
sfree(es->events_initial);
|
|
|
|
}
|
|
|
|
if (es->events_circular) {
|
|
|
|
for (i = 0; i < LOGEVENT_CIRCULAR_MAX; i++)
|
|
|
|
sfree(es->events_circular[i]);
|
|
|
|
sfree(es->events_circular);
|
|
|
|
}
|
2018-12-01 09:35:52 +00:00
|
|
|
strbuf_free(es->seldata);
|
2018-10-08 18:30:01 +00:00
|
|
|
|
|
|
|
sfree(es);
|
|
|
|
}
|
|
|
|
|
|
|
|
void logevent_dlg(eventlog_stuff *es, const char *string)
|
2003-04-09 18:46:45 +00:00
|
|
|
{
|
|
|
|
char timebuf[40];
|
2005-01-09 14:27:48 +00:00
|
|
|
struct tm tm;
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
char **location;
|
|
|
|
size_t i;
|
2003-04-09 18:46:45 +00:00
|
|
|
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
if (es->ninitial == 0) {
|
|
|
|
es->events_initial = sresize(es->events_initial, LOGEVENT_INITIAL_MAX, char *);
|
|
|
|
for (i = 0; i < LOGEVENT_INITIAL_MAX; i++)
|
|
|
|
es->events_initial[i] = NULL;
|
|
|
|
es->events_circular = sresize(es->events_circular, LOGEVENT_CIRCULAR_MAX, char *);
|
|
|
|
for (i = 0; i < LOGEVENT_CIRCULAR_MAX; i++)
|
|
|
|
es->events_circular[i] = NULL;
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
if (es->ninitial < LOGEVENT_INITIAL_MAX)
|
|
|
|
location = &es->events_initial[es->ninitial];
|
|
|
|
else
|
|
|
|
location = &es->events_circular[(es->circular_first + es->ncircular) % LOGEVENT_CIRCULAR_MAX];
|
|
|
|
|
2005-01-09 14:27:48 +00:00
|
|
|
tm=ltime();
|
|
|
|
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
|
2003-04-09 18:46:45 +00:00
|
|
|
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
sfree(*location);
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
*location = dupcat(timebuf, string);
|
2003-04-09 18:46:45 +00:00
|
|
|
if (es->window) {
|
2019-09-08 19:29:00 +00:00
|
|
|
dlg_listbox_add(es->listctrl, &es->dp, *location);
|
Don't grow logevent buf indefinitely
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
2013-07-22 23:09:04 +00:00
|
|
|
}
|
|
|
|
if (es->ninitial < LOGEVENT_INITIAL_MAX) {
|
|
|
|
es->ninitial++;
|
|
|
|
} else if (es->ncircular < LOGEVENT_CIRCULAR_MAX) {
|
|
|
|
es->ncircular++;
|
|
|
|
} else if (es->ncircular == LOGEVENT_CIRCULAR_MAX) {
|
|
|
|
es->circular_first = (es->circular_first + 1) % LOGEVENT_CIRCULAR_MAX;
|
|
|
|
sfree(es->events_circular[es->circular_first]);
|
|
|
|
es->events_circular[es->circular_first] = dupstr("..");
|
2003-04-09 18:46:45 +00:00
|
|
|
}
|
|
|
|
}
|
2003-04-11 18:44:05 +00:00
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
int gtkdlg_askappend(Seat *seat, Filename *filename,
|
Refactor the LogContext type.
LogContext is now the owner of the logevent() function that back ends
and so forth are constantly calling. Previously, logevent was owned by
the Frontend, which would store the message into its list for the GUI
Event Log dialog (or print it to standard error, or whatever) and then
pass it _back_ to LogContext to write to the currently open log file.
Now it's the other way round: LogContext gets the message from the
back end first, writes it to its log file if it feels so inclined, and
communicates it back to the front end.
This means that lots of parts of the back end system no longer need to
have a pointer to a full-on Frontend; the only thing they needed it
for was logging, so now they just have a LogContext (which many of
them had to have anyway, e.g. for logging SSH packets or session
traffic).
LogContext itself also doesn't get a full Frontend pointer any more:
it now talks back to the front end via a little vtable of its own
called LogPolicy, which contains the method that passes Event Log
entries through, the old askappend() function that decides whether to
truncate a pre-existing log file, and an emergency function for
printing an especially prominent message if the log file can't be
created. One minor nice effect of this is that console and GUI apps
can implement that last function subtly differently, so that Unix
console apps can write it with a plain \n instead of the \r\n
(harmless but inelegant) that the old centralised implementation
generated.
One other consequence of this is that the LogContext has to be
provided to backend_init() so that it's available to backends from the
instant of creation, rather than being provided via a separate API
call a couple of function calls later, because backends have typically
started doing things that need logging (like making network
connections) before the call to backend_provide_logctx. Fortunately,
there's no case in the whole code base where we don't already have
logctx by the time we make a backend (so I don't actually remember why
I ever delayed providing one). So that shortens the backend API by one
function, which is always nice.
While I'm tidying up, I've also moved the printf-style logeventf() and
the handy logevent_and_free() into logging.c, instead of having copies
of them scattered around other places. This has also let me remove
some stub functions from a couple of outlying applications like
Pageant. Finally, I've removed the pointless "_tag" at the end of
LogContext's official struct name.
2018-10-10 18:26:18 +00:00
|
|
|
void (*callback)(void *ctx, int result), void *ctx)
|
2003-04-11 18:44:05 +00:00
|
|
|
{
|
|
|
|
static const char msgtemplate[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"The session log file \"%.*s\" already exists. "
|
|
|
|
"You can overwrite it with a new session log, "
|
|
|
|
"append your session log to the end of it, "
|
|
|
|
"or disable session logging for this session.";
|
2017-11-26 14:05:02 +00:00
|
|
|
static const struct message_box_button button_array_append[] = {
|
|
|
|
{"Overwrite", 'o', 1, 2},
|
|
|
|
{"Append", 'a', 0, 1},
|
|
|
|
{"Disable", 'd', -1, 0},
|
|
|
|
};
|
|
|
|
static const struct message_box_buttons buttons_append = {
|
|
|
|
button_array_append, lenof(button_array_append),
|
|
|
|
};
|
|
|
|
|
2003-04-11 18:44:05 +00:00
|
|
|
char *message;
|
|
|
|
char *mbtitle;
|
2017-11-26 16:56:03 +00:00
|
|
|
struct simple_prompt_result_ctx *result_ctx;
|
|
|
|
GtkWidget *mainwin, *msgbox;
|
2003-04-11 18:44:05 +00:00
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
message = dupprintf(msgtemplate, FILENAME_MAX, filename->path);
|
2003-04-11 18:44:05 +00:00
|
|
|
mbtitle = dupprintf("%s Log to File", appname);
|
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx = snew(struct simple_prompt_result_ctx);
|
|
|
|
result_ctx->callback = callback;
|
|
|
|
result_ctx->callback_ctx = ctx;
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
result_ctx->seat = seat;
|
2017-11-26 16:56:03 +00:00
|
|
|
result_ctx->dialog_slot = DIALOG_SLOT_LOGFILE_PROMPT;
|
|
|
|
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
mainwin = GTK_WIDGET(gtk_seat_get_window(seat));
|
2017-11-26 16:56:03 +00:00
|
|
|
msgbox = create_message_box(
|
|
|
|
mainwin, mbtitle, message,
|
|
|
|
string_width("LINE OF TEXT SUITABLE FOR THE ASKAPPEND WIDTH"),
|
2018-10-29 19:50:29 +00:00
|
|
|
false, &buttons_append, simple_prompt_result_callback, result_ctx);
|
New abstraction 'Seat', to pass to backends.
This is a new vtable-based abstraction which is passed to a backend in
place of Frontend, and it implements only the subset of the Frontend
functions needed by a backend. (Many other Frontend functions still
exist, notably the wide range of things called by terminal.c providing
platform-independent operations on the GUI terminal window.)
The purpose of making it a vtable is that this opens up the
possibility of creating a backend as an internal implementation detail
of some other activity, by providing just that one backend with a
custom Seat that implements the methods differently.
For example, this refactoring should make it feasible to directly
implement an SSH proxy type, aka the 'jump host' feature supported by
OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP
mode, and then expose the main channel of that as the Socket for the
primary connection'. (Which of course you can already do by spawning
'plink -nc' as a separate proxy process, but this would permit it in
the _same_ process without anything getting confused.)
I've centralised a full set of stub methods in misc.c for the new
abstraction, which allows me to get rid of several annoying stubs in
the previous code. Also, while I'm here, I've moved a lot of
duplicated modalfatalbox() type functions from application main
program files into wincons.c / uxcons.c, which I think saves
duplication overall. (A minor visible effect is that the prefixes on
those console-based fatal error messages will now be more consistent
between applications.)
2018-10-11 18:58:42 +00:00
|
|
|
register_dialog(seat, result_ctx->dialog_slot, msgbox);
|
2003-04-11 18:44:05 +00:00
|
|
|
|
|
|
|
sfree(message);
|
|
|
|
sfree(mbtitle);
|
|
|
|
|
2017-11-26 16:56:03 +00:00
|
|
|
return -1; /* dialog still in progress */
|
2003-04-11 18:44:05 +00:00
|
|
|
}
|