pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Make PurpleNotificationManager implement GListModel
18 months ago, Gary Kramlich
c4a96b5eecba
Make PurpleNotificationManager implement GListModel
Also a few other clean ups, including making the user of the manager responsible
for sorting the list.
Testing Done:
Ran the unit tests and verified the notification list in pidgin was updating correctly.
Reviewed at https://reviews.imfreedom.org/r/2085/
/*
* 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/>.
*/
#ifdef HAVE_CONFIG_H
#
include
<config.h>
#endif
#ifdef HAVE_MESON_CONFIG
#include
"meson-config.h"
#endif
#include
<glib/gi18n-lib.h>
#include
<gdk-pixbuf/gdk-pixbuf.h>
#include
<json-glib/json-glib.h>
#include
<talkatu.h>
#include
"pidginabout.h"
#include
"package_revision.h"
#include
"gtkutils.h"
#include
"pidgincore.h"
#include
"pidginresources.h"
struct
_PidginAboutDialog
{
GtkDialog
parent
;
GtkWidget
*
close_button
;
GtkWidget
*
application_name
;
GtkWidget
*
stack
;
GtkWidget
*
main_scrolled_window
;
GtkTextBuffer
*
main_buffer
;
GtkWidget
*
developers_page
;
GtkWidget
*
developers_treeview
;
GtkTreeStore
*
developers_store
;
GtkWidget
*
translators_page
;
GtkWidget
*
translators_treeview
;
GtkTreeStore
*
translators_store
;
GtkWidget
*
build_info_page
;
GtkWidget
*
build_info_treeview
;
GtkTreeStore
*
build_info_store
;
};
/******************************************************************************
* Helpers
*****************************************************************************/
static
void
pidgin_about_dialog_load_application_name
(
PidginAboutDialog
*
about
)
{
gchar
*
label
=
g_strdup_printf
(
"%s %s"
,
PIDGIN_NAME
,
VERSION
);
gtk_label_set_text
(
GTK_LABEL
(
about
->
application_name
),
label
);
g_free
(
label
);
}
static
void
pidgin_about_dialog_load_main_page
(
PidginAboutDialog
*
about
)
{
GtkTextIter
start
;
GInputStream
*
istream
=
NULL
;
GString
*
str
=
NULL
;
TalkatuMarkdownBuffer
*
md_buffer
=
NULL
;
gchar
buffer
[
8192
];
gssize
read
=
0
,
size
=
0
;
/* now load the html */
istream
=
g_resource_open_stream
(
pidgin_get_resource
(),
"/im/pidgin/Pidgin3/About/about.md"
,
G_RESOURCE_LOOKUP_FLAGS_NONE
,
NULL
);
str
=
g_string_new
(
""
);
while
((
read
=
g_input_stream_read
(
istream
,
buffer
,
sizeof
(
buffer
),
NULL
,
NULL
))
>
0
)
{
g_string_append_len
(
str
,
(
gchar
*
)
buffer
,
read
);
size
+=
read
;
}
gtk_text_buffer_get_start_iter
(
about
->
main_buffer
,
&
start
);
md_buffer
=
TALKATU_MARKDOWN_BUFFER
(
about
->
main_buffer
);
talkatu_markdown_buffer_insert_markdown
(
md_buffer
,
&
start
,
str
->
str
,
size
);
g_string_free
(
str
,
TRUE
);
g_input_stream_close
(
istream
,
NULL
,
NULL
);
}
static
void
pidgin_about_dialog_load_json
(
GtkTreeStore
*
store
,
const
gchar
*
json_section
)
{
GInputStream
*
istream
=
NULL
;
GList
*
l
=
NULL
,
*
sections
=
NULL
;
GError
*
error
=
NULL
;
JsonParser
*
parser
=
NULL
;
JsonNode
*
root_node
=
NULL
;
JsonObject
*
root_object
=
NULL
;
JsonArray
*
sections_array
=
NULL
;
/* get a stream to the credits resource */
istream
=
g_resource_open_stream
(
pidgin_get_resource
(),
"/im/pidgin/Pidgin3/About/credits.json"
,
G_RESOURCE_LOOKUP_FLAGS_NONE
,
NULL
);
/* create our parser */
parser
=
json_parser_new
();
if
(
!
json_parser_load_from_stream
(
parser
,
istream
,
NULL
,
&
error
))
{
g_critical
(
"%s"
,
error
->
message
);
}
root_node
=
json_parser_get_root
(
parser
);
root_object
=
json_node_get_object
(
root_node
);
sections_array
=
json_object_get_array_member
(
root_object
,
json_section
);
sections
=
json_array_get_elements
(
sections_array
);
for
(
l
=
sections
;
l
;
l
=
l
->
next
)
{
GtkTreeIter
section_iter
;
JsonObject
*
section
=
json_node_get_object
(
l
->
data
);
JsonArray
*
people
=
NULL
;
gchar
*
markup
=
NULL
;
const
gchar
*
title
=
NULL
;
guint
idx
=
0
,
n_people
=
0
;
title
=
json_object_get_string_member
(
section
,
"title"
);
markup
=
g_strdup_printf
(
"<b><big>%s</big></b>"
,
title
);
gtk_tree_store_append
(
store
,
&
section_iter
,
NULL
);
gtk_tree_store_set
(
store
,
&
section_iter
,
0
,
markup
,
1
,
0.5f
,
-1
);
g_free
(
markup
);
people
=
json_object_get_array_member
(
section
,
"people"
);
n_people
=
json_array_get_length
(
people
);
for
(
idx
=
0
;
idx
<
n_people
;
idx
++
)
{
GtkTreeIter
person_iter
;
gtk_tree_store_append
(
store
,
&
person_iter
,
&
section_iter
);
gtk_tree_store_set
(
store
,
&
person_iter
,
0
,
json_array_get_string_element
(
people
,
idx
),
1
,
0.5f
,
-1
);
}
}
g_list_free
(
sections
);
/* clean up */
g_object_unref
(
G_OBJECT
(
parser
));
g_input_stream_close
(
istream
,
NULL
,
NULL
);
}
static
void
pidgin_about_dialog_load_developers
(
PidginAboutDialog
*
about
)
{
pidgin_about_dialog_load_json
(
about
->
developers_store
,
"developers"
);
}
static
void
pidgin_about_dialog_load_translators
(
PidginAboutDialog
*
about
)
{
pidgin_about_dialog_load_json
(
about
->
translators_store
,
"languages"
);
}
static
void
pidgin_about_dialog_add_build_args
(
PidginAboutDialog
*
about
,
const
gchar
*
title
,
const
gchar
*
build_args
)
{
GtkTreeIter
section
,
value
;
gchar
**
splits
=
NULL
;
gchar
*
markup
=
NULL
;
gint
idx
=
0
;
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
title
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* now walk through the arguments and add them */
splits
=
g_strsplit
(
build_args
,
" "
,
-1
);
for
(
idx
=
0
;
splits
[
idx
];
idx
++
)
{
gchar
**
value_split
=
g_strsplit
(
splits
[
idx
],
"="
,
2
);
if
(
value_split
[
0
]
==
NULL
||
value_split
[
0
][
0
]
==
'\0'
)
{
continue
;
}
gtk_tree_store_append
(
about
->
build_info_store
,
&
value
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
value
,
0
,
value_split
[
0
],
1
,
value_split
[
1
]
?
value_split
[
1
]
:
""
,
-1
);
g_strfreev
(
value_split
);
}
g_strfreev
(
splits
);
}
static
void
pidgin_about_dialog_build_info_add_version
(
GtkTreeStore
*
store
,
GtkTreeIter
*
section
,
const
gchar
*
title
,
guint
major
,
guint
minor
,
guint
micro
)
{
GtkTreeIter
item
;
gchar
*
version
=
g_strdup_printf
(
"%u.%u.%u"
,
major
,
minor
,
micro
);
gtk_tree_store_append
(
store
,
&
item
,
section
);
gtk_tree_store_set
(
store
,
&
item
,
0
,
title
,
1
,
version
,
-1
);
g_free
(
version
);
}
static
void
pidgin_about_dialog_load_build_info
(
PidginAboutDialog
*
about
)
{
GtkTreeIter
section
,
item
;
gchar
*
markup
=
NULL
;
/* create the section */
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
_
(
"Build Information"
));
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* add the commit hash */
gtk_tree_store_append
(
about
->
build_info_store
,
&
item
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
item
,
0
,
"Commit Hash"
,
1
,
REVISION
,
-1
);
/* add the purple version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"Purple Version"
),
PURPLE_MAJOR_VERSION
,
PURPLE_MINOR_VERSION
,
PURPLE_MICRO_VERSION
);
/* add the glib version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"GLib Version"
),
GLIB_MAJOR_VERSION
,
GLIB_MINOR_VERSION
,
GLIB_MICRO_VERSION
);
/* add the gtk version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"GTK Version"
),
GTK_MAJOR_VERSION
,
GTK_MINOR_VERSION
,
GTK_MICRO_VERSION
);
}
static
void
pidgin_about_dialog_load_runtime_info
(
PidginAboutDialog
*
about
)
{
GtkTreeIter
section
;
gchar
*
markup
=
NULL
;
/* create the section */
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
_
(
"Runtime Information"
));
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* add the purple version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"Purple Version"
),
purple_major_version
,
purple_minor_version
,
purple_micro_version
);
/* add the glib version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"GLib Version"
),
glib_major_version
,
glib_minor_version
,
glib_micro_version
);
/* add the gtk version */
pidgin_about_dialog_build_info_add_version
(
about
->
build_info_store
,
&
section
,
_
(
"GTK Version"
),
gtk_get_major_version
(),
gtk_get_minor_version
(),
gtk_get_micro_version
());
}
static
void
pidgin_about_dialog_load_gtk_settings
(
PidginAboutDialog
*
about
)
{
GtkTreeIter
section
,
iter
;
gchar
*
markup
=
NULL
;
gchar
*
cursor_theme_name
=
NULL
,
*
theme_name
=
NULL
;
gchar
*
icon_theme_name
=
NULL
;
gchar
*
im_module
=
NULL
;
gchar
*
sound_theme_name
=
NULL
;
gboolean
enable_animations
=
FALSE
;
gboolean
shell_shows_app_menu
=
FALSE
,
shell_shows_menubar
=
FALSE
;
/* create the section */
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
_
(
"GTK Settings"
));
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* 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
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-cursor-theme-name"
,
1
,
(
cursor_theme_name
!=
NULL
)
?
cursor_theme_name
:
_
(
"(not set)"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-enable-animations"
,
1
,
enable_animations
?
_
(
"yes"
)
:
_
(
"no"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-icon-theme-name"
,
1
,
(
icon_theme_name
!=
NULL
)
?
icon_theme_name
:
_
(
"(not set)"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-im-module"
,
1
,
(
im_module
!=
NULL
)
?
im_module
:
_
(
"(not set)"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-shell-shows-app-menu"
,
1
,
shell_shows_app_menu
?
_
(
"yes"
)
:
_
(
"no"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-shell-shows-menubar"
,
1
,
shell_shows_menubar
?
_
(
"yes"
)
:
_
(
"no"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-sound-theme-name"
,
1
,
(
sound_theme_name
!=
NULL
)
?
sound_theme_name
:
_
(
"(not set)"
),
-1
);
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
"gtk-theme-name"
,
1
,
(
theme_name
!=
NULL
)
?
theme_name
:
_
(
"(not set)"
),
-1
);
g_free
(
cursor_theme_name
);
g_free
(
icon_theme_name
);
g_free
(
im_module
);
g_free
(
sound_theme_name
);
g_free
(
theme_name
);
}
static
void
pidgin_about_dialog_load_plugin_search_paths
(
PidginAboutDialog
*
about
)
{
GtkTreeIter
section
;
GList
*
paths
=
NULL
;
gchar
*
markup
=
NULL
;
GPluginManager
*
manager
=
gplugin_manager_get_default
();
/* create the section */
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
_
(
"Plugin Search Paths"
));
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* add the search paths */
paths
=
gplugin_manager_get_paths
(
manager
);
while
(
paths
!=
NULL
)
{
GtkTreeIter
iter
;
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
1
,
(
gchar
*
)(
paths
->
data
),
-1
);
paths
=
paths
->
next
;
}
}
static
void
pidgin_about_dialog_load_conf_path_info
(
PidginAboutDialog
*
about
)
{
GtkTreeIter
section
,
iter
;
gchar
*
markup
=
NULL
;
const
gchar
*
path
=
NULL
;
/* create the section */
markup
=
g_strdup_printf
(
"<b>%s</b>"
,
_
(
"Runtime Directories"
));
gtk_tree_store_append
(
about
->
build_info_store
,
&
section
,
NULL
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
section
,
0
,
markup
,
-1
);
g_free
(
markup
);
/* add the cache directory path */
path
=
purple_cache_dir
();
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
_
(
"Cache"
),
1
,
(
gchar
*
)
path
,
-1
);
/* add the config directory path */
path
=
purple_config_dir
();
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
_
(
"Configuration"
),
1
,
(
gchar
*
)
path
,
-1
);
/* add the data directory path */
path
=
purple_data_dir
();
gtk_tree_store_append
(
about
->
build_info_store
,
&
iter
,
&
section
);
gtk_tree_store_set
(
about
->
build_info_store
,
&
iter
,
0
,
_
(
"Data"
),
1
,
(
gchar
*
)
path
,
-1
);
}
static
void
pidgin_about_dialog_load_build_configuration
(
PidginAboutDialog
*
about
)
{
#ifdef MESON_ARGS
pidgin_about_dialog_add_build_args
(
about
,
_
(
"Meson Arguments"
),
MESON_ARGS
);
#endif
/* MESON_ARGS */
pidgin_about_dialog_load_build_info
(
about
);
pidgin_about_dialog_load_runtime_info
(
about
);
pidgin_about_dialog_load_gtk_settings
(
about
);
pidgin_about_dialog_load_plugin_search_paths
(
about
);
pidgin_about_dialog_load_conf_path_info
(
about
);
}
/******************************************************************************
* Callbacks
*****************************************************************************/
static
void
pidgin_about_dialog_response_cb
(
GtkDialog
*
dialog
,
gint
response_id
,
G_GNUC_UNUSED
gpointer
data
)
{
switch
(
response_id
)
{
case
GTK_RESPONSE_CLOSE
:
case
GTK_RESPONSE_DELETE_EVENT
:
gtk_window_destroy
(
GTK_WINDOW
(
dialog
));
break
;
}
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
G_DEFINE_TYPE
(
PidginAboutDialog
,
pidgin_about_dialog
,
GTK_TYPE_DIALOG
);
static
void
pidgin_about_dialog_class_init
(
PidginAboutDialogClass
*
klass
)
{
GtkWidgetClass
*
widget_class
=
GTK_WIDGET_CLASS
(
klass
);
gtk_widget_class_set_template_from_resource
(
widget_class
,
"/im/pidgin/Pidgin3/About/about.ui"
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
close_button
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
application_name
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
stack
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
main_scrolled_window
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
main_buffer
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
developers_page
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
developers_store
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
developers_treeview
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
translators_page
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
translators_store
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
translators_treeview
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
build_info_page
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
build_info_store
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAboutDialog
,
build_info_treeview
);
gtk_widget_class_bind_template_callback
(
widget_class
,
pidgin_about_dialog_response_cb
);
}
static
void
pidgin_about_dialog_init
(
PidginAboutDialog
*
about
)
{
gtk_widget_init_template
(
GTK_WIDGET
(
about
));
/* setup the application name label */
pidgin_about_dialog_load_application_name
(
about
);
/* setup the main page */
pidgin_about_dialog_load_main_page
(
about
);
/* setup the developers stuff */
pidgin_about_dialog_load_developers
(
about
);
gtk_tree_view_expand_all
(
GTK_TREE_VIEW
(
about
->
developers_treeview
));
/* setup the translators stuff */
pidgin_about_dialog_load_translators
(
about
);
gtk_tree_view_expand_all
(
GTK_TREE_VIEW
(
about
->
translators_treeview
));
/* setup the build info page */
pidgin_about_dialog_load_build_configuration
(
about
);
gtk_tree_view_expand_all
(
GTK_TREE_VIEW
(
about
->
build_info_treeview
));
}
/******************************************************************************
* Public API
*****************************************************************************/
GtkWidget
*
pidgin_about_dialog_new
(
void
)
{
return
g_object_new
(
PIDGIN_TYPE_ABOUT_DIALOG
,
"title"
,
"About Pidgin"
,
NULL
);
}