# HG changeset patch
# Node ID a236a14a0ff6
# Parent  1a53026013e4
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
--- a/pidgin/gtkmedia.c
+++ b/pidgin/gtkmedia.c
@@ -32,6 +32,7 @@
 #include "gtkmedia.h"
 #include "gtkutils.h"
 #include "pidgincore.h"
+#include "pidginkeypad.h"
 
 #ifdef USE_VV
 
@@ -653,96 +654,14 @@ pidgin_media_add_audio_widget(PidginMedi
 }
 
 static void
-phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
+pidgin_media_keypad_pressed_cb(PidginKeypad *keypad, guint key, gpointer data)
 {
-	PidginMedia *gtkmedia = user_data;
-	gint num;
+	PidginMedia *gtkmedia = data;
 	gchar *sid;
 
-	num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
-	sid = g_object_get_data(G_OBJECT(button), "session-id");
-
-	purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
-}
-
-static inline GtkWidget *
-phone_create_button(const gchar *text_hi, const gchar *text_lo)
-{
-	GtkWidget *button;
-	GtkWidget *label_hi;
-	GtkWidget *label_lo;
-	GtkWidget *grid;
-	const gchar *text_hi_local;
-
-	if (text_hi)
-		text_hi_local = _(text_hi);
-	else
-		text_hi_local = "";
-
-	grid = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
-	gtk_box_set_homogeneous(GTK_BOX(grid), TRUE);
-
-	button = gtk_button_new();
-	label_hi = gtk_label_new(text_hi_local);
-	gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
-	label_lo = gtk_label_new(text_lo);
-	gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
-	gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
-	gtk_container_add(GTK_CONTAINER(button), grid);
-
-	return button;
-}
+	sid = g_object_get_data(G_OBJECT(gtkmedia), "session-id");
 
-static struct phone_label {
-	gchar *subtext;
-	gchar *text;
-	gchar chr;
-} phone_labels[] = {
-	{"<b>1</b>", NULL, '1'},
-	/* Translators note: These are the letters on the keys of a numeric
-	   keypad; translate according to the tables in §7 of ETSI ES 202 130:
-           http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
-         */
-	 /* Letters on the '2' key of a numeric keypad */
-	{"<b>2</b>", N_("ABC"), '2'},
-	 /* Letters on the '3' key of a numeric keypad */
-	{"<b>3</b>", N_("DEF"), '3'},
-	 /* Letters on the '4' key of a numeric keypad */
-	{"<b>4</b>", N_("GHI"), '4'},
-	 /* Letters on the '5' key of a numeric keypad */
-	{"<b>5</b>", N_("JKL"), '5'},
-	 /* Letters on the '6' key of a numeric keypad */
-	{"<b>6</b>", N_("MNO"), '6'},
-	 /* Letters on the '7' key of a numeric keypad */
-	{"<b>7</b>", N_("PQRS"), '7'},
-	 /* Letters on the '8' key of a numeric keypad */
-	{"<b>8</b>", N_("TUV"), '8'},
-	 /* Letters on the '9' key of a numeric keypad */
-	{"<b>9</b>", N_("WXYZ"), '9'},
-	{"<b>*</b>", NULL, '*'},
-	{"<b>0</b>", NULL, '0'},
-	{"<b>#</b>", NULL, '#'},
-	{NULL, NULL, 0}
-};
-
-static gboolean
-pidgin_media_dtmf_key_press_event_cb(G_GNUC_UNUSED GtkEventControllerKey *controller,
-                                     guint keyval,
-                                     G_GNUC_UNUSED guint keycode,
-                                     G_GNUC_UNUSED GdkModifierType state,
-                                     gpointer user_data)
-{
-	PidginMedia *gtkmedia = user_data;
-
-	if ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) ||
-		keyval == GDK_KEY_asterisk ||
-		keyval == GDK_KEY_numbersign) {
-		gchar *sid = g_object_get_data(G_OBJECT(gtkmedia), "session-id");
-
-		purple_media_send_dtmf(gtkmedia->priv->media, sid, keyval, 25, 50);
-	}
-
-	return FALSE;
+	purple_media_send_dtmf(gtkmedia->priv->media, sid, key, 25, 50);
 }
 
 static GtkWidget *
