pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Make libpurple depend on its schemas
11 months ago, Gary Kramlich
d039c29040d3
Make libpurple depend on its schemas
In certain situations you could build libpurple but not the schemas which would
cause runtime failures.
Testing Done:
Built
Reviewed at https://reviews.imfreedom.org/r/2452/
/* pidgin
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include
<glib/gi18n-lib.h>
#include
<talkatu.h>
#include
<purple.h>
#include
"gtkrequest.h"
#include
"gtkblist.h"
#include
"gtkutils.h"
#include
"pidginaccountchooser.h"
#include
"pidginaccountdisplay.h"
#include
"pidginaccountfilterconnected.h"
#include
"pidgincore.h"
#include
<gdk/gdkkeysyms.h>
typedef
struct
{
PurpleRequestType
type
;
void
*
user_data
;
/* May be GtkWidget or GtkNativeDialog */
gpointer
dialog
;
GtkWidget
*
ok_button
;
size_t
cb_count
;
GCallback
*
cbs
;
union
{
struct
{
GtkProgressBar
*
progress_bar
;
}
wait
;
struct
{
GtkWidget
*
entry
;
gboolean
multiline
;
gchar
*
hint
;
}
input
;
struct
{
PurpleRequestPage
*
page
;
}
multifield
;
struct
{
gboolean
savedialog
;
}
file
;
}
u
;
}
PidginRequestData
;
static
GHashTable
*
datasheet_stock
=
NULL
;
static
void
pidgin_widget_decorate_account
(
GtkWidget
*
cont
,
PurpleAccount
*
account
)
{
GtkWidget
*
display
=
NULL
;
if
(
!
PURPLE_IS_ACCOUNT
(
account
))
{
return
;
}
if
(
!
GTK_IS_BOX
(
cont
))
{
return
;
}
display
=
pidgin_account_display_new
(
account
);
gtk_widget_set_halign
(
display
,
GTK_ALIGN_CENTER
);
gtk_box_append
(
GTK_BOX
(
cont
),
display
);
}
static
void
generic_response_start
(
PidginRequestData
*
data
)
{
g_return_if_fail
(
data
!=
NULL
);
g_object_set_data
(
G_OBJECT
(
data
->
dialog
),
"pidgin-window-is-closing"
,
GINT_TO_POINTER
(
TRUE
));
gtk_widget_set_visible
(
GTK_WIDGET
(
data
->
dialog
),
FALSE
);
}
static
void
input_response_cb
(
G_GNUC_UNUSED
GtkDialog
*
dialog
,
gint
id
,
PidginRequestData
*
data
)
{
const
char
*
value
;
char
*
multiline_value
=
NULL
;
generic_response_start
(
data
);
if
(
data
->
u
.
input
.
multiline
||
purple_strequal
(
data
->
u
.
input
.
hint
,
"html"
))
{
GtkTextBuffer
*
buffer
=
gtk_text_view_get_buffer
(
GTK_TEXT_VIEW
(
data
->
u
.
input
.
entry
));
if
(
purple_strequal
(
data
->
u
.
input
.
hint
,
"html"
))
{
multiline_value
=
talkatu_markup_get_html
(
buffer
,
NULL
);
}
else
{
GtkTextIter
start_iter
,
end_iter
;
gtk_text_buffer_get_start_iter
(
buffer
,
&
start_iter
);
gtk_text_buffer_get_end_iter
(
buffer
,
&
end_iter
);
multiline_value
=
gtk_text_buffer_get_text
(
buffer
,
&
start_iter
,
&
end_iter
,
FALSE
);
}
value
=
multiline_value
;
}
else
{
value
=
gtk_editable_get_text
(
GTK_EDITABLE
(
data
->
u
.
input
.
entry
));
}
if
(
id
>=
0
&&
(
gsize
)
id
<
data
->
cb_count
&&
data
->
cbs
[
id
]
!=
NULL
)
((
PurpleRequestInputCb
)
data
->
cbs
[
id
])(
data
->
user_data
,
value
);
else
if
(
data
->
cbs
[
1
]
!=
NULL
)
((
PurpleRequestInputCb
)
data
->
cbs
[
1
])(
data
->
user_data
,
value
);
if
(
data
->
u
.
input
.
multiline
)
{
g_free
(
multiline_value
);
}
purple_request_close
(
PURPLE_REQUEST_INPUT
,
data
);
}
static
void
action_response_cb
(
G_GNUC_UNUSED
GtkDialog
*
dialog
,
gint
id
,
PidginRequestData
*
data
)
{
generic_response_start
(
data
);
if
(
id
>=
0
&&
(
gsize
)
id
<
data
->
cb_count
&&
data
->
cbs
[
id
]
!=
NULL
)
((
PurpleRequestActionCb
)
data
->
cbs
[
id
])(
data
->
user_data
,
id
);
purple_request_close
(
PURPLE_REQUEST_INPUT
,
data
);
}
static
void
choice_response_cb
(
GtkDialog
*
dialog
,
gint
id
,
PidginRequestData
*
data
)
{
GtkDropDown
*
dropdown
=
g_object_get_data
(
G_OBJECT
(
dialog
),
"dropdown"
);
generic_response_start
(
data
);
if
(
0
<=
id
&&
(
gsize
)
id
<
data
->
cb_count
&&
data
->
cbs
[
id
]
!=
NULL
)
{
GObject
*
item
=
gtk_drop_down_get_selected_item
(
dropdown
);
if
(
G_IS_OBJECT
(
item
))
{
gpointer
value
=
g_object_get_data
(
item
,
"choice_value"
);
((
PurpleRequestChoiceCb
)
data
->
cbs
[
id
])(
data
->
user_data
,
value
);
}
}
purple_request_close
(
PURPLE_REQUEST_INPUT
,
data
);
}
static
void
field_choice_option_cb
(
GObject
*
obj
,
G_GNUC_UNUSED
GParamSpec
*
pspec
,
gpointer
data
)
{
PurpleRequestField
*
field
=
data
;
GtkDropDown
*
dropdown
=
GTK_DROP_DOWN
(
obj
);
GObject
*
item
=
gtk_drop_down_get_selected_item
(
dropdown
);
if
(
G_IS_OBJECT
(
item
))
{
gpointer
value
=
g_object_get_data
(
item
,
"choice_value"
);
purple_request_field_choice_set_value
(
PURPLE_REQUEST_FIELD_CHOICE
(
field
),
value
);
}
}
static
void
field_account_cb
(
GObject
*
obj
,
G_GNUC_UNUSED
GParamSpec
*
pspec
,
gpointer
data
)
{
PurpleRequestField
*
field
=
data
;
PidginAccountChooser
*
chooser
=
PIDGIN_ACCOUNT_CHOOSER
(
obj
);
purple_request_field_account_set_value
(
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
),
pidgin_account_chooser_get_selected
(
chooser
));
}
static
void
multifield_response_cb
(
G_GNUC_UNUSED
GtkDialog
*
dialog
,
gint
response
,
gpointer
data
)
{
PidginRequestData
*
req_data
=
data
;
PurpleRequestFieldsCb
cb
=
NULL
;
generic_response_start
(
req_data
);
if
(
response
==
GTK_RESPONSE_OK
)
{
cb
=
(
PurpleRequestFieldsCb
)
req_data
->
cbs
[
0
];
}
else
if
(
response
==
GTK_RESPONSE_CANCEL
||
response
==
GTK_RESPONSE_DELETE_EVENT
)
{
cb
=
(
PurpleRequestFieldsCb
)
req_data
->
cbs
[
1
];
}
else
if
(
2
<=
response
&&
(
gsize
)
response
<
req_data
->
cb_count
)
{
cb
=
(
PurpleRequestFieldsCb
)
req_data
->
cbs
[
response
];
}
if
(
cb
!=
NULL
)
{
cb
(
req_data
->
user_data
,
req_data
->
u
.
multifield
.
page
);
}
purple_request_close
(
PURPLE_REQUEST_FIELDS
,
data
);
}
static
gchar
*
pidgin_request_escape
(
PurpleRequestCommonParameters
*
cpar
,
const
gchar
*
text
)
{
if
(
text
==
NULL
)
return
NULL
;
if
(
purple_request_cpar_is_html
(
cpar
))
{
gboolean
valid
;
valid
=
pango_parse_markup
(
text
,
-1
,
0
,
NULL
,
NULL
,
NULL
,
NULL
);
if
(
valid
)
return
g_strdup
(
text
);
else
{
purple_debug_error
(
"pidgin"
,
"Passed label text is not "
"a valid markup. Falling back to plain text."
);
}
}
return
g_markup_escape_text
(
text
,
-1
);
}
static
GtkWidget
*
pidgin_request_dialog_icon
(
PurpleRequestType
dialog_type
,
PurpleRequestCommonParameters
*
cpar
)
{
GtkWidget
*
img
=
NULL
;
PurpleRequestIconType
icon_type
;
gconstpointer
icon_data
;
gsize
icon_size
;
const
gchar
*
icon_name
=
"dialog-question"
;
/* Dialog icon. */
icon_data
=
purple_request_cpar_get_custom_icon
(
cpar
,
&
icon_size
);
if
(
icon_data
)
{
GdkPixbuf
*
pixbuf
;
pixbuf
=
purple_gdk_pixbuf_from_data
(
icon_data
,
icon_size
);
if
(
pixbuf
)
{
/* scale the image if it is too large */
int
width
=
gdk_pixbuf_get_width
(
pixbuf
);
int
height
=
gdk_pixbuf_get_height
(
pixbuf
);
if
(
width
>
128
||
height
>
128
)
{
int
scaled_width
=
width
>
height
?
128
:
(
128
*
width
)
/
height
;
int
scaled_height
=
height
>
width
?
128
:
(
128
*
height
)
/
width
;
GdkPixbuf
*
scaled
;
purple_debug_info
(
"pidgin"
,
"dialog icon was "
"too large, scaling it down"
);
scaled
=
gdk_pixbuf_scale_simple
(
pixbuf
,
scaled_width
,
scaled_height
,
GDK_INTERP_BILINEAR
);
if
(
scaled
)
{
g_object_unref
(
pixbuf
);
pixbuf
=
scaled
;
}
}
img
=
gtk_image_new_from_pixbuf
(
pixbuf
);
g_object_unref
(
pixbuf
);
}
else
{
purple_debug_info
(
"pidgin"
,
"failed to parse dialog icon"
);
}
}
if
(
img
)
return
img
;
icon_type
=
purple_request_cpar_get_icon
(
cpar
);
switch
(
icon_type
)
{
case
PURPLE_REQUEST_ICON_DEFAULT
:
icon_name
=
NULL
;
break
;
case
PURPLE_REQUEST_ICON_REQUEST
:
icon_name
=
"dialog-question"
;
break
;
case
PURPLE_REQUEST_ICON_DIALOG
:
case
PURPLE_REQUEST_ICON_INFO
:
case
PURPLE_REQUEST_ICON_WAIT
:
/* TODO: we need another icon */
icon_name
=
"dialog-information"
;
break
;
case
PURPLE_REQUEST_ICON_WARNING
:
icon_name
=
"dialog-warning"
;
break
;
case
PURPLE_REQUEST_ICON_ERROR
:
icon_name
=
"dialog-error"
;
break
;
/* intentionally no default value */
}
if
(
icon_name
==
NULL
)
{
switch
(
dialog_type
)
{
case
PURPLE_REQUEST_INPUT
:
case
PURPLE_REQUEST_CHOICE
:
case
PURPLE_REQUEST_ACTION
:
case
PURPLE_REQUEST_FIELDS
:
case
PURPLE_REQUEST_FILE
:
case
PURPLE_REQUEST_FOLDER
:
icon_name
=
"dialog-question"
;
break
;
case
PURPLE_REQUEST_WAIT
:
icon_name
=
"dialog-information"
;
break
;
/* intentionally no default value */
}
}
if
(
icon_name
==
NULL
)
{
icon_name
=
"dialog-question"
;
}
img
=
gtk_image_new_from_icon_name
(
icon_name
);
gtk_image_set_icon_size
(
GTK_IMAGE
(
img
),
GTK_ICON_SIZE_LARGE
);
return
img
;
}
static
void
pidgin_request_help_clicked
(
GtkButton
*
button
,
G_GNUC_UNUSED
gpointer
_unused
)
{
PurpleRequestHelpCb
cb
;
gpointer
data
;
cb
=
g_object_get_data
(
G_OBJECT
(
button
),
"pidgin-help-cb"
);
data
=
g_object_get_data
(
G_OBJECT
(
button
),
"pidgin-help-data"
);
g_return_if_fail
(
cb
!=
NULL
);
cb
(
data
);
}
static
void
pidgin_request_add_help
(
GtkDialog
*
dialog
,
PurpleRequestCommonParameters
*
cpar
)
{
GtkWidget
*
button
;
PurpleRequestHelpCb
help_cb
;
gpointer
help_data
;
help_cb
=
purple_request_cpar_get_help_cb
(
cpar
,
&
help_data
);
if
(
help_cb
==
NULL
)
return
;
button
=
gtk_dialog_add_button
(
dialog
,
_
(
"_Help"
),
GTK_RESPONSE_HELP
);
g_object_set_data
(
G_OBJECT
(
button
),
"pidgin-help-cb"
,
help_cb
);
g_object_set_data
(
G_OBJECT
(
button
),
"pidgin-help-data"
,
help_data
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
pidgin_request_help_clicked
),
NULL
);
}
static
void
*
pidgin_request_input
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
const
char
*
default_value
,
gboolean
multiline
,
gboolean
masked
,
gchar
*
hint
,
const
char
*
ok_text
,
GCallback
ok_cb
,
const
char
*
cancel_text
,
GCallback
cancel_cb
,
PurpleRequestCommonParameters
*
cpar
,
void
*
user_data
)
{
PidginRequestData
*
data
;
GtkWidget
*
dialog
;
GtkWidget
*
vbox
;
GtkWidget
*
hbox
;
GtkLabel
*
label
;
GtkWidget
*
img
;
GtkWidget
*
content
;
char
*
label_text
;
char
*
primary_esc
,
*
secondary_esc
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_INPUT
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
2
;
data
->
cbs
=
g_new0
(
GCallback
,
2
);
data
->
cbs
[
0
]
=
ok_cb
;
data
->
cbs
[
1
]
=
cancel_cb
;
/* Create the dialog. */
dialog
=
gtk_dialog_new_with_buttons
(
title
?
title
:
PIDGIN_ALERT_TITLE
,
NULL
,
0
,
cancel_text
,
1
,
ok_text
,
0
,
NULL
);
data
->
dialog
=
dialog
;
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
input_response_cb
),
data
);
/* Setup the dialog */
gtk_widget_set_margin_top
(
dialog
,
6
);
gtk_widget_set_margin_bottom
(
dialog
,
6
);
gtk_widget_set_margin_start
(
dialog
,
6
);
gtk_widget_set_margin_end
(
dialog
,
6
);
content
=
gtk_dialog_get_content_area
(
GTK_DIALOG
(
dialog
));
gtk_widget_set_margin_top
(
content
,
6
);
gtk_widget_set_margin_bottom
(
content
,
6
);
gtk_widget_set_margin_start
(
content
,
6
);
gtk_widget_set_margin_end
(
content
,
6
);
if
(
!
multiline
)
gtk_window_set_resizable
(
GTK_WINDOW
(
dialog
),
FALSE
);
gtk_dialog_set_default_response
(
GTK_DIALOG
(
dialog
),
0
);
gtk_box_set_spacing
(
GTK_BOX
(
gtk_dialog_get_content_area
(
GTK_DIALOG
(
dialog
))),
12
);
/* Setup the main horizontal box */
hbox
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
12
);
gtk_box_append
(
GTK_BOX
(
content
),
hbox
);
/* Dialog icon. */
img
=
pidgin_request_dialog_icon
(
PURPLE_REQUEST_INPUT
,
cpar
);
gtk_widget_set_halign
(
img
,
GTK_ALIGN_START
);
gtk_widget_set_valign
(
img
,
GTK_ALIGN_START
);
gtk_box_append
(
GTK_BOX
(
hbox
),
img
);
pidgin_request_add_help
(
GTK_DIALOG
(
dialog
),
cpar
);
/* Vertical box */
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
12
);
gtk_widget_set_hexpand
(
vbox
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
hbox
),
vbox
);
pidgin_widget_decorate_account
(
vbox
,
purple_request_cpar_get_account
(
cpar
));
/* Descriptive label */
primary_esc
=
pidgin_request_escape
(
cpar
,
primary
);
secondary_esc
=
pidgin_request_escape
(
cpar
,
secondary
);
label_text
=
g_strdup_printf
((
primary
?
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>"
"%s</span>%s%s"
:
"%s%s%s"
),
(
primary
?
primary_esc
:
""
),
((
primary
&&
secondary
)
?
"
\n\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
GTK_LABEL
(
gtk_label_new
(
NULL
));
gtk_label_set_markup
(
label
,
label_text
);
gtk_label_set_wrap
(
label
,
TRUE
);
gtk_label_set_xalign
(
label
,
0
);
gtk_label_set_yalign
(
label
,
0
);
gtk_box_append
(
GTK_BOX
(
vbox
),
GTK_WIDGET
(
label
));
g_free
(
label_text
);
/* Entry field. */
data
->
u
.
input
.
multiline
=
multiline
;
data
->
u
.
input
.
hint
=
g_strdup
(
hint
);
if
(
multiline
||
purple_strequal
(
data
->
u
.
input
.
hint
,
"html"
))
{
GtkWidget
*
editor
=
talkatu_editor_new
();
GtkWidget
*
input
=
talkatu_editor_get_input
(
TALKATU_EDITOR
(
editor
));
GtkTextBuffer
*
buffer
=
NULL
;
gtk_widget_set_size_request
(
input
,
320
,
130
);
gtk_widget_set_name
(
input
,
"pidgin_request_input"
);
gtk_widget_set_vexpand
(
editor
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
editor
);
if
(
purple_strequal
(
data
->
u
.
input
.
hint
,
"html"
))
{
GSimpleActionGroup
*
ag
=
NULL
;
ag
=
talkatu_action_group_new
(
TALKATU_FORMAT_HTML
);
buffer
=
talkatu_buffer_new
(
ag
);
talkatu_action_group_set_buffer
(
TALKATU_ACTION_GROUP
(
ag
),
buffer
);
g_clear_object
(
&
ag
);
if
(
default_value
!=
NULL
)
{
talkatu_markup_set_html
(
TALKATU_BUFFER
(
buffer
),
default_value
,
-1
);
}
}
else
{
buffer
=
gtk_text_buffer_new
(
NULL
);
if
(
default_value
!=
NULL
)
{
gtk_text_buffer_set_text
(
buffer
,
default_value
,
-1
);
}
}
gtk_text_view_set_buffer
(
GTK_TEXT_VIEW
(
input
),
buffer
);
data
->
u
.
input
.
entry
=
input
;
}
else
{
GtkWidget
*
entry
=
NULL
;
if
(
masked
)
{
entry
=
gtk_password_entry_new
();
g_object_set
(
entry
,
"activates-default"
,
TRUE
,
"show-peek-icon"
,
TRUE
,
NULL
);
}
else
{
entry
=
gtk_entry_new
();
gtk_entry_set_activates_default
(
GTK_ENTRY
(
entry
),
TRUE
);
}
gtk_box_append
(
GTK_BOX
(
vbox
),
entry
);
if
(
default_value
!=
NULL
)
{
gtk_editable_set_text
(
GTK_EDITABLE
(
entry
),
default_value
);
}
data
->
u
.
input
.
entry
=
entry
;
}
pidgin_set_accessible_label
(
data
->
u
.
input
.
entry
,
label
);
pidgin_auto_parent_window
(
dialog
);
/* Show everything. */
gtk_widget_set_visible
(
dialog
,
TRUE
);
return
data
;
}
static
void
*
pidgin_request_choice
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
gpointer
default_value
,
const
char
*
ok_text
,
GCallback
ok_cb
,
const
char
*
cancel_text
,
GCallback
cancel_cb
,
PurpleRequestCommonParameters
*
cpar
,
void
*
user_data
,
va_list
args
)
{
PidginRequestData
*
data
;
GtkWidget
*
dialog
;
GtkWidget
*
vbox
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GtkWidget
*
img
;
GtkWidget
*
dropdown
;
GListModel
*
model
;
GtkWidget
*
content
;
char
*
label_text
;
const
char
*
radio_text
;
char
*
primary_esc
,
*
secondary_esc
;
guint
index
,
selected
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_ACTION
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
2
;
data
->
cbs
=
g_new0
(
GCallback
,
2
);
data
->
cbs
[
0
]
=
cancel_cb
;
data
->
cbs
[
1
]
=
ok_cb
;
/* Create the dialog. */
data
->
dialog
=
dialog
=
gtk_dialog_new
();
if
(
title
!=
NULL
)
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
title
);
#ifdef _WIN32
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
PIDGIN_ALERT_TITLE
);
#endif
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
cancel_text
,
0
);
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
ok_text
,
1
);
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
choice_response_cb
),
data
);
/* Setup the dialog */
gtk_widget_set_margin_top
(
dialog
,
6
);
gtk_widget_set_margin_bottom
(
dialog
,
6
);
gtk_widget_set_margin_start
(
dialog
,
6
);
gtk_widget_set_margin_end
(
dialog
,
6
);
gtk_window_set_resizable
(
GTK_WINDOW
(
dialog
),
FALSE
);
content
=
gtk_dialog_get_content_area
(
GTK_DIALOG
(
dialog
));
gtk_widget_set_margin_top
(
content
,
6
);
gtk_widget_set_margin_bottom
(
content
,
6
);
gtk_widget_set_margin_start
(
content
,
6
);
gtk_widget_set_margin_end
(
content
,
6
);
gtk_box_set_spacing
(
GTK_BOX
(
content
),
12
);
/* Setup the main horizontal box */
hbox
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
12
);
gtk_box_append
(
GTK_BOX
(
content
),
hbox
);
/* Dialog icon. */
img
=
pidgin_request_dialog_icon
(
PURPLE_REQUEST_CHOICE
,
cpar
);
gtk_widget_set_halign
(
img
,
GTK_ALIGN_START
);
gtk_widget_set_valign
(
img
,
GTK_ALIGN_START
);
gtk_box_append
(
GTK_BOX
(
hbox
),
img
);
pidgin_request_add_help
(
GTK_DIALOG
(
dialog
),
cpar
);
/* Vertical box */
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
12
);
gtk_box_append
(
GTK_BOX
(
hbox
),
vbox
);
pidgin_widget_decorate_account
(
vbox
,
purple_request_cpar_get_account
(
cpar
));
/* Descriptive label */
primary_esc
=
pidgin_request_escape
(
cpar
,
primary
);
secondary_esc
=
pidgin_request_escape
(
cpar
,
secondary
);
label_text
=
g_strdup_printf
((
primary
?
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>"
"%s</span>%s%s"
:
"%s%s%s"
),
(
primary
?
primary_esc
:
""
),
((
primary
&&
secondary
)
?
"
\n\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_label_set_xalign
(
GTK_LABEL
(
label
),
0
);
gtk_label_set_yalign
(
GTK_LABEL
(
label
),
0
);
gtk_widget_set_vexpand
(
label
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
label
);
g_free
(
label_text
);
dropdown
=
gtk_drop_down_new_from_strings
(
NULL
);
gtk_box_append
(
GTK_BOX
(
vbox
),
dropdown
);
g_object_set_data
(
G_OBJECT
(
dialog
),
"dropdown"
,
dropdown
);
index
=
0
;
selected
=
GTK_INVALID_LIST_POSITION
;
model
=
gtk_drop_down_get_model
(
GTK_DROP_DOWN
(
dropdown
));
while
((
radio_text
=
va_arg
(
args
,
const
char
*
)))
{
GObject
*
item
=
NULL
;
gpointer
resp
=
va_arg
(
args
,
gpointer
);
gtk_string_list_append
(
GTK_STRING_LIST
(
model
),
radio_text
);
item
=
g_list_model_get_item
(
model
,
index
);
g_object_set_data
(
item
,
"choice_value"
,
resp
);
if
(
resp
==
default_value
)
{
selected
=
index
;
}
g_clear_object
(
&
item
);
index
++
;
}
if
(
selected
!=
GTK_INVALID_LIST_POSITION
)
{
gtk_drop_down_set_selected
(
GTK_DROP_DOWN
(
dropdown
),
selected
);
}
/* Show everything. */
pidgin_auto_parent_window
(
dialog
);
gtk_widget_set_visible
(
dialog
,
TRUE
);
return
data
;
}
static
void
*
pidgin_request_action
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
int
default_action
,
PurpleRequestCommonParameters
*
cpar
,
void
*
user_data
,
size_t
action_count
,
va_list
actions
)
{
PidginRequestData
*
data
;
GtkWidget
*
dialog
;
GtkWidget
*
vbox
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GtkWidget
*
img
=
NULL
;
GtkWidget
*
content
;
void
**
buttons
;
char
*
label_text
;
char
*
primary_esc
,
*
secondary_esc
;
gsize
i
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_ACTION
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
action_count
;
data
->
cbs
=
g_new0
(
GCallback
,
action_count
);
/* Reverse the buttons */
buttons
=
g_new0
(
void
*
,
action_count
*
2
);
for
(
i
=
0
;
i
<
action_count
*
2
;
i
+=
2
)
{
buttons
[(
action_count
*
2
)
-
i
-
2
]
=
va_arg
(
actions
,
char
*
);
buttons
[(
action_count
*
2
)
-
i
-
1
]
=
va_arg
(
actions
,
GCallback
);
}
/* Create the dialog. */
data
->
dialog
=
dialog
=
gtk_dialog_new
();
gtk_window_set_deletable
(
GTK_WINDOW
(
data
->
dialog
),
FALSE
);
if
(
title
!=
NULL
)
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
title
);
#ifdef _WIN32
else
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
PIDGIN_ALERT_TITLE
);
#endif
for
(
i
=
0
;
i
<
action_count
;
i
++
)
{
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
buttons
[
2
*
i
],
i
);
data
->
cbs
[
i
]
=
buttons
[
2
*
i
+
1
];
}
g_free
(
buttons
);
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
action_response_cb
),
data
);
/* Setup the dialog */
gtk_widget_set_margin_top
(
dialog
,
6
);
gtk_widget_set_margin_bottom
(
dialog
,
6
);
gtk_widget_set_margin_start
(
dialog
,
6
);
gtk_widget_set_margin_end
(
dialog
,
6
);
gtk_window_set_resizable
(
GTK_WINDOW
(
dialog
),
FALSE
);
content
=
gtk_dialog_get_content_area
(
GTK_DIALOG
(
dialog
));
gtk_widget_set_margin_top
(
content
,
6
);
gtk_widget_set_margin_bottom
(
content
,
6
);
gtk_widget_set_margin_start
(
content
,
6
);
gtk_widget_set_margin_end
(
content
,
6
);
gtk_box_set_spacing
(
GTK_BOX
(
content
),
12
);
/* Setup the main horizontal box */
hbox
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
12
);
gtk_box_append
(
GTK_BOX
(
content
),
hbox
);
img
=
pidgin_request_dialog_icon
(
PURPLE_REQUEST_ACTION
,
cpar
);
gtk_widget_set_halign
(
img
,
GTK_ALIGN_START
);
gtk_widget_set_valign
(
img
,
GTK_ALIGN_START
);
gtk_box_append
(
GTK_BOX
(
hbox
),
img
);
/* Vertical box */
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
12
);
gtk_box_append
(
GTK_BOX
(
hbox
),
vbox
);
pidgin_widget_decorate_account
(
vbox
,
purple_request_cpar_get_account
(
cpar
));
pidgin_request_add_help
(
GTK_DIALOG
(
dialog
),
cpar
);
/* Descriptive label */
primary_esc
=
pidgin_request_escape
(
cpar
,
primary
);
secondary_esc
=
pidgin_request_escape
(
cpar
,
secondary
);
label_text
=
g_strdup_printf
((
primary
?
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>"
"%s</span>%s%s"
:
"%s%s%s"
),
(
primary
?
primary_esc
:
""
),
((
primary
&&
secondary
)
?
"
\n\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_label_set_xalign
(
GTK_LABEL
(
label
),
0
);
gtk_label_set_yalign
(
GTK_LABEL
(
label
),
0
);
gtk_label_set_selectable
(
GTK_LABEL
(
label
),
TRUE
);
gtk_widget_set_vexpand
(
label
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
label
);
g_free
(
label_text
);
if
(
default_action
!=
PURPLE_DEFAULT_ACTION_NONE
)
{
/*
* Need to invert the default_action number because the
* buttons are added to the dialog in reverse order.
*/
gtk_dialog_set_default_response
(
GTK_DIALOG
(
dialog
),
action_count
-
1
-
default_action
);
}
/* Show everything. */
pidgin_auto_parent_window
(
dialog
);
gtk_widget_set_visible
(
dialog
,
TRUE
);
return
data
;
}
static
void
wait_response_cb
(
G_GNUC_UNUSED
GtkDialog
*
dialog
,
G_GNUC_UNUSED
gint
id
,
PidginRequestData
*
data
)
{
generic_response_start
(
data
);
if
(
data
->
cbs
[
0
]
!=
NULL
)
((
PurpleRequestCancelCb
)
data
->
cbs
[
0
])(
data
->
user_data
);
purple_request_close
(
PURPLE_REQUEST_FIELDS
,
data
);
}
static
void
*
pidgin_request_wait
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
gboolean
with_progress
,
PurpleRequestCancelCb
cancel_cb
,
PurpleRequestCommonParameters
*
cpar
,
void
*
user_data
)
{
PidginRequestData
*
data
;
GtkWidget
*
dialog
,
*
content
;
GtkWidget
*
hbox
,
*
vbox
,
*
img
,
*
label
;
gchar
*
primary_esc
,
*
secondary_esc
,
*
label_text
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_WAIT
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
1
;
data
->
cbs
=
g_new0
(
GCallback
,
1
);
data
->
cbs
[
0
]
=
(
GCallback
)
cancel_cb
;
data
->
dialog
=
dialog
=
gtk_dialog_new
();
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
wait_response_cb
),
data
);
gtk_window_set_deletable
(
GTK_WINDOW
(
data
->
dialog
),
cancel_cb
!=
NULL
);
if
(
title
!=
NULL
)
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
title
);
else
gtk_window_set_title
(
GTK_WINDOW
(
dialog
),
_
(
"Please wait"
));
/* Setup the dialog */
gtk_widget_set_margin_top
(
dialog
,
6
);
gtk_widget_set_margin_bottom
(
dialog
,
6
);
gtk_widget_set_margin_start
(
dialog
,
6
);
gtk_widget_set_margin_end
(
dialog
,
6
);
gtk_window_set_resizable
(
GTK_WINDOW
(
dialog
),
FALSE
);
content
=
gtk_dialog_get_content_area
(
GTK_DIALOG
(
dialog
));
gtk_widget_set_margin_top
(
content
,
6
);
gtk_widget_set_margin_bottom
(
content
,
6
);
gtk_widget_set_margin_start
(
content
,
6
);
gtk_widget_set_margin_end
(
content
,
6
);
gtk_box_set_spacing
(
GTK_BOX
(
content
),
12
);
/* Setup the main horizontal box */
hbox
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
12
);
gtk_box_append
(
GTK_BOX
(
content
),
hbox
);
img
=
pidgin_request_dialog_icon
(
PURPLE_REQUEST_WAIT
,
cpar
);
gtk_widget_set_halign
(
img
,
GTK_ALIGN_START
);
gtk_widget_set_valign
(
img
,
GTK_ALIGN_START
);
gtk_box_append
(
GTK_BOX
(
hbox
),
img
);
/* Cancel button */
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
_
(
"Cancel"
),
GTK_RESPONSE_CANCEL
);
/* Vertical box */
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
12
);
gtk_box_append
(
GTK_BOX
(
hbox
),
vbox
);
pidgin_widget_decorate_account
(
vbox
,
purple_request_cpar_get_account
(
cpar
));
pidgin_request_add_help
(
GTK_DIALOG
(
dialog
),
cpar
);
/* Descriptive label */
primary_esc
=
pidgin_request_escape
(
cpar
,
primary
);
secondary_esc
=
pidgin_request_escape
(
cpar
,
secondary
);
label_text
=
g_strdup_printf
((
primary
?
"<span weight=
\"
bold
\"
"
"size=
\"
larger
\"
>%s</span>%s%s"
:
"%s%s%s"
),
(
primary
?
primary_esc
:
""
),
((
primary
&&
secondary
)
?
"
\n\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_label_set_xalign
(
GTK_LABEL
(
label
),
0
);
gtk_label_set_yalign
(
GTK_LABEL
(
label
),
0
);
gtk_label_set_selectable
(
GTK_LABEL
(
label
),
FALSE
);
gtk_widget_set_vexpand
(
label
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
label
);
g_free
(
label_text
);
if
(
with_progress
)
{
GtkProgressBar
*
bar
;
bar
=
data
->
u
.
wait
.
progress_bar
=
GTK_PROGRESS_BAR
(
gtk_progress_bar_new
());
gtk_progress_bar_set_fraction
(
bar
,
0
);
gtk_box_append
(
GTK_BOX
(
vbox
),
GTK_WIDGET
(
bar
));
}
/* Show everything. */
pidgin_auto_parent_window
(
dialog
);
gtk_widget_set_visible
(
dialog
,
TRUE
);
return
data
;
}
static
void
pidgin_request_wait_update
(
void
*
ui_handle
,
gboolean
pulse
,
gfloat
fraction
)
{
GtkProgressBar
*
bar
;
PidginRequestData
*
data
=
ui_handle
;
g_return_if_fail
(
data
->
type
==
PURPLE_REQUEST_WAIT
);
bar
=
data
->
u
.
wait
.
progress_bar
;
if
(
pulse
)
gtk_progress_bar_pulse
(
bar
);
else
gtk_progress_bar_set_fraction
(
bar
,
fraction
);
}
static
GtkWidget
*
create_label_field
(
void
)
{
GtkWidget
*
row
=
NULL
;
GtkWidget
*
label
=
NULL
;
row
=
adw_preferences_row_new
();
gtk_widget_set_focusable
(
row
,
FALSE
);
gtk_list_box_row_set_activatable
(
GTK_LIST_BOX_ROW
(
row
),
FALSE
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_xalign
(
GTK_LABEL
(
label
),
0.0
);
gtk_widget_set_margin_start
(
label
,
12
);
gtk_widget_set_margin_end
(
label
,
12
);
gtk_widget_set_margin_bottom
(
label
,
12
);
gtk_widget_set_margin_top
(
label
,
12
);
g_object_bind_property
(
row
,
"title"
,
label
,
"label"
,
G_BINDING_DEFAULT
);
g_object_bind_property
(
row
,
"use-markup"
,
label
,
"use-markup"
,
G_BINDING_DEFAULT
);
g_object_bind_property
(
row
,
"use-underline"
,
label
,
"use-underline"
,
G_BINDING_DEFAULT
);
gtk_list_box_row_set_child
(
GTK_LIST_BOX_ROW
(
row
),
label
);
return
row
;
}
static
void
multiline_state_flags_changed_cb
(
GtkWidget
*
self
,
GtkStateFlags
flags
,
gpointer
data
)
{
GtkWidget
*
row
=
data
;
gboolean
before
=
FALSE
,
after
=
FALSE
;
before
=
!!
(
flags
&
GTK_STATE_FLAG_FOCUS_WITHIN
);
after
=
!!
(
gtk_widget_get_state_flags
(
self
)
&
GTK_STATE_FLAG_FOCUS_WITHIN
);
if
(
before
!=
after
)
{
if
(
after
)
{
gtk_widget_add_css_class
(
row
,
"focused"
);
}
else
{
gtk_widget_remove_css_class
(
row
,
"focused"
);
}
}
}
static
GtkWidget
*
create_string_field
(
PurpleRequestField
*
field
,
G_GNUC_UNUSED
PurpleKeyValuePair
**
hinted_widget
)
{
PurpleRequestFieldString
*
strfield
=
PURPLE_REQUEST_FIELD_STRING
(
field
);
const
char
*
value
;
GtkWidget
*
row
;
value
=
purple_request_field_string_get_default_value
(
strfield
);
if
(
purple_request_field_string_is_multiline
(
strfield
))
{
GtkWidget
*
vbox
=
NULL
;
GtkWidget
*
title
=
NULL
;
GtkWidget
*
textview
=
NULL
;
GtkWidget
*
sw
=
NULL
;
GtkTextBuffer
*
buffer
=
NULL
;
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
6
);
gtk_widget_set_margin_top
(
vbox
,
6
);
title
=
gtk_label_new
(
NULL
);
gtk_widget_set_halign
(
title
,
GTK_ALIGN_START
);
gtk_widget_set_margin_start
(
title
,
12
);
gtk_widget_set_margin_end
(
title
,
12
);
gtk_widget_add_css_class
(
title
,
"subtitle"
);
gtk_box_append
(
GTK_BOX
(
vbox
),
title
);
textview
=
gtk_text_view_new
();
gtk_widget_set_margin_start
(
textview
,
12
);
gtk_widget_set_margin_end
(
textview
,
12
);
gtk_widget_remove_css_class
(
textview
,
"view"
);
gtk_text_view_set_wrap_mode
(
GTK_TEXT_VIEW
(
textview
),
GTK_WRAP_WORD_CHAR
);
buffer
=
gtk_text_view_get_buffer
(
GTK_TEXT_VIEW
(
textview
));
if
(
value
!=
NULL
)
{
gtk_text_buffer_set_text
(
buffer
,
value
,
-1
);
}
g_object_bind_property
(
field
,
"value"
,
buffer
,
"text"
,
G_BINDING_BIDIRECTIONAL
);
sw
=
gtk_scrolled_window_new
();
gtk_scrolled_window_set_child
(
GTK_SCROLLED_WINDOW
(
sw
),
textview
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_NEVER
,
GTK_POLICY_ALWAYS
);
gtk_widget_set_size_request
(
sw
,
-1
,
75
);
gtk_widget_set_hexpand
(
sw
,
TRUE
);
gtk_widget_set_vexpand
(
sw
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
sw
);
row
=
adw_preferences_row_new
();
g_object_bind_property
(
row
,
"title"
,
title
,
"label"
,
G_BINDING_DEFAULT
);
g_object_bind_property
(
row
,
"use-markup"
,
title
,
"use-markup"
,
G_BINDING_DEFAULT
);
g_object_bind_property
(
row
,
"use-underline"
,
title
,
"use-underline"
,
G_BINDING_DEFAULT
);
gtk_list_box_row_set_child
(
GTK_LIST_BOX_ROW
(
row
),
vbox
);
gtk_widget_set_focusable
(
row
,
FALSE
);
gtk_widget_add_css_class
(
row
,
"entry"
);
g_signal_connect
(
textview
,
"state-flags-changed"
,
G_CALLBACK
(
multiline_state_flags_changed_cb
),
row
);
}
else
{
if
(
purple_request_field_string_is_masked
(
strfield
))
{
row
=
adw_password_entry_row_new
();
}
else
{
row
=
adw_entry_row_new
();
}
if
(
value
!=
NULL
)
{
gtk_editable_set_text
(
GTK_EDITABLE
(
row
),
value
);
}
g_object_bind_property
(
field
,
"value"
,
row
,
"text"
,
G_BINDING_BIDIRECTIONAL
);
}
return
row
;
}
static
GtkWidget
*
create_int_field
(
PurpleRequestField
*
field
,
G_GNUC_UNUSED
PurpleKeyValuePair
**
hinted_widget
)
{
PurpleRequestFieldInt
*
intfield
=
PURPLE_REQUEST_FIELD_INT
(
field
);
GtkWidget
*
widget
=
NULL
;
GtkWidget
*
spin
=
NULL
;
int
value
;
spin
=
gtk_spin_button_new_with_range
(
purple_request_field_int_get_lower_bound
(
intfield
),
purple_request_field_int_get_upper_bound
(
intfield
),
1
);
value
=
purple_request_field_int_get_default_value
(
intfield
);
gtk_spin_button_set_value
(
GTK_SPIN_BUTTON
(
spin
),
value
);
g_object_bind_property
(
field
,
"value"
,
spin
,
"value"
,
G_BINDING_BIDIRECTIONAL
);
gtk_widget_set_valign
(
spin
,
GTK_ALIGN_CENTER
);
widget
=
adw_action_row_new
();
gtk_widget_set_focusable
(
widget
,
FALSE
);
adw_action_row_add_suffix
(
ADW_ACTION_ROW
(
widget
),
spin
);
adw_action_row_set_activatable_widget
(
ADW_ACTION_ROW
(
widget
),
spin
);
return
widget
;
}
static
GtkWidget
*
create_bool_field
(
PurpleRequestField
*
field
)
{
PurpleRequestFieldBool
*
boolfield
=
PURPLE_REQUEST_FIELD_BOOL
(
field
);
GtkWidget
*
row
=
NULL
;
GtkWidget
*
sw
=
NULL
;
sw
=
gtk_switch_new
();
gtk_switch_set_active
(
GTK_SWITCH
(
sw
),
purple_request_field_bool_get_default_value
(
boolfield
));
gtk_widget_set_focusable
(
sw
,
TRUE
);
gtk_widget_set_valign
(
sw
,
GTK_ALIGN_CENTER
);
g_object_bind_property
(
field
,
"value"
,
sw
,
"active"
,
G_BINDING_SYNC_CREATE
|
G_BINDING_BIDIRECTIONAL
);
row
=
adw_action_row_new
();
gtk_widget_set_focusable
(
row
,
FALSE
);
adw_action_row_add_suffix
(
ADW_ACTION_ROW
(
row
),
sw
);
adw_action_row_set_activatable_widget
(
ADW_ACTION_ROW
(
row
),
sw
);
return
row
;
}
static
GtkWidget
*
create_choice_field
(
PurpleRequestField
*
field
)
{
PurpleRequestFieldChoice
*
choicefield
=
PURPLE_REQUEST_FIELD_CHOICE
(
field
);
GtkWidget
*
widget
;
GListModel
*
model
=
NULL
;
GList
*
elements
=
NULL
;
guint
default_index
=
GTK_INVALID_LIST_POSITION
;
gpointer
default_value
;
guint
index
;
default_value
=
purple_request_field_choice_get_value
(
choicefield
);
widget
=
gtk_drop_down_new_from_strings
(
NULL
);
model
=
gtk_drop_down_get_model
(
GTK_DROP_DOWN
(
widget
));
index
=
0
;
elements
=
purple_request_field_choice_get_elements
(
choicefield
);
for
(
GList
*
l
=
elements
;
l
!=
NULL
;
l
=
g_list_next
(
l
))
{
PurpleKeyValuePair
*
choice
=
l
->
data
;
GObject
*
item
=
NULL
;
gtk_string_list_append
(
GTK_STRING_LIST
(
model
),
choice
->
key
);
item
=
g_list_model_get_item
(
model
,
index
);
g_object_set_data
(
item
,
"choice_value"
,
choice
->
value
);
if
(
choice
->
value
==
default_value
)
{
default_index
=
index
;
}
g_clear_object
(
&
item
);
index
++
;
}
gtk_drop_down_set_selected
(
GTK_DROP_DOWN
(
widget
),
default_index
);
g_signal_connect
(
G_OBJECT
(
widget
),
"notify::selected"
,
G_CALLBACK
(
field_choice_option_cb
),
field
);
if
(
default_index
==
GTK_INVALID_LIST_POSITION
&&
index
>
0
)
{
GObject
*
item
=
g_list_model_get_item
(
model
,
0
);
gpointer
value
=
g_object_get_data
(
item
,
"choice_value"
);
purple_request_field_choice_set_value
(
choicefield
,
value
);
g_clear_object
(
&
item
);
}
return
widget
;
}
static
GtkWidget
*
create_image_field
(
PurpleRequestField
*
field
)
{
PurpleRequestFieldImage
*
ifield
=
PURPLE_REQUEST_FIELD_IMAGE
(
field
);
GtkWidget
*
widget
;
GdkPixbuf
*
buf
,
*
scale
;
buf
=
purple_gdk_pixbuf_from_data
(
(
const
guchar
*
)
purple_request_field_image_get_buffer
(
ifield
),
purple_request_field_image_get_size
(
ifield
));
scale
=
gdk_pixbuf_scale_simple
(
buf
,
purple_request_field_image_get_scale_x
(
ifield
)
*
gdk_pixbuf_get_width
(
buf
),
purple_request_field_image_get_scale_y
(
ifield
)
*
gdk_pixbuf_get_height
(
buf
),
GDK_INTERP_BILINEAR
);
widget
=
gtk_image_new_from_pixbuf
(
scale
);
g_object_unref
(
G_OBJECT
(
buf
));
g_object_unref
(
G_OBJECT
(
scale
));
return
widget
;
}
static
gboolean
field_custom_account_filter_cb
(
gpointer
item
,
gpointer
data
)
{
PurpleRequestFieldAccount
*
field
=
data
;
gboolean
ret
=
FALSE
;
if
(
PURPLE_IS_ACCOUNT
(
item
))
{
ret
=
purple_request_field_account_match
(
field
,
PURPLE_ACCOUNT
(
item
));
}
return
ret
;
}
static
GtkWidget
*
create_account_field
(
PurpleRequestField
*
field
,
GtkWidget
**
account_hint
)
{
PurpleRequestFieldAccount
*
afield
=
NULL
;
GtkWidget
*
widget
=
NULL
;
PurpleAccount
*
account
=
NULL
;
GtkCustomFilter
*
custom_filter
=
NULL
;
GtkFilter
*
filter
=
NULL
;
const
char
*
type_hint
=
NULL
;
widget
=
pidgin_account_chooser_new
();
afield
=
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
);
account
=
purple_request_field_account_get_default_value
(
afield
);
custom_filter
=
gtk_custom_filter_new
(
field_custom_account_filter_cb
,
afield
,
NULL
);
filter
=
GTK_FILTER
(
custom_filter
);
if
(
!
purple_request_field_account_get_show_all
(
afield
))
{
GtkEveryFilter
*
every
=
NULL
;
every
=
gtk_every_filter_new
();
if
(
GTK_IS_FILTER
(
filter
))
{
gtk_multi_filter_append
(
GTK_MULTI_FILTER
(
every
),
filter
);
}
filter
=
pidgin_account_filter_connected_new
();
gtk_multi_filter_append
(
GTK_MULTI_FILTER
(
every
),
filter
);
filter
=
GTK_FILTER
(
every
);
}
pidgin_account_chooser_set_selected
(
PIDGIN_ACCOUNT_CHOOSER
(
widget
),
account
);
g_signal_connect
(
widget
,
"notify::account"
,
G_CALLBACK
(
field_account_cb
),
field
);
if
(
GTK_IS_FILTER
(
filter
))
{
pidgin_account_chooser_set_filter
(
PIDGIN_ACCOUNT_CHOOSER
(
widget
),
filter
);
g_object_unref
(
filter
);
}
type_hint
=
purple_request_field_get_type_hint
(
field
);
if
(
purple_strequal
(
type_hint
,
"account"
))
{
*
account_hint
=
widget
;
}
return
widget
;
}
static
void
setup_list_field_listitem_cb
(
G_GNUC_UNUSED
GtkSignalListItemFactory
*
self
,
GtkListItem
*
item
,
gpointer
data
)
{
PurpleRequestFieldList
*
field
=
data
;
GtkWidget
*
box
=
NULL
;
GtkWidget
*
widget
=
NULL
;
box
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
6
);
gtk_list_item_set_child
(
item
,
box
);
widget
=
gtk_label_new
(
NULL
);
gtk_box_append
(
GTK_BOX
(
box
),
widget
);
if
(
purple_request_field_list_has_icons
(
field
))
{
widget
=
gtk_image_new
();
gtk_box_append
(
GTK_BOX
(
box
),
widget
);
}
}
static
void
bind_list_field_listitem_cb
(
G_GNUC_UNUSED
GtkSignalListItemFactory
*
self
,
GtkListItem
*
item
,
gpointer
data
)
{
PurpleRequestFieldList
*
field
=
data
;
GtkWidget
*
box
=
NULL
;
GtkWidget
*
label
=
NULL
;
GObject
*
wrapper
=
NULL
;
box
=
gtk_list_item_get_child
(
item
);
wrapper
=
gtk_list_item_get_item
(
item
);
label
=
gtk_widget_get_first_child
(
box
);
gtk_label_set_text
(
GTK_LABEL
(
label
),
g_object_get_data
(
wrapper
,
"text"
));
if
(
purple_request_field_list_has_icons
(
field
))
{
GtkWidget
*
image
=
NULL
;
image
=
gtk_widget_get_last_child
(
box
);
gtk_image_set_from_pixbuf
(
GTK_IMAGE
(
image
),
g_object_get_data
(
wrapper
,
"pixbuf"
));
}
}
static
void
list_field_select_changed_cb
(
GtkSelectionModel
*
self
,
G_GNUC_UNUSED
guint
position
,
G_GNUC_UNUSED
guint
n_items
,
gpointer
data
)
{
PurpleRequestFieldList
*
field
=
data
;
GtkBitset
*
bitset
=
NULL
;
purple_request_field_list_clear_selected
(
field
);
bitset
=
gtk_selection_model_get_selection
(
self
);
n_items
=
gtk_bitset_get_size
(
bitset
);
for
(
guint
index
=
0
;
index
<
n_items
;
index
++
)
{
GObject
*
wrapper
=
NULL
;
const
char
*
text
=
NULL
;
wrapper
=
g_list_model_get_item
(
G_LIST_MODEL
(
self
),
gtk_bitset_get_nth
(
bitset
,
index
));
text
=
g_object_get_data
(
wrapper
,
"text"
);
purple_request_field_list_add_selected
(
field
,
text
);
g_object_unref
(
wrapper
);
}
gtk_bitset_unref
(
bitset
);
}
static
GtkWidget
*
create_list_field
(
PurpleRequestField
*
field
)
{
PurpleRequestFieldList
*
listfield
=
PURPLE_REQUEST_FIELD_LIST
(
field
);
GtkWidget
*
sw
;
GtkWidget
*
listview
=
NULL
;
GtkSelectionModel
*
sel
=
NULL
;
GtkListItemFactory
*
factory
=
NULL
;
GListStore
*
store
=
NULL
;
guint
index
=
0
;
GList
*
l
;
gboolean
has_icons
;
has_icons
=
purple_request_field_list_has_icons
(
listfield
);
/* Create the list store */
store
=
g_list_store_new
(
G_TYPE_OBJECT
);
if
(
purple_request_field_list_get_multi_select
(
listfield
))
{
sel
=
GTK_SELECTION_MODEL
(
gtk_multi_selection_new
(
G_LIST_MODEL
(
store
)));
}
else
{
sel
=
GTK_SELECTION_MODEL
(
gtk_single_selection_new
(
G_LIST_MODEL
(
store
)));
}
/* Create the row factory. */
factory
=
gtk_signal_list_item_factory_new
();
g_signal_connect
(
factory
,
"setup"
,
G_CALLBACK
(
setup_list_field_listitem_cb
),
field
);
g_signal_connect
(
factory
,
"bind"
,
G_CALLBACK
(
bind_list_field_listitem_cb
),
field
);
/* Create the list view */
listview
=
gtk_list_view_new
(
sel
,
factory
);
if
(
has_icons
)
{
gtk_widget_set_size_request
(
listview
,
200
,
400
);
}
for
(
index
=
0
,
l
=
purple_request_field_list_get_items
(
listfield
);
l
!=
NULL
;
index
++
,
l
=
l
->
next
)
{
PurpleKeyValuePair
*
item
=
l
->
data
;
const
char
*
text
=
(
const
char
*
)
item
->
key
;
GObject
*
wrapper
=
NULL
;
wrapper
=
g_object_new
(
G_TYPE_OBJECT
,
NULL
);
g_list_store_append
(
store
,
wrapper
);
g_object_set_data
(
wrapper
,
"data"
,
purple_request_field_list_get_data
(
listfield
,
text
));
g_object_set_data_full
(
wrapper
,
"text"
,
g_strdup
(
text
),
g_free
);
if
(
has_icons
)
{
const
char
*
icon_path
=
(
const
char
*
)
item
->
value
;
GdkPixbuf
*
pixbuf
=
NULL
;
if
(
icon_path
)
{
pixbuf
=
purple_gdk_pixbuf_new_from_file
(
icon_path
);
}
g_object_set_data_full
(
wrapper
,
"pixbuf"
,
pixbuf
,
g_object_unref
);
}
if
(
purple_request_field_list_is_selected
(
listfield
,
text
))
{
gtk_selection_model_select_item
(
sel
,
index
,
FALSE
);
}
g_object_unref
(
wrapper
);
}
/*
* We only want to catch changes made by the user, so it's important
* that we wait until after the list is created to connect this
* handler. If we connect the handler before the loop above and
* there are multiple items selected, then selecting the first item
* in the view causes list_field_select_changed_cb to be triggered
* which clears out the rest of the list of selected items.
*/
g_signal_connect
(
G_OBJECT
(
sel
),
"selection-changed"
,
G_CALLBACK
(
list_field_select_changed_cb
),
field
);
sw
=
gtk_scrolled_window_new
();
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_AUTOMATIC
,
GTK_POLICY_AUTOMATIC
);
gtk_scrolled_window_set_child
(
GTK_SCROLLED_WINDOW
(
sw
),
listview
);
return
sw
;
}
static
GdkPixbuf
*
_pidgin_datasheet_stock_icon_get
(
const
gchar
*
stock_name
)
{
GdkPixbuf
*
image
=
NULL
;
if
(
stock_name
==
NULL
)
return
NULL
;
/* core is quitting */
if
(
datasheet_stock
==
NULL
)
return
NULL
;
if
(
g_hash_table_lookup_extended
(
datasheet_stock
,
stock_name
,
NULL
,
(
gpointer
*
)
&
image
))
{
return
image
;
}
purple_debug_error
(
"gtkrequest"
,
"Unknown icon: %s"
,
stock_name
);
return
NULL
;
}
static
PurpleRequestDatasheetRecord
*
datasheet_get_selected_row
(
GtkWidget
*
sheet_widget
)
{
PurpleRequestDatasheet
*
sheet
;
GtkTreeView
*
view
;
GtkTreeSelection
*
selection
;
GtkTreeModel
*
model
;
GtkTreeIter
iter
;
GList
*
sel_list
;
gpointer
key
;
g_return_val_if_fail
(
sheet_widget
!=
NULL
,
NULL
);
view
=
GTK_TREE_VIEW
(
g_object_get_data
(
G_OBJECT
(
sheet_widget
),
"view"
));
sheet
=
g_object_get_data
(
G_OBJECT
(
sheet_widget
),
"sheet"
);
g_return_val_if_fail
(
view
!=
NULL
,
NULL
);
g_return_val_if_fail
(
sheet
!=
NULL
,
NULL
);
selection
=
gtk_tree_view_get_selection
(
view
);
if
(
gtk_tree_selection_count_selected_rows
(
selection
)
!=
1
)
return
NULL
;
sel_list
=
gtk_tree_selection_get_selected_rows
(
selection
,
&
model
);
gtk_tree_model_get_iter
(
model
,
&
iter
,
sel_list
->
data
);
g_list_free_full
(
sel_list
,
(
GDestroyNotify
)
gtk_tree_path_free
);
gtk_tree_model_get
(
model
,
&
iter
,
0
,
&
key
,
-1
);
return
purple_request_datasheet_record_find
(
sheet
,
key
);
}
#if 0
static void
datasheet_button_check_sens(GtkWidget *button, gpointer _sheet_widget)
{
PurpleRequestDatasheetAction *act;
GtkWidget *sheet_widget = GTK_WIDGET(_sheet_widget);
g_return_if_fail(sheet_widget != NULL);
act = g_object_get_data(G_OBJECT(button), "action");
g_return_if_fail(act != NULL);
gtk_widget_set_sensitive(button,
purple_request_datasheet_action_is_sensitive(act,
datasheet_get_selected_row(sheet_widget)));
}
#endif
static
void
datasheet_selection_changed
(
G_GNUC_UNUSED
GtkWidget
*
sheet_widget
)
{
#if 0
gpointer buttons_box;
g_return_if_fail(sheet_widget != NULL);
buttons_box = g_object_get_data(G_OBJECT(sheet_widget), "buttons");
gtk_container_foreach(GTK_CONTAINER(buttons_box),
datasheet_button_check_sens, sheet_widget);
#endif
}
static
void
datasheet_update_rec
(
PurpleRequestDatasheetRecord
*
rec
,
GtkListStore
*
model
,
GtkTreeIter
*
iter
)
{
guint
i
,
col_count
;
PurpleRequestDatasheet
*
sheet
;
g_return_if_fail
(
rec
!=
NULL
);
g_return_if_fail
(
model
!=
NULL
);
g_return_if_fail
(
iter
!=
NULL
);
sheet
=
purple_request_datasheet_record_get_datasheet
(
rec
);
g_return_if_fail
(
sheet
!=
NULL
);
col_count
=
purple_request_datasheet_get_column_count
(
sheet
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
PurpleRequestDatasheetColumnType
type
;
type
=
purple_request_datasheet_get_column_type
(
sheet
,
i
);
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_STRING
)
{
GValue
val
;
val
.
g_type
=
0
;
g_value_init
(
&
val
,
G_TYPE_STRING
);
g_value_set_string
(
&
val
,
purple_request_datasheet_record_get_string_data
(
rec
,
i
));
gtk_list_store_set_value
(
model
,
iter
,
i
+
1
,
&
val
);
}
else
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE
)
{
GdkPixbuf
*
pixbuf
;
pixbuf
=
_pidgin_datasheet_stock_icon_get
(
purple_request_datasheet_record_get_image_data
(
rec
,
i
));
gtk_list_store_set
(
model
,
iter
,
i
+
1
,
pixbuf
,
-1
);
}
else
g_warn_if_reached
();
}
}
static
void
datasheet_fill
(
PurpleRequestDatasheet
*
sheet
,
GtkListStore
*
model
)
{
const
GList
*
it
;
gtk_list_store_clear
(
model
);
it
=
purple_request_datasheet_get_records
(
sheet
);
for
(;
it
!=
NULL
;
it
=
g_list_next
(
it
))
{
PurpleRequestDatasheetRecord
*
rec
=
it
->
data
;
GtkTreeIter
iter
;
gtk_list_store_append
(
model
,
&
iter
);
gtk_list_store_set
(
model
,
&
iter
,
0
,
purple_request_datasheet_record_get_key
(
rec
),
-1
);
datasheet_update_rec
(
rec
,
model
,
&
iter
);
}
datasheet_selection_changed
(
GTK_WIDGET
(
g_object_get_data
(
G_OBJECT
(
model
),
"sheet-widget"
)));
}
static
void
datasheet_update
(
PurpleRequestDatasheet
*
sheet
,
gpointer
key
,
GtkListStore
*
model
)
{
PurpleRequestDatasheetRecord
*
rec
;
GtkTreeIter
iter
;
GtkTreeModel
*
tmodel
=
GTK_TREE_MODEL
(
model
);
gboolean
found
=
FALSE
;
g_return_if_fail
(
tmodel
!=
NULL
);
if
(
key
==
NULL
)
{
datasheet_fill
(
sheet
,
model
);
return
;
}
rec
=
purple_request_datasheet_record_find
(
sheet
,
key
);
if
(
gtk_tree_model_get_iter_first
(
tmodel
,
&
iter
))
{
do
{
gpointer
ikey
;
gtk_tree_model_get
(
tmodel
,
&
iter
,
0
,
&
ikey
,
-1
);
if
(
key
==
ikey
)
{
found
=
TRUE
;
break
;
}
}
while
(
gtk_tree_model_iter_next
(
tmodel
,
&
iter
));
}
if
(
rec
==
NULL
&&
!
found
)
return
;
if
(
rec
==
NULL
)
{
gtk_list_store_remove
(
model
,
&
iter
);
return
;
}
if
(
!
found
)
{
gtk_list_store_append
(
model
,
&
iter
);
gtk_list_store_set
(
model
,
&
iter
,
0
,
key
,
-1
);
}
datasheet_update_rec
(
rec
,
model
,
&
iter
);
datasheet_selection_changed
(
GTK_WIDGET
(
g_object_get_data
(
G_OBJECT
(
model
),
"sheet-widget"
)));
}
static
void
datasheet_selection_changed_cb
(
G_GNUC_UNUSED
GtkTreeSelection
*
sel
,
gpointer
sheet_widget
)
{
datasheet_selection_changed
(
GTK_WIDGET
(
sheet_widget
));
}
static
void
datasheet_action_clicked
(
GtkButton
*
btn
,
PurpleRequestDatasheetAction
*
act
)
{
GtkWidget
*
sheet_widget
;
sheet_widget
=
g_object_get_data
(
G_OBJECT
(
btn
),
"sheet-widget"
);
g_return_if_fail
(
sheet_widget
!=
NULL
);
purple_request_datasheet_action_call
(
act
,
datasheet_get_selected_row
(
sheet_widget
));
}
static
GtkWidget
*
create_datasheet_field
(
PurpleRequestField
*
field
,
GtkSizeGroup
*
buttons_sg
)
{
PurpleRequestFieldDatasheet
*
dfield
=
PURPLE_REQUEST_FIELD_DATASHEET
(
field
);
PurpleRequestDatasheet
*
sheet
;
guint
i
,
col_count
;
GType
*
col_types
;
GtkListStore
*
model
;
GtkTreeView
*
view
;
GtkTreeSelection
*
sel
;
GtkWidget
*
scrollable
;
GtkCellRenderer
*
renderer_image
=
NULL
,
*
renderer_text
=
NULL
;
GtkTreeViewColumn
*
id_column
;
GtkWidget
*
main_box
;
GtkWidget
*
buttons_box
;
const
GList
*
it
;
sheet
=
purple_request_field_datasheet_get_sheet
(
dfield
);
main_box
=
gtk_box_new
(
GTK_ORIENTATION_HORIZONTAL
,
0
);
col_count
=
purple_request_datasheet_get_column_count
(
sheet
);
col_types
=
g_new0
(
GType
,
col_count
+
1
);
col_types
[
0
]
=
G_TYPE_POINTER
;
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
PurpleRequestDatasheetColumnType
type
;
type
=
purple_request_datasheet_get_column_type
(
sheet
,
i
);
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_STRING
)
col_types
[
i
+
1
]
=
G_TYPE_STRING
;
else
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE
)
col_types
[
i
+
1
]
=
GDK_TYPE_PIXBUF
;
else
g_warn_if_reached
();
}
model
=
gtk_list_store_newv
(
col_count
+
1
,
col_types
);
g_free
(
col_types
);
view
=
GTK_TREE_VIEW
(
gtk_tree_view_new_with_model
(
GTK_TREE_MODEL
(
model
)));
g_object_set_data
(
G_OBJECT
(
model
),
"sheet-widget"
,
main_box
);
g_object_unref
(
G_OBJECT
(
model
));
id_column
=
gtk_tree_view_column_new
();
gtk_tree_view_column_set_visible
(
id_column
,
FALSE
);
gtk_tree_view_append_column
(
view
,
id_column
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
PurpleRequestDatasheetColumnType
type
;
const
gchar
*
title
;
GtkCellRenderer
*
renderer
=
NULL
;
const
gchar
*
type_str
=
""
;
type
=
purple_request_datasheet_get_column_type
(
sheet
,
i
);
title
=
purple_request_datasheet_get_column_title
(
sheet
,
i
);
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_STRING
)
{
type_str
=
"text"
;
if
(
!
renderer_text
)
renderer_text
=
gtk_cell_renderer_text_new
();
renderer
=
renderer_text
;
}
else
if
(
type
==
PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE
)
{
type_str
=
"pixbuf"
;
if
(
!
renderer_image
)
renderer_image
=
gtk_cell_renderer_pixbuf_new
();
renderer
=
renderer_image
;
}
else
g_warn_if_reached
();
if
(
title
==
NULL
)
title
=
""
;
gtk_tree_view_insert_column_with_attributes
(
view
,
-1
,
title
,
renderer
,
type_str
,
i
+
1
,
NULL
);
}
gtk_widget_set_size_request
(
GTK_WIDGET
(
view
),
400
,
250
);
scrollable
=
gtk_scrolled_window_new
();
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
scrollable
),
GTK_POLICY_AUTOMATIC
,
GTK_POLICY_ALWAYS
);
gtk_scrolled_window_set_child
(
GTK_SCROLLED_WINDOW
(
scrollable
),
GTK_WIDGET
(
view
));
gtk_widget_set_hexpand
(
scrollable
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
main_box
),
scrollable
);
buttons_box
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
12
);
gtk_size_group_add_widget
(
buttons_sg
,
buttons_box
);
gtk_box_append
(
GTK_BOX
(
main_box
),
buttons_box
);
it
=
purple_request_datasheet_get_actions
(
sheet
);
for
(;
it
!=
NULL
;
it
=
g_list_next
(
it
))
{
PurpleRequestDatasheetAction
*
act
=
it
->
data
;
GtkButton
*
btn
;
const
gchar
*
label
;
label
=
purple_request_datasheet_action_get_label
(
act
);
btn
=
GTK_BUTTON
(
gtk_button_new_with_label
(
label
?
label
:
""
));
g_object_set_data
(
G_OBJECT
(
btn
),
"action"
,
act
);
g_object_set_data
(
G_OBJECT
(
btn
),
"sheet-widget"
,
main_box
);
g_signal_connect
(
G_OBJECT
(
btn
),
"clicked"
,
G_CALLBACK
(
datasheet_action_clicked
),
act
);
gtk_box_append
(
GTK_BOX
(
buttons_box
),
GTK_WIDGET
(
btn
));
}
g_object_set_data
(
G_OBJECT
(
main_box
),
"view"
,
view
);
g_object_set_data
(
G_OBJECT
(
main_box
),
"buttons"
,
buttons_box
);
g_object_set_data
(
G_OBJECT
(
main_box
),
"sheet"
,
sheet
);
datasheet_fill
(
sheet
,
model
);
purple_signal_connect
(
sheet
,
"record-changed"
,
pidgin_request_get_handle
(),
G_CALLBACK
(
datasheet_update
),
model
);
sel
=
gtk_tree_view_get_selection
(
view
);
g_signal_connect
(
G_OBJECT
(
sel
),
"changed"
,
G_CALLBACK
(
datasheet_selection_changed_cb
),
main_box
);
return
main_box
;
}
static
void
*
pidgin_request_fields
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
PurpleRequestPage
*
page
,
const
char
*
ok_text
,
GCallback
ok_cb
,
const
char
*
cancel_text
,
GCallback
cancel_cb
,
PurpleRequestCommonParameters
*
cpar
,
void
*
user_data
)
{
PidginRequestData
*
data
;
GtkWidget
*
win
;
GtkWidget
*
page_widget
=
NULL
;
AdwPreferencesGroup
*
group_widget
=
NULL
;
GtkWidget
*
vbox
=
NULL
;
GtkWidget
*
img
;
GtkWidget
*
content
;
GtkSizeGroup
*
datasheet_buttons_sg
;
GSList
*
extra_actions
;
gint
response
;
guint
n_groups
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_FIELDS
;
data
->
user_data
=
user_data
;
data
->
u
.
multifield
.
page
=
page
;
data
->
dialog
=
win
=
gtk_dialog_new
();
if
(
title
!=
NULL
)
{
gtk_window_set_title
(
GTK_WINDOW
(
win
),
title
);
}
else
{
gtk_window_set_title
(
GTK_WINDOW
(
win
),
PIDGIN_ALERT_TITLE
);
}
gtk_window_set_resizable
(
GTK_WINDOW
(
win
),
TRUE
);
gtk_window_set_default_size
(
GTK_WINDOW
(
win
),
480
,
-1
);
content
=
gtk_dialog_get_content_area
(
GTK_DIALOG
(
win
));
page_widget
=
adw_preferences_page_new
();
gtk_widget_set_vexpand
(
page_widget
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
content
),
page_widget
);
/* Setup the general info box. */
group_widget
=
ADW_PREFERENCES_GROUP
(
adw_preferences_group_new
());
adw_preferences_page_add
(
ADW_PREFERENCES_PAGE
(
page_widget
),
group_widget
);
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
6
);
gtk_widget_set_margin_bottom
(
vbox
,
12
);
adw_preferences_group_add
(
group_widget
,
vbox
);
/* Dialog icon. */
img
=
pidgin_request_dialog_icon
(
PURPLE_REQUEST_FIELDS
,
cpar
);
if
(
gtk_image_get_storage_type
(
GTK_IMAGE
(
img
))
==
GTK_IMAGE_ICON_NAME
)
{
gtk_image_set_pixel_size
(
GTK_IMAGE
(
img
),
64
);
}
gtk_box_append
(
GTK_BOX
(
vbox
),
img
);
pidgin_widget_decorate_account
(
vbox
,
purple_request_cpar_get_account
(
cpar
));
pidgin_request_add_help
(
GTK_DIALOG
(
win
),
cpar
);
/* Add responses and callbacks. */
g_signal_connect
(
data
->
dialog
,
"response"
,
G_CALLBACK
(
multifield_response_cb
),
data
);
extra_actions
=
purple_request_cpar_get_extra_actions
(
cpar
);
data
->
cb_count
=
2
+
g_slist_length
(
extra_actions
);
data
->
cbs
=
g_new0
(
GCallback
,
data
->
cb_count
);
data
->
cbs
[
0
]
=
ok_cb
;
data
->
cbs
[
1
]
=
cancel_cb
;
response
=
2
;
/* So that response == data->cbs index. */
for
(;
extra_actions
!=
NULL
;
extra_actions
=
extra_actions
->
next
)
{
PurpleKeyValuePair
*
extra_action
=
extra_actions
->
data
;
gtk_dialog_add_button
(
GTK_DIALOG
(
win
),
extra_action
->
key
,
response
);
data
->
cbs
[
response
]
=
extra_action
->
value
;
response
++
;
}
/* Cancel button */
gtk_dialog_add_button
(
GTK_DIALOG
(
win
),
cancel_text
,
GTK_RESPONSE_CANCEL
);
response
=
GTK_RESPONSE_CANCEL
;
/* OK button */
if
(
ok_text
!=
NULL
)
{
data
->
ok_button
=
gtk_dialog_add_button
(
GTK_DIALOG
(
win
),
ok_text
,
GTK_RESPONSE_OK
);
response
=
GTK_RESPONSE_OK
;
}
gtk_dialog_set_default_response
(
GTK_DIALOG
(
win
),
response
);
datasheet_buttons_sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
if
(
primary
!=
NULL
)
{
GtkWidget
*
label
=
gtk_label_new
(
primary
);
gtk_label_set_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_widget_add_css_class
(
label
,
"title-1"
);
adw_preferences_group_add
(
group_widget
,
label
);
}
if
(
secondary
!=
NULL
)
{
GtkWidget
*
label
=
gtk_label_new
(
secondary
);
gtk_label_set_wrap
(
GTK_LABEL
(
label
),
TRUE
);
adw_preferences_group_add
(
group_widget
,
label
);
}
n_groups
=
g_list_model_get_n_items
(
G_LIST_MODEL
(
page
));
for
(
guint
group_index
=
0
;
group_index
<
n_groups
;
group_index
++
)
{
PurpleRequestGroup
*
group
=
NULL
;
guint
n_fields
=
0
;
GSList
*
username_widgets
=
NULL
;
GtkWidget
*
account_hint
=
NULL
;
group
=
g_list_model_get_item
(
G_LIST_MODEL
(
page
),
group_index
);
group_widget
=
ADW_PREFERENCES_GROUP
(
adw_preferences_group_new
());
adw_preferences_group_set_title
(
group_widget
,
purple_request_group_get_title
(
group
));
adw_preferences_page_add
(
ADW_PREFERENCES_PAGE
(
page_widget
),
group_widget
);
n_fields
=
g_list_model_get_n_items
(
G_LIST_MODEL
(
group
));
for
(
guint
field_index
=
0
;
field_index
<
n_fields
;
field_index
++
)
{
PurpleRequestField
*
field
=
NULL
;
GtkWidget
*
widget
=
NULL
;
gboolean
was_handled_by_create
=
FALSE
;
field
=
g_list_model_get_item
(
G_LIST_MODEL
(
group
),
field_index
);
if
(
!
purple_request_field_is_visible
(
field
))
{
g_object_unref
(
field
);
continue
;
}
if
(
PURPLE_IS_REQUEST_FIELD_LABEL
(
field
))
{
widget
=
create_label_field
();
was_handled_by_create
=
TRUE
;
}
else
if
(
PURPLE_IS_REQUEST_FIELD_STRING
(
field
))
{
PurpleKeyValuePair
*
username_hint
=
NULL
;
widget
=
create_string_field
(
field
,
&
username_hint
);
was_handled_by_create
=
TRUE
;
if
(
username_hint
!=
NULL
)
{
username_widgets
=
g_slist_prepend
(
username_widgets
,
username_hint
);
}
}
else
if
(
PURPLE_IS_REQUEST_FIELD_INT
(
field
))
{
PurpleKeyValuePair
*
username_hint
=
NULL
;
widget
=
create_int_field
(
field
,
&
username_hint
);
was_handled_by_create
=
TRUE
;
if
(
username_hint
!=
NULL
)
{
username_widgets
=
g_slist_prepend
(
username_widgets
,
username_hint
);
}
}
else
if
(
PURPLE_IS_REQUEST_FIELD_BOOL
(
field
))
{
widget
=
create_bool_field
(
field
);
was_handled_by_create
=
TRUE
;
}
else
if
(
PURPLE_IS_REQUEST_FIELD_CHOICE
(
field
))
{
widget
=
create_choice_field
(
field
);
}
else
if
(
PURPLE_IS_REQUEST_FIELD_LIST
(
field
))
{
widget
=
create_list_field
(
field
);
}
else
if
(
PURPLE_IS_REQUEST_FIELD_IMAGE
(
field
))
{
widget
=
create_image_field
(
field
);
}
else
if
(
PURPLE_IS_REQUEST_FIELD_ACCOUNT
(
field
))
{
widget
=
create_account_field
(
field
,
&
account_hint
);
}
else
if
(
PURPLE_IS_REQUEST_FIELD_DATASHEET
(
field
))
{
widget
=
create_datasheet_field
(
field
,
datasheet_buttons_sg
);
}
else
{
g_warning
(
"Unhandled field type: %s"
,
G_OBJECT_TYPE_NAME
(
field
));
g_object_unref
(
field
);
continue
;
}
g_object_bind_property
(
field
,
"tooltip"
,
widget
,
"tooltip-text"
,
G_BINDING_DEFAULT
|
G_BINDING_SYNC_CREATE
);
g_object_bind_property
(
field
,
"sensitive"
,
widget
,
"sensitive"
,
G_BINDING_DEFAULT
|
G_BINDING_SYNC_CREATE
);
if
(
!
was_handled_by_create
)
{
GtkWidget
*
row
=
NULL
;
gtk_widget_set_valign
(
widget
,
GTK_ALIGN_CENTER
);
row
=
adw_action_row_new
();
adw_action_row_add_suffix
(
ADW_ACTION_ROW
(
row
),
widget
);
widget
=
row
;
}
g_object_bind_property
(
field
,
"label"
,
widget
,
"title"
,
G_BINDING_DEFAULT
|
G_BINDING_SYNC_CREATE
);
adw_preferences_row_set_use_underline
(
ADW_PREFERENCES_ROW
(
widget
),
TRUE
);
adw_preferences_group_add
(
group_widget
,
widget
);
g_object_unref
(
field
);
}
/* Link autocompletion of entry widgets to account if we found any. */
if
(
username_widgets
!=
NULL
&&
account_hint
!=
NULL
)
{
while
(
username_widgets
!=
NULL
)
{
PurpleKeyValuePair
*
pair
=
username_widgets
->
data
;
const
char
*
type_hint
=
pair
->
key
;
GtkWidget
*
entry
=
pair
->
value
;
gboolean
show_all
;
show_all
=
purple_strequal
(
type_hint
,
"screenname-all"
);
pidgin_setup_screenname_autocomplete
(
entry
,
account_hint
,
pidgin_screenname_autocomplete_default_filter
,
GINT_TO_POINTER
(
show_all
));
purple_key_value_pair_free
(
pair
);
username_widgets
=
g_slist_delete_link
(
username_widgets
,
username_widgets
);
}
}
else
{
g_slist_free_full
(
username_widgets
,
(
GDestroyNotify
)
purple_key_value_pair_free
);
}
g_object_unref
(
group
);
}
g_object_unref
(
datasheet_buttons_sg
);
g_object_bind_property
(
page
,
"valid"
,
data
->
ok_button
,
"sensitive"
,
0
);
pidgin_auto_parent_window
(
win
);
gtk_widget_set_visible
(
win
,
TRUE
);
return
data
;
}
static
void
pidgin_request_file_folder_response_cb
(
G_GNUC_UNUSED
GtkWidget
*
widget
,
gint
response
,
PidginRequestData
*
data
)
{
GFile
*
current_path
;
if
(
response
!=
GTK_RESPONSE_ACCEPT
)
{
if
(
data
->
cbs
[
0
]
!=
NULL
)
((
PurpleRequestFileCb
)
data
->
cbs
[
0
])(
data
->
user_data
,
NULL
);
purple_request_close
(
data
->
type
,
data
);
return
;
}
current_path
=
gtk_file_chooser_get_current_folder
(
GTK_FILE_CHOOSER
(
data
->
dialog
));
if
(
current_path
!=
NULL
)
{
gchar
*
current_folder
=
g_file_get_path
(
current_path
);
if
(
data
->
u
.
file
.
savedialog
)
{
purple_prefs_set_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_save_folder"
,
current_folder
);
}
else
{
purple_prefs_set_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_open_folder"
,
current_folder
);
}
g_free
(
current_folder
);
}
if
(
data
->
cbs
[
1
]
!=
NULL
)
{
GFile
*
file
=
gtk_file_chooser_get_file
(
GTK_FILE_CHOOSER
(
data
->
dialog
));
char
*
filename
=
g_file_get_path
(
file
);
((
PurpleRequestFileCb
)
data
->
cbs
[
1
])(
data
->
user_data
,
filename
);
g_free
(
filename
);
g_object_unref
(
file
);
}
purple_request_close
(
data
->
type
,
data
);
g_clear_object
(
&
current_path
);
}
static
void
*
pidgin_request_file
(
const
char
*
title
,
const
char
*
filename
,
gboolean
savedialog
,
GCallback
ok_cb
,
GCallback
cancel_cb
,
G_GNUC_UNUSED
PurpleRequestCommonParameters
*
cpar
,
gpointer
user_data
)
{
PidginRequestData
*
data
;
GtkFileChooserNative
*
filesel
;
#ifdef _WIN32
GFile
*
file
=
NULL
;
const
gchar
*
current_folder
;
gboolean
folder_set
=
FALSE
;
#endif
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_FILE
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
2
;
data
->
cbs
=
g_new0
(
GCallback
,
2
);
data
->
cbs
[
0
]
=
cancel_cb
;
data
->
cbs
[
1
]
=
ok_cb
;
data
->
u
.
file
.
savedialog
=
savedialog
;
filesel
=
gtk_file_chooser_native_new
(
title
?
title
:
(
savedialog
?
_
(
"Save File..."
)
:
_
(
"Open File..."
)),
NULL
,
savedialog
?
GTK_FILE_CHOOSER_ACTION_SAVE
:
GTK_FILE_CHOOSER_ACTION_OPEN
,
savedialog
?
_
(
"_Save"
)
:
_
(
"_Open"
),
_
(
"_Cancel"
));
if
((
filename
!=
NULL
)
&&
(
*
filename
!=
'\0'
))
{
GFile
*
path
=
g_file_new_for_path
(
filename
);
if
(
savedialog
)
{
gtk_file_chooser_set_file
(
GTK_FILE_CHOOSER
(
filesel
),
path
,
NULL
);
}
else
if
(
g_file_test
(
filename
,
G_FILE_TEST_EXISTS
))
{
gtk_file_chooser_set_file
(
GTK_FILE_CHOOSER
(
filesel
),
path
,
NULL
);
}
g_object_unref
(
path
);
}
#ifdef _WIN32
if
(
savedialog
)
{
current_folder
=
purple_prefs_get_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_save_folder"
);
}
else
{
current_folder
=
purple_prefs_get_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_open_folder"
);
}
if
((
filename
==
NULL
||
*
filename
==
'\0'
||
!
g_file_test
(
filename
,
G_FILE_TEST_EXISTS
))
&&
(
current_folder
!=
NULL
)
&&
(
*
current_folder
!=
'\0'
))
{
file
=
g_file_new_for_path
(
current_folder
);
folder_set
=
gtk_file_chooser_set_current_folder
(
GTK_FILE_CHOOSER
(
filesel
),
file
,
NULL
);
}
if
(
!
folder_set
&&
(
filename
==
NULL
||
*
filename
==
'\0'
||
!
g_file_test
(
filename
,
G_FILE_TEST_EXISTS
)))
{
char
*
my_documents
=
wpurple_get_special_folder
(
CSIDL_PERSONAL
);
g_clear_object
(
&
file
);
if
(
my_documents
!=
NULL
)
{
file
=
g_file_new_for_path
(
my_documents
);
gtk_file_chooser_set_current_folder
(
GTK_FILE_CHOOSER
(
filesel
),
file
,
NULL
);
g_free
(
my_documents
);
}
}
g_clear_object
(
&
file
);
#endif
g_signal_connect
(
G_OBJECT
(
filesel
),
"response"
,
G_CALLBACK
(
pidgin_request_file_folder_response_cb
),
data
);
#if 0
/* FIXME: Not implemented for native dialogs. */
pidgin_auto_parent_window(filesel);
#endif
data
->
dialog
=
filesel
;
gtk_native_dialog_show
(
GTK_NATIVE_DIALOG
(
filesel
));
return
(
void
*
)
data
;
}
static
void
*
pidgin_request_folder
(
const
char
*
title
,
const
char
*
dirname
,
GCallback
ok_cb
,
GCallback
cancel_cb
,
G_GNUC_UNUSED
PurpleRequestCommonParameters
*
cpar
,
gpointer
user_data
)
{
PidginRequestData
*
data
;
GtkFileChooserNative
*
dirsel
;
data
=
g_new0
(
PidginRequestData
,
1
);
data
->
type
=
PURPLE_REQUEST_FOLDER
;
data
->
user_data
=
user_data
;
data
->
cb_count
=
2
;
data
->
cbs
=
g_new0
(
GCallback
,
2
);
data
->
cbs
[
0
]
=
cancel_cb
;
data
->
cbs
[
1
]
=
ok_cb
;
data
->
u
.
file
.
savedialog
=
FALSE
;
dirsel
=
gtk_file_chooser_native_new
(
title
?
title
:
_
(
"Select Folder..."
),
NULL
,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
,
_
(
"_OK"
),
_
(
"_Cancel"
));
if
((
dirname
!=
NULL
)
&&
(
*
dirname
!=
'\0'
))
{
GFile
*
path
=
g_file_new_for_path
(
dirname
);
gtk_file_chooser_set_current_folder
(
GTK_FILE_CHOOSER
(
dirsel
),
path
,
NULL
);
g_object_unref
(
path
);
}
g_signal_connect
(
G_OBJECT
(
dirsel
),
"response"
,
G_CALLBACK
(
pidgin_request_file_folder_response_cb
),
data
);
data
->
dialog
=
dirsel
;
#if 0
/* FIXME: Not implemented for native dialogs. */
pidgin_auto_parent_window(dirsel);
#endif
gtk_native_dialog_show
(
GTK_NATIVE_DIALOG
(
dirsel
));
return
(
void
*
)
data
;
}
/* if request callback issues another request, it should be attached to the
* primary request parent */
static
void
pidgin_window_detach_children
(
GtkWindow
*
win
)
{
GList
*
it
;
GtkWindow
*
par
;
g_return_if_fail
(
win
!=
NULL
);
par
=
gtk_window_get_transient_for
(
win
);
it
=
gtk_window_list_toplevels
();
for
(
it
=
g_list_first
(
it
);
it
!=
NULL
;
it
=
g_list_delete_link
(
it
,
it
))
{
GtkWindow
*
child
=
GTK_WINDOW
(
it
->
data
);
if
(
gtk_window_get_transient_for
(
child
)
!=
win
)
continue
;
if
(
gtk_window_get_destroy_with_parent
(
child
))
{
#ifdef _WIN32
/* XXX test/verify it: Win32 gtk ignores
* gtk_window_set_destroy_with_parent(..., FALSE). */
gtk_window_set_transient_for
(
child
,
NULL
);
#endif
continue
;
}
gtk_window_set_transient_for
(
child
,
par
);
}
}
static
void
pidgin_close_request
(
PurpleRequestType
type
,
void
*
ui_handle
)
{
PidginRequestData
*
data
=
(
PidginRequestData
*
)
ui_handle
;
g_free
(
data
->
cbs
);
if
(
type
==
PURPLE_REQUEST_FILE
||
type
==
PURPLE_REQUEST_FOLDER
)
{
/* Will be a GtkNativeDialog, not GtkDialog. */
g_object_unref
(
data
->
dialog
);
}
else
{
pidgin_window_detach_children
(
GTK_WINDOW
(
data
->
dialog
));
gtk_window_destroy
(
GTK_WINDOW
(
data
->
dialog
));
}
if
(
type
==
PURPLE_REQUEST_FIELDS
)
{
g_clear_object
(
&
data
->
u
.
multifield
.
page
);
}
g_free
(
data
);
}
GtkWindow
*
pidgin_request_get_dialog_window
(
void
*
ui_handle
)
{
PidginRequestData
*
data
=
ui_handle
;
g_return_val_if_fail
(
purple_request_is_valid_ui_handle
(
data
,
NULL
),
NULL
);
if
(
data
->
type
==
PURPLE_REQUEST_FILE
||
data
->
type
==
PURPLE_REQUEST_FOLDER
)
{
/* Not a GtkWidget, but a GtkFileChooserNative. Eventually this function
* should not be needed, once we don't need to auto-parent. */
return
NULL
;
}
return
GTK_WINDOW
(
data
->
dialog
);
}
static
PurpleRequestUiOps
ops
=
{
.
features
=
PURPLE_REQUEST_FEATURE_HTML
,
.
request_input
=
pidgin_request_input
,
.
request_choice
=
pidgin_request_choice
,
.
request_action
=
pidgin_request_action
,
.
request_wait
=
pidgin_request_wait
,
.
request_wait_update
=
pidgin_request_wait_update
,
.
request_fields
=
pidgin_request_fields
,
.
request_file
=
pidgin_request_file
,
.
request_folder
=
pidgin_request_folder
,
.
close_request
=
pidgin_close_request
,
};
PurpleRequestUiOps
*
pidgin_request_get_ui_ops
(
void
)
{
return
&
ops
;
}
void
*
pidgin_request_get_handle
(
void
)
{
static
int
handle
;
return
&
handle
;
}
static
void
pidgin_request_datasheet_stock_remove
(
gpointer
obj
)
{
if
(
obj
==
NULL
)
return
;
g_object_unref
(
obj
);
}
void
pidgin_request_init
(
void
)
{
datasheet_stock
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
pidgin_request_datasheet_stock_remove
);
}
void
pidgin_request_uninit
(
void
)
{
purple_signals_disconnect_by_handle
(
pidgin_request_get_handle
());
g_clear_pointer
(
&
datasheet_stock
,
g_hash_table_destroy
);
}