# HG changeset patch
# Node ID ecfa89fa9d6b
# Parent  e99acd742190
diff --git a/pidgin/pidginabout.c b/pidgin/pidginabout.c
--- a/pidgin/pidginabout.c
+++ b/pidgin/pidginabout.c
@@ -235,6 +235,25 @@ pidgin_about_dialog_load_build_info(Pidg
 	                                           GTK_MICRO_VERSION);
 }
 
+static char *
+pidgin_about_dialog_copy_build_info(void) {
+	char *info = NULL;
+
+	info = g_strdup_printf(
+		"Build Information\n"
+		"=================\n"
+		"Commit Hash: %s\n"
+		"Purple Version: %u.%u.%u\n"
+		"GLib Version: %u.%u.%u\n"
+		"GTK Version: %u.%u.%u\n",
+		REVISION,
+		PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION,
+		GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION,
+		GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
+
+	return info;
+}
+
 static void
 pidgin_about_dialog_load_runtime_info(PidginAboutDialog *about) {
 	/* add the purple version */
@@ -259,6 +278,23 @@ pidgin_about_dialog_load_runtime_info(Pi
 	                                           gtk_get_micro_version());
 }
 
+static char *
+pidgin_about_dialog_copy_runtime_info(void) {
+	char *info = NULL;
+
+	info = g_strdup_printf(
+		"Runtime Information\n"
+		"===================\n"
+		"Purple Version: %u.%u.%u\n"
+		"GLib Version: %u.%u.%u\n"
+		"GTK Version: %u.%u.%u\n",
+		purple_major_version, purple_minor_version, purple_micro_version,
+		glib_major_version, glib_minor_version, glib_micro_version,
+		gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
+
+	return info;
+}
+
 static void
 pidgin_about_dialog_load_gtk_settings(PidginAboutDialog *about) {
 	gchar *cursor_theme_name = NULL, *theme_name = NULL;
@@ -328,6 +364,69 @@ pidgin_about_dialog_load_gtk_settings(Pi
 	g_free(theme_name);
 }
 
+static char *
+pidgin_about_dialog_copy_gtk_settings(void) {
+	char *cursor_theme_name = NULL, *theme_name = NULL;
+	char *icon_theme_name = NULL;
+	char *im_module = NULL;
+	char *sound_theme_name = NULL;
+	gboolean enable_animations = FALSE;
+	gboolean shell_shows_app_menu = FALSE, shell_shows_menubar = FALSE;
+	GString *info = NULL;
+
+	/* get the settings we're interested in */
+	g_object_get(
+		gtk_settings_get_default(),
+		"gtk-cursor-theme-name", &cursor_theme_name,
+		"gtk-enable-animations", &enable_animations,
+		"gtk-icon-theme-name", &icon_theme_name,
+		"gtk-im-module", &im_module,
+		"gtk-shell-shows-app-menu", &shell_shows_app_menu,
+		"gtk-shell-shows-menubar", &shell_shows_menubar,
+		"gtk-sound-theme-name", &sound_theme_name,
+		"gtk-theme-name", &theme_name,
+		NULL);
+
+	info = g_string_new(
+		"GTK Settings\n"
+		"============\n");
+
+	g_string_append_printf(
+		info, "gtk-cursor-theme-name: %s\n",
+		(cursor_theme_name != NULL) ? cursor_theme_name : _("(not set)"));
+
+	g_string_append_printf(info, "gtk-enable-animations: %s\n",
+	                       enable_animations ? _("yes") : _("no"));
+
+	g_string_append_printf(
+		info, "gtk-icon-theme-name: %s\n",
+		(icon_theme_name != NULL) ? icon_theme_name : _("(not set)"));
+
+	g_string_append_printf(info, "gtk-im-module: %s\n",
+	                       (im_module != NULL) ? im_module : _("(not set)"));
+
+	g_string_append_printf(info, "gtk-shell-shows-app-menu: %s\n",
+	                       shell_shows_app_menu ? _("yes") : _("no"));
+
+	g_string_append_printf(info, "gtk-shell-shows-menubar: %s\n",
+	                       shell_shows_menubar ? _("yes") : _("no"));
+
+	g_string_append_printf(
+		info, "gtk-sound-theme-name: %s\n",
+		(sound_theme_name != NULL) ? sound_theme_name : _("(not set)"));
+
+	g_string_append_printf(info, "gtk-theme-name: %s\n",
+	                       (theme_name != NULL) ? theme_name : _("(not set)"));
+
+	g_free(cursor_theme_name);
+	g_free(icon_theme_name);
+	g_free(im_module);
+	g_free(sound_theme_name);
+	g_free(theme_name);
+
+	return g_string_free(info, FALSE);
+}
+
 static void
 pidgin_about_dialog_load_plugin_search_paths(PidginAboutDialog *about) {
 	GList *paths = NULL;
@@ -343,6 +442,27 @@ pidgin_about_dialog_load_plugin_search_p
 	}
 }
 
+static char *
+pidgin_about_dialog_copy_plugin_search_paths(void) {
+	GList *paths = NULL;
+	GPluginManager *manager = gplugin_manager_get_default();
+	GString *info = NULL;
+
+	info = g_string_new(
+		"Plugin Search Paths\n"
+		"===================\n");
+
+	/* add the search paths */
+	paths = gplugin_manager_get_paths(manager);
+	while(paths != NULL) {
+		g_string_append_printf(info, "- %s\n", (char *)paths->data);
+
+		paths = paths->next;
+	}
+
+	return g_string_free(info, FALSE);
+}
+
 static void
 pidgin_about_dialog_load_conf_path_info(PidginAboutDialog *about) {
 	/* add the cache directory path */
@@ -358,6 +478,23 @@ pidgin_about_dialog_load_conf_path_info(
 	                                  purple_data_dir());
 }
 
+static char *
+pidgin_about_dialog_copy_conf_path_info(void) {
+	char *info = NULL;
+
+	info = g_strdup_printf(
+		"Runtime Directories\n"
+		"===================\n"
+		"Cache: %s\n"
+		"Configuration: %s\n"
+		"Data: %s\n",
+		purple_cache_dir(),
+		purple_config_dir(),
+		purple_data_dir());
+
+	return info;
+}
+
 static void
 pidgin_about_dialog_add_build_args(PidginAboutDialog *about,
                                    const char *build_args)
@@ -382,6 +519,42 @@ pidgin_about_dialog_add_build_args(Pidgi
 	g_strfreev(splits);
 }
 
+static char *
+pidgin_about_dialog_copy_build_args(const char *build_args) {
+	char **splits = NULL;
+	GString *info = NULL;
+
+	info = g_string_new(
+		"Meson Arguments\n"
+		"===============\n");
+
+	/* Walk through the arguments and add them */
+	splits = g_strsplit(build_args, " ", -1);
+	for(gint idx = 0; splits[idx]; idx++) {
+		char **value_split = g_strsplit(splits[idx], "=", 2);
+		char *value = NULL;
+
+		if(value_split[0] == NULL || value_split[0][0] == '\0') {
+			continue;
+		}
+
+		if(value_split[1] != NULL) {
+			value = purple_unescape_text(value_split[1]);
+		} else {
+			value = NULL;
+		}
+
+		g_string_append_printf(info, "%s: %s\n", value_split[0], value);
+
+		g_free(value);
+		g_strfreev(value_split);
+	}
+
+	g_strfreev(splits);
+
+	return g_string_free(info, FALSE);
+}
+
 static void
 pidgin_about_dialog_load_build_configuration(PidginAboutDialog *about) {
 	pidgin_about_dialog_load_build_info(about);
@@ -418,6 +591,73 @@ pidgin_about_dialog_open_url_cb(G_GNUC_U
 	gtk_show_uri(GTK_WINDOW(data), url, GDK_CURRENT_TIME);
 }
 
+static void
+pidgin_about_dialog_copy_button_cb(GtkButton *button,
+                                   gpointer data)
+{
+	PidginAboutDialog *about = NULL;
+	GdkClipboard *clipboard = NULL;
+	char *info = NULL;
+
+	about = PIDGIN_ABOUT_DIALOG(gtk_widget_get_root(GTK_WIDGET(button)));
+
+	if(data == about->build_info_group) {
+		info = pidgin_about_dialog_copy_build_info();
+	} else if(data == about->runtime_info_group) {
+		info = pidgin_about_dialog_copy_runtime_info();
+	} else if(data == about->gtk_settings_group) {
+		info = pidgin_about_dialog_copy_gtk_settings();
+	} else if(data == about->plugin_search_paths_group) {
+		info = pidgin_about_dialog_copy_plugin_search_paths();
+	} else if(data == about->conf_path_info_group) {
+		info = pidgin_about_dialog_copy_conf_path_info();
+#ifdef MESON_ARGS
+	} else if(data == about->build_args_group) {
+		info = pidgin_about_dialog_copy_build_args(MESON_ARGS);
+#endif
+	} else {
+		GString *everything = g_string_new(NULL);
+
+		info = pidgin_about_dialog_copy_build_info();
+		g_string_append(everything, info);
+		g_string_append_c(everything, '\n');
+		g_free(info);
+
+		info = pidgin_about_dialog_copy_runtime_info();
+		g_string_append(everything, info);
+		g_string_append_c(everything, '\n');
+		g_free(info);
+
+		info = pidgin_about_dialog_copy_conf_path_info();
+		g_string_append(everything, info);
+		g_string_append_c(everything, '\n');
+		g_free(info);
+
+		info = pidgin_about_dialog_copy_gtk_settings();
+		g_string_append(everything, info);
+		g_string_append_c(everything, '\n');
+		g_free(info);
+
+		info = pidgin_about_dialog_copy_plugin_search_paths();
+		g_string_append(everything, info);
+		g_free(info);
+
+#ifdef MESON_ARGS
+		g_string_append_c(everything, '\n');
+		info = pidgin_about_dialog_copy_build_args(MESON_ARGS);
+		g_string_append(everything, info);
+		g_free(info);
+#endif
+
+		info = g_string_free(everything, FALSE);
+	}
+
+	clipboard = gtk_widget_get_clipboard(GTK_WIDGET(about));
+	gdk_clipboard_set_text(clipboard, info);
+
+	g_free(info);
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -461,6 +701,8 @@ pidgin_about_dialog_class_init(PidginAbo
 	                                        pidgin_about_dialog_response_cb);
 	gtk_widget_class_bind_template_callback(widget_class,
 	                                        pidgin_about_dialog_open_url_cb);
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_about_dialog_copy_button_cb);
 }
 
 static void
diff --git a/pidgin/resources/About/about.ui b/pidgin/resources/About/about.ui
--- a/pidgin/resources/About/about.ui
+++ b/pidgin/resources/About/about.ui
@@ -122,34 +122,93 @@ Foundation, Inc., 51 Franklin Street, Fi
                     <property name="child">
                       <object class="AdwPreferencesPage">
                         <child>
+                          <object class="GtkButton" id="copy_all_button">
+                            <property name="css-classes">pill</property>
+                            <property name="halign">center</property>
+                            <property name="valign">end</property>
+                            <property name="child">
+                              <object class="AdwButtonContent">
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <property name="label" translatable="1">_Copy All</property>
+                                <property name="use-underline">1</property>
+                              </object>
+                            </property>
+                            <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" swapped="no"/>
+                          </object>
+                        </child>
+                        <!-- AdwPreferencesPage doesn't track non-AdwPreferencesGroup children, so manually remove when it's destroyed. -->
+                        <signal name="destroy" handler="gtk_widget_unparent" object="copy_all_button" swapped="yes"/>
+                        <child>
                           <object class="AdwPreferencesGroup" id="build_info_group">
                             <property name="title" translatable="1">Build Information</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="build_info_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
                           <object class="AdwPreferencesGroup" id="runtime_info_group">
                             <property name="title" translatable="1">Runtime Information</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="runtime_info_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
                           <object class="AdwPreferencesGroup" id="conf_path_info_group">
                             <property name="title" translatable="1">Runtime Directories</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="conf_path_info_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
                           <object class="AdwPreferencesGroup" id="gtk_settings_group">
                             <property name="title" translatable="1">GTK Settings</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="gtk_settings_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
                           <object class="AdwPreferencesGroup" id="plugin_search_paths_group">
                             <property name="title" translatable="1">Plugin Search Paths</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="plugin_search_paths_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
                           <object class="AdwPreferencesGroup" id="build_args_group">
                             <property name="title" translatable="1">Meson Arguments</property>
                             <property name="visible">0</property>
+                            <child type="header-suffix">
+                              <object class="GtkButton">
+                                <property name="css-classes">flat</property>
+                                <property name="icon-name">edit-copy-symbolic</property>
+                                <signal name="clicked" handler="pidgin_about_dialog_copy_button_cb" object="build_args_group" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                       </object>