@@ -750,42 +669,20 @@ pidgin_media_add_dtmf_widget(PidginMedia
 		PurpleMediaSessionType type, const gchar *_sid)
 {
 	GtkApplicationWindow *win = GTK_APPLICATION_WINDOW(gtkmedia);
-	GtkEventController *event_controller = NULL;
-	GtkWidget *grid = gtk_grid_new();
-	GtkWidget *button;
-	gint index = 0;
-
-	gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
-	gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
+	GtkWidget *keypad = NULL;
 
-	/* Add buttons */
-	for (index = 0; phone_labels[index].subtext != NULL; index++) {
-		button = phone_create_button(phone_labels[index].text,
-				phone_labels[index].subtext);
-		g_signal_connect(button, "pressed",
-				G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia);
-		g_object_set_data(G_OBJECT(button), "dtmf-digit",
-				GINT_TO_POINTER(phone_labels[index].chr));
-		g_object_set_data_full(G_OBJECT(button), "session-id",
-				g_strdup(_sid), g_free);
-		gtk_grid_attach(GTK_GRID(grid), button,
-				index % 3, index / 3, 1, 1);
-		g_object_set(button, "expand", TRUE, "margin", 2, NULL);
-	}
-
-	event_controller = gtk_event_controller_key_new(GTK_WIDGET(win));
-	g_object_set_data_full(G_OBJECT(win), "pidgin-event-controller",
-	                       event_controller, g_object_unref);
-
-	g_signal_connect(event_controller, "key-pressed",
-		G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia);
+	keypad = pidgin_keypad_new();
+	pidgin_keypad_set_key_capture_widget(PIDGIN_KEYPAD(keypad),
+	                                     GTK_WIDGET(win));
+	g_signal_connect(keypad, "pressed",
+	                 G_CALLBACK(pidgin_media_keypad_pressed_cb), gtkmedia);
 
 	g_object_set_data_full(G_OBJECT(win), "session-id",
 		g_strdup(_sid), g_free);
 
-	gtk_widget_show_all(grid);
+	gtk_widget_show_all(keypad);
 
-	return grid;
+	return keypad;
 }
 
 static void
diff --git a/pidgin/meson.build b/pidgin/meson.build
--- a/pidgin/meson.build
+++ b/pidgin/meson.build
@@ -39,6 +39,7 @@ libpidgin_SOURCES = [
 	'pidginiconname.c',
 	'pidgininfopane.c',
 	'pidgininvitedialog.c',
+	'pidginkeypad.c',
 	'pidginmessage.c',
 	'pidginmooddialog.c',
 	'pidginnotificationconnectionerror.c',
@@ -112,6 +113,7 @@ libpidgin_headers = [
 	'pidginiconname.h',
 	'pidgininfopane.h',
 	'pidgininvitedialog.h',
+	'pidginkeypad.h',
 	'pidginmessage.h',
 	'pidginmooddialog.h',
 	'pidginnotificationconnectionerror.h',
diff --git a/pidgin/pidginkeypad.h b/pidgin/pidginkeypad.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginkeypad.h
@@ -0,0 +1,75 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_KEYPAD_H
+#define PIDGIN_KEYPAD_H
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+/**
+ * PidginKeypad:
+ *
+ * #PidginKeypad is a widget that displays a DTMF keypad, with the digits 0-9,
+ * an asterisk, and a number sign.
+ *
+ * Since: 3.0.0
+ */
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_KEYPAD pidgin_keypad_get_type()
+G_DECLARE_FINAL_TYPE(PidginKeypad, pidgin_keypad, PIDGIN, KEYPAD, GtkGrid)
+
+/**
+ * pidgin_keypad_new:
+ *
+ * Creates a new #PidginKeypad.
+ *
+ * Returns: (transfer full): The new instance.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_keypad_new(void);
+
+/**
+ * pidgin_keypad_set_key_capture_widget:
+ * @keypad: The keypad.
+ * @widget: A widget to capture keys from.
+ *
+ * Sets @widget as the widget that @keypad will capture key events from.
+ *
+ * If key events are handled by the keypad, the DTMF digits will be captured
+ * and trigger the pressed signal on @keypad.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_keypad_set_key_capture_widget(PidginKeypad *keypad, GtkWidget *widget);
+
+G_END_DECLS
+
+#endif /* PIDGIN_KEYPAD_H */
diff --git a/pidgin/pidginkeypad.c b/pidgin/pidginkeypad.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginkeypad.c
@@ -0,0 +1,152 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "pidginkeypad.h"
+
+struct _PidginKeypad {
+	GtkGrid parent;
+};
+
+enum {
+	SIG_PRESSED,
+	N_SIGNALS
+};
+static guint signals[N_SIGNALS] = {0, };
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_keypad_action_activate(G_GNUC_UNUSED GSimpleAction *action,
+                              GVariant *parameter,
+                              gpointer data)
+{
+	PidginKeypad *keypad = PIDGIN_KEYPAD(data);
+	gint32 num = 0;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_INT32)) {
+		g_critical("PidginKeypad dtmf action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+		return;
+	}
+
+	num = g_variant_get_int32(parameter);
+
+	g_signal_emit(keypad, signals[SIG_PRESSED], 0, num);
+}
+
+static gboolean
+pidgin_keypad_key_pressed_cb(G_GNUC_UNUSED GtkEventControllerKey *controller,
+                             guint keyval,
+                             G_GNUC_UNUSED guint keycode,
+                             G_GNUC_UNUSED GdkModifierType state,
+                             gpointer data)
+{
+	PidginKeypad *keypad = PIDGIN_KEYPAD(data);
+
+	if(GDK_KEY_KP_0 <= keyval && keyval <= GDK_KEY_KP_9) {
+		/* Normalize to the same ASCII numbers. */
+		keyval = (keyval - GDK_KEY_KP_0) + GDK_KEY_0;
+	}
+
+	if((GDK_KEY_0 <= keyval && keyval <= GDK_KEY_9) ||
+		keyval == GDK_KEY_asterisk ||
+		keyval == GDK_KEY_numbersign) {
+
+		g_signal_emit(keypad, signals[SIG_PRESSED], 0, keyval);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginKeypad, pidgin_keypad, GTK_TYPE_GRID)
+
+static void
+pidgin_keypad_init(PidginKeypad *keypad) {
+	GtkWidget *widget = GTK_WIDGET(keypad);
+	GSimpleActionGroup *action_group = NULL;
+	GActionEntry actions[] = {
+		{
+			.name = "dtmf",
+			.activate = pidgin_keypad_action_activate,
+			.parameter_type = "i",
+		},
+	};
+
+	gtk_widget_init_template(widget);
+
+	action_group = g_simple_action_group_new();
+	g_action_map_add_action_entries(G_ACTION_MAP(action_group),
+	                                actions, G_N_ELEMENTS(actions), keypad);
+	gtk_widget_insert_action_group(widget, "keypad",
+	                               G_ACTION_GROUP(action_group));
+}
+
+static void
+pidgin_keypad_class_init(PidginKeypadClass *klass) {
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	/**
+	 * PidginKeypad::pressed:
+	 * @keypad: The #PidginKeypad.
+	 * @key: The key that was pressed, [const@Gdk.KEY_0] - [const@Gdk.KEY_9],
+	 *       [const@Gdk.KEY_asterisk], or [const@Gdk.KEY_numbersign].
+	 *
+	 * Emitted when a key is pressed (by keyboard or from clicking a button).
+	 */
+	signals[SIG_PRESSED] = g_signal_new("pressed",
+	                                    G_TYPE_FROM_CLASS(klass),
+	                                    0, 0, NULL, NULL, NULL, G_TYPE_NONE,
+	                                    1, G_TYPE_UINT);
+
+	gtk_widget_class_set_template_from_resource(widget_class,
+	                                            "/im/pidgin/Pidgin3/Keypad/keypad.ui");
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_keypad_new(void) {
+	return g_object_new(PIDGIN_TYPE_KEYPAD, NULL);
+}
+
+void
+pidgin_keypad_set_key_capture_widget(PidginKeypad *keypad, GtkWidget *widget) {
+	GtkEventController *controller = NULL;
+
+	g_return_if_fail(PIDGIN_IS_KEYPAD(keypad));
+	g_return_if_fail(GTK_IS_WIDGET(widget));
+
+	controller = gtk_event_controller_key_new(widget);
+	gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_CAPTURE);
+	g_signal_connect(controller, "key-pressed",
+	                 G_CALLBACK(pidgin_keypad_key_pressed_cb), keypad);
+	g_object_set_data_full(G_OBJECT(widget), "pidgin-keypad-key-controller",
+	                       controller, g_object_unref);
+}
diff --git a/pidgin/resources/Keypad/keypad.ui b/pidgin/resources/Keypad/keypad.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/Keypad/keypad.ui
@@ -0,0 +1,759 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 
+
+Pidgin - Internet Messenger
+Copyright (C) Pidgin Developers <devel@pidgin.im>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+-->
+<interface>
+  <requires lib="gtk+" version="3.22"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <!-- n-columns=3 n-rows=4 -->
+  <template class="PidginKeypad" parent="GtkGrid">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="row-homogeneous">True</property>
+    <property name="column-homogeneous">True</property>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">49</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">1</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[1]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <style>
+          <class name="image-button"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">50</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">2</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[2]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '2' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">ABC</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">1</property>
+        <property name="top-attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">51</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">3</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[3]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '3' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">DEF</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">2</property>
+        <property name="top-attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">52</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">4</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[4]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '4' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">GHI</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">53</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">5</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[5]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '5' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">JKL</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">1</property>
+        <property name="top-attach">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">54</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">6</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[6]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '6' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">MNO</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">2</property>
+        <property name="top-attach">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">55</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">7</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[7]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '7' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">PQRS</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">56</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">8</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[8]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '8' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">TUV</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">1</property>
+        <property name="top-attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">57</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">9</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[9]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <!-- Translators: These are the letters on the '9' key of a numeric
+                  keypad; translate according to the tables in §7 of ETSI ES 202 130:
+                  http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
+                -->
+                <property name="label" translatable="yes">WXYZ</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">2</property>
+        <property name="top-attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">42</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">*</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="asterisk">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">48</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">0</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="secondary_labels[0]">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">1</property>
+        <property name="top-attach">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="margin-left">2</property>
+        <property name="margin-right">2</property>
+        <property name="margin-start">2</property>
+        <property name="margin-end">2</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="action-name">keypad.dtmf</property>
+        <property name="action-target">35</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">#</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="octothorpe">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">2</property>
+        <property name="top-attach">3</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/pidgin/resources/pidgin.gresource.xml b/pidgin/resources/pidgin.gresource.xml
--- a/pidgin/resources/pidgin.gresource.xml
+++ b/pidgin/resources/pidgin.gresource.xml
@@ -18,6 +18,7 @@
     <file compressed="true">Debug/debug.ui</file>
     <file compressed="true">Debug/filter.css</file>
     <file compressed="true">Debug/plugininfo.ui</file>
+    <file compressed="true">Keypad/keypad.ui</file>
     <file compressed="true">Log/log-viewer.ui</file>
     <file compressed="true">Notifications/connectionerror.ui</file>
     <file compressed="true">Notifications/list.ui</file>
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -356,6 +356,7 @@ pidgin/pidgindialog.c
 pidgin/pidginiconname.c
 pidgin/pidgininfopane.c
 pidgin/pidgininvitedialog.c
+pidgin/pidginkeypad.c
 pidgin/pidginmessage.c
 pidgin/pidginmooddialog.c
 pidgin/pidginplugininfo.c
@@ -405,6 +406,7 @@ pidgin/resources/Conversations/invite_di
 pidgin/resources/Conversations/window.ui
 pidgin/resources/Debug/debug.ui
 pidgin/resources/Debug/plugininfo.ui
+pidgin/resources/Keypad/keypad.ui
 pidgin/resources/Log/log-viewer.ui
 pidgin/resources/Plugins/dialog.ui
 pidgin/resources/Plugins/menu.ui
