diff options
author | Günther Wagner <info@gunibert.de> | 2022-02-01 22:05:39 +0100 |
---|---|---|
committer | Günther Wagner <info@gunibert.de> | 2022-02-01 22:18:07 +0100 |
commit | 110734aaee4675535f4f28bd8fc60d7aba2e5645 (patch) | |
tree | 1c2326c9487198b85ab5d83e82c1a21322943621 | |
parent | 51b5ed4003c8d074e6edbbd946524d30e1b84ad5 (diff) | |
download | librest-110734aaee4675535f4f28bd8fc60d7aba2e5645.tar.gz |
demo: add authentication methods
-rw-r--r-- | examples/demo/demo-main.c | 2 | ||||
-rw-r--r-- | examples/demo/demo-rest-page.c | 437 | ||||
-rw-r--r-- | examples/demo/demo-rest-page.ui | 333 | ||||
-rw-r--r-- | examples/demo/demo-table-row.c | 149 | ||||
-rw-r--r-- | examples/demo/demo-table-row.h | 35 | ||||
-rw-r--r-- | examples/demo/demo-table-row.ui | 36 | ||||
-rw-r--r-- | examples/demo/demo-table.c | 210 | ||||
-rw-r--r-- | examples/demo/demo-table.h | 34 | ||||
-rw-r--r-- | examples/demo/demo-window.c | 10 | ||||
-rw-r--r-- | examples/demo/meson.build | 2 | ||||
-rw-r--r-- | examples/demo/org.gnome.RestDemo.gresource.xml | 2 | ||||
-rw-r--r-- | examples/demo/org.gnome.RestDemo.json | 14 | ||||
-rw-r--r-- | examples/demo/style.css | 3 |
13 files changed, 1255 insertions, 12 deletions
diff --git a/examples/demo/demo-main.c b/examples/demo/demo-main.c index 8f12404..f4f6029 100644 --- a/examples/demo/demo-main.c +++ b/examples/demo/demo-main.c @@ -23,6 +23,7 @@ #include <adwaita.h> #include <gtksourceview/gtksource.h> #include "demo-window.h" +#include "demo-table.h" static void on_activate (GApplication *app, gpointer user_data) @@ -76,6 +77,7 @@ main (gint argc, { adw_init (); gtk_source_init (); + g_type_ensure (DEMO_TYPE_TABLE); g_set_prgname ("librest-demo"); g_set_application_name ("librest-demo"); diff --git a/examples/demo/demo-rest-page.c b/examples/demo/demo-rest-page.c index 05290c0..46ebfec 100644 --- a/examples/demo/demo-rest-page.c +++ b/examples/demo/demo-rest-page.c @@ -20,8 +20,11 @@ #include "demo-rest-page.h" #include <rest/rest.h> +#include <libsoup/soup.h> #include <gtksourceview/gtksource.h> #include <json-glib/json-glib.h> +#include "demo-table.h" +#include "demo-table-row.h" struct _DemoRestPage { @@ -33,8 +36,45 @@ struct _DemoRestPage GtkWidget *sourceview; GtkWidget *body; GtkWidget *notebook; + GtkWidget *authentication; + GtkWidget *authmethods; + GtkWidget *authentication_stack; + GtkWidget *headers; + GtkWidget *headerslb; + GtkWidget *params; + GtkWidget *paramslb; + + /* basic auth */ + GtkWidget *basic_username; + GtkWidget *basic_password; + + /* digest auth */ + GtkWidget *digest_username; + GtkWidget *digest_password; + + /* oauth 1 auth */ + GtkWidget *oauth1_client_identifier; + GtkWidget *oauth1_client_secret; + RestProxy *oauth1_proxy; + + /* oauth 2 auth */ + GtkWidget *oauth2_client_identifier; + GtkWidget *oauth2_client_secret; + GtkWidget *oauth2_auth_url; + GtkWidget *oauth2_token_url; + GtkWidget *oauth2_redirect_url; + RestProxy *oauth2_proxy; + RestPkceCodeChallenge *pkce; }; +typedef enum { + AUTHMODE_NO, + AUTHMODE_BASIC, + AUTHMODE_DIGEST, + AUTHMODE_OAUTH1, + AUTHMODE_OAUTH2 +} AuthMode; + G_DEFINE_FINAL_TYPE (DemoRestPage, demo_rest_page, GTK_TYPE_BOX) DemoRestPage * @@ -48,9 +88,35 @@ demo_rest_page_finalize (GObject *object) { DemoRestPage *self = (DemoRestPage *)object; + g_clear_object (&self->oauth1_proxy); + g_clear_object (&self->oauth2_proxy); + g_clear_pointer (&self->pkce, rest_pkce_code_challenge_free); + G_OBJECT_CLASS (demo_rest_page_parent_class)->finalize (object); } +static AuthMode +get_current_auth_mode (DemoRestPage *self) +{ + const gchar *stack_name; + + g_return_val_if_fail (DEMO_IS_REST_PAGE (self), 0); + + stack_name = gtk_stack_get_visible_child_name (GTK_STACK (self->authentication_stack)); + if (g_strcmp0 (stack_name, "no_auth") == 0) + return AUTHMODE_NO; + else if (g_strcmp0 (stack_name, "basic") == 0) + return AUTHMODE_BASIC; + else if (g_strcmp0 (stack_name, "digest") == 0) + return AUTHMODE_DIGEST; + else if (g_strcmp0 (stack_name, "oauth1") == 0) + return AUTHMODE_OAUTH1; + else if (g_strcmp0 (stack_name, "oauth2") == 0) + return AUTHMODE_OAUTH2; + + return AUTHMODE_NO; +} + static void populate_http_method (DemoRestPage *self) { @@ -82,6 +148,265 @@ set_json_response (DemoRestPage *self, GtkSourceLanguageManager *manager = gtk_source_language_manager_get_default (); GtkSourceLanguage *lang = gtk_source_language_manager_guess_language (manager, NULL, "application/json"); gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), lang); + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), 0); +} + +static void +set_text_response (DemoRestPage *self, + RestProxyCall *call) +{ + g_autoptr(GHashTable) response_headers = NULL; + + const gchar *payload = rest_proxy_call_get_payload (call); + goffset payload_length = rest_proxy_call_get_payload_length (call); + response_headers = rest_proxy_call_get_response_headers (call); + + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->sourceview)); + gtk_text_buffer_set_text (buffer, payload, payload_length); + + GtkSourceLanguageManager *manager = gtk_source_language_manager_get_default (); + GtkSourceLanguage *lang = gtk_source_language_manager_guess_language (manager, NULL, g_hash_table_lookup (response_headers, "Content-Type")); + gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), lang); + gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), 0); +} + +static void +demo_rest_page_fetched_oauth1_access_token (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + RestProxy *proxy = (RestProxy *)object; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + oauth_proxy_access_token_finish (OAUTH_PROXY (proxy), result, &error); +} + +static void +demo_rest_page_fetched_oauth2_access_token (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + RestProxy *proxy = (RestProxy *)object; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + rest_oauth2_proxy_fetch_access_token_finish (REST_OAUTH2_PROXY (proxy), result, &error); +} + +static void +oauth1_dialog_response (GtkDialog *dialog, + gint response_id, + DemoRestPage *self) +{ + switch (response_id) + { + case GTK_RESPONSE_OK: + { + const gchar *verifier = NULL; + GtkWidget *content_area = gtk_dialog_get_content_area (dialog); + GtkWidget *box = gtk_widget_get_first_child (content_area); + GtkWidget *entry = gtk_widget_get_last_child (box); + + verifier = gtk_editable_get_text (GTK_EDITABLE (entry)); + oauth_proxy_access_token_async (OAUTH_PROXY (self->oauth1_proxy), + "access_token", + verifier, + NULL, + demo_rest_page_fetched_oauth1_access_token, + self); + break; + } + case GTK_RESPONSE_CANCEL: + g_clear_object (&self->oauth1_proxy); + break; + } +} + +static void +oauth2_dialog_response (GtkDialog *dialog, + gint response_id, + DemoRestPage *self) +{ + switch (response_id) + { + case GTK_RESPONSE_OK: + { + const gchar *code = NULL; + GtkWidget *content_area = gtk_dialog_get_content_area (dialog); + GtkWidget *box = gtk_widget_get_first_child (content_area); + GtkWidget *entry = gtk_widget_get_last_child (box); + + code = gtk_editable_get_text (GTK_EDITABLE (entry)); + rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (self->oauth2_proxy), + code, + rest_pkce_code_challenge_get_verifier (self->pkce), + NULL, + demo_rest_page_fetched_oauth2_access_token, + self); + break; + } + case GTK_RESPONSE_CANCEL: + g_clear_object (&self->oauth2_proxy); + break; + } +} + +static GtkWidget * +demo_rest_page_create_oauth1_dialog (DemoRestPage *self, + RestProxy *proxy) +{ + GtkWidget *dialog = NULL; + GtkWidget *content_area; + GtkWidget *box, *lbl, *token_lbl, *verifier_entry; + g_autofree char *token_str = NULL; + + dialog = gtk_dialog_new_with_buttons ("Get Verifier...", + GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, + "Ok", + GTK_RESPONSE_OK, + "Cancel", + GTK_RESPONSE_CANCEL, + NULL); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_widget_set_margin_top (content_area, 6); + gtk_widget_set_margin_start (content_area, 6); + gtk_widget_set_margin_bottom (content_area, 6); + gtk_widget_set_margin_end (content_area, 6); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + lbl = gtk_label_new ("Open a browser and authorize this application..."); + gtk_box_append (GTK_BOX (box), lbl); + token_str = g_strdup_printf ("Use this token: %s", oauth_proxy_get_token (OAUTH_PROXY (proxy))); + token_lbl = gtk_label_new (token_str); + gtk_label_set_selectable (GTK_LABEL (token_lbl), TRUE); + gtk_box_append (GTK_BOX (box), token_lbl); + verifier_entry = gtk_entry_new (); + gtk_box_append (GTK_BOX (box), verifier_entry); + + gtk_box_append (GTK_BOX (content_area), box); + + g_signal_connect (dialog, "response", G_CALLBACK (oauth1_dialog_response), self); + g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_window_destroy), dialog); + + return dialog; +} + +static GtkWidget * +demo_rest_page_create_oauth2_dialog (DemoRestPage *self, + RestProxy *proxy) +{ + GtkWidget *dialog = NULL; + GtkWidget *content_area; + GtkWidget *box, *lbl, *token_lbl, *verifier_entry; + g_autofree char *token_str = NULL; + + dialog = gtk_dialog_new_with_buttons ("Get Code...", + GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, + "Ok", + GTK_RESPONSE_OK, + "Cancel", + GTK_RESPONSE_CANCEL, + NULL); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_widget_set_margin_top (content_area, 6); + gtk_widget_set_margin_start (content_area, 6); + gtk_widget_set_margin_bottom (content_area, 6); + gtk_widget_set_margin_end (content_area, 6); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + lbl = gtk_label_new ("Open a browser and authorize this application:"); + gtk_box_append (GTK_BOX (box), lbl); + token_str = g_markup_printf_escaped ("Use this <a href=\"%s\">link</a>", rest_oauth2_proxy_build_authorization_url (REST_OAUTH2_PROXY (self->oauth2_proxy), + rest_pkce_code_challenge_get_challenge (self->pkce), + NULL, + NULL)); + token_lbl = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (token_lbl), token_str); + gtk_label_set_selectable (GTK_LABEL (token_lbl), TRUE); + gtk_box_append (GTK_BOX (box), token_lbl); + verifier_entry = gtk_entry_new (); + gtk_box_append (GTK_BOX (box), verifier_entry); + + gtk_box_append (GTK_BOX (content_area), box); + + g_signal_connect (dialog, "response", G_CALLBACK (oauth2_dialog_response), self); + g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_window_destroy), dialog); + + return dialog; +} + +static void +demo_rest_page_fetched_oauth1_request_token (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + DemoRestPage *self = (DemoRestPage *)user_data; + RestProxy *proxy = (RestProxy *)object; + GtkWidget *dialog = NULL; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + + oauth_proxy_request_token_finish (OAUTH_PROXY (proxy), result, &error); + + /* here we show a dialog requesting the user to a browser for authentication */ + /* g_print ("%s\n", oauth_proxy_get_token (proxy)); */ + dialog = demo_rest_page_create_oauth1_dialog (self, proxy); + + gtk_widget_show (dialog); +} + +static void +on_oauth1_get_access_token_clicked (GtkButton *btn, + gpointer user_data) +{ + DemoRestPage *self = (DemoRestPage *)user_data; + const char *url = NULL; + const char *consumer_key = NULL, *consumer_secret = NULL; + const char *function = NULL; + + url = gtk_editable_get_text (GTK_EDITABLE (self->host)); + consumer_key = gtk_editable_get_text (GTK_EDITABLE (self->oauth1_client_identifier)); + consumer_secret = gtk_editable_get_text (GTK_EDITABLE (self->oauth1_client_secret)); + function = gtk_editable_get_text (GTK_EDITABLE (self->function)); + + self->oauth1_proxy = oauth_proxy_new (consumer_key, consumer_secret, url, FALSE); + oauth_proxy_request_token_async (OAUTH_PROXY (self->oauth1_proxy), function, "https://www.gnome.org", NULL, demo_rest_page_fetched_oauth1_request_token, self); +} + +static void +on_oauth2_get_access_token_clicked (GtkButton *btn, + gpointer user_data) +{ + DemoRestPage *self = (DemoRestPage *)user_data; + GtkWidget *dialog = NULL; + const char *url = NULL; + const char *client_id = NULL, *client_secret = NULL; + const char *authurl = NULL, *tokenurl = NULL, *redirecturl = NULL; + + url = gtk_editable_get_text (GTK_EDITABLE (self->host)); + client_id = gtk_editable_get_text (GTK_EDITABLE (self->oauth2_client_identifier)); + client_secret = gtk_editable_get_text (GTK_EDITABLE (self->oauth2_client_secret)); + authurl = gtk_editable_get_text (GTK_EDITABLE (self->oauth2_auth_url)); + tokenurl = gtk_editable_get_text (GTK_EDITABLE (self->oauth2_token_url)); + redirecturl = gtk_editable_get_text (GTK_EDITABLE (self->oauth2_redirect_url)); + + self->oauth2_proxy = REST_PROXY (rest_oauth2_proxy_new (authurl, tokenurl, redirecturl, client_id, client_secret, url)); + self->pkce = rest_pkce_code_challenge_new_random (); + + dialog = demo_rest_page_create_oauth2_dialog (self, self->oauth2_proxy); + + gtk_widget_show (dialog); } static void @@ -103,6 +428,8 @@ call_returned (GObject *object, { if (g_strcmp0 (content_type, "application/json") == 0) set_json_response (self, call); + else + set_text_response (self, call); } } @@ -116,24 +443,101 @@ on_send_clicked (GtkButton *btn, const gchar *function; guint selected_method; GListModel *model; + AuthMode auth_mode; + RestProxy *proxy; g_return_if_fail (DEMO_IS_REST_PAGE (self)); selected_method = gtk_drop_down_get_selected (GTK_DROP_DOWN (self->httpmethod)); model = gtk_drop_down_get_model (GTK_DROP_DOWN (self->httpmethod)); http = gtk_string_list_get_string (GTK_STRING_LIST (model), selected_method); + auth_mode = get_current_auth_mode (self); url = gtk_editable_get_text (GTK_EDITABLE (self->host)); function = gtk_editable_get_text (GTK_EDITABLE (self->function)); - RestProxy *proxy = rest_proxy_new (url, FALSE); + switch (auth_mode) + { + case AUTHMODE_NO: + proxy = rest_proxy_new (url, FALSE); + break; + // TODO: provide a direct auth possibility in librest + case AUTHMODE_BASIC: + { + const gchar *username, *password; + + username = gtk_editable_get_text (GTK_EDITABLE (self->basic_username)); + password = gtk_editable_get_text (GTK_EDITABLE (self->basic_password)); + + proxy = rest_proxy_new_with_authentication (url, FALSE, username, password); + break; + } + // thanks to libsoup there is no difference as the server defines the used authentication mechanism + case AUTHMODE_DIGEST: + { + const gchar *username, *password; + + username = gtk_editable_get_text (GTK_EDITABLE (self->basic_username)); + password = gtk_editable_get_text (GTK_EDITABLE (self->basic_password)); + + proxy = rest_proxy_new_with_authentication (url, FALSE, username, password); + break; + } + case AUTHMODE_OAUTH1: + { + g_object_set (self->oauth1_proxy, "url-format", url, NULL); + proxy = self->oauth1_proxy; + + break; + } + case AUTHMODE_OAUTH2: + { + proxy = rest_proxy_new (url, FALSE); + break; + } + } + SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY); + rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); RestProxyCall *call = rest_proxy_new_call (proxy); rest_proxy_call_set_method (call, http); rest_proxy_call_set_function (call, function); + + /* get params */ + model = demo_table_get_model (DEMO_TABLE (self->paramslb)); + for (guint i = 0; i < g_list_model_get_n_items (model); i++) + { + DemoTableRow *h = (DemoTableRow *)g_list_model_get_item (model, i); + rest_proxy_call_add_param (call, demo_table_row_get_key (h), demo_table_row_get_value (h)); + } + + /* get headers */ + model = demo_table_get_model (DEMO_TABLE (self->headerslb)); + for (guint i = 0; i < g_list_model_get_n_items (model); i++) + { + DemoTableRow *h = (DemoTableRow *)g_list_model_get_item (model, i); + rest_proxy_call_add_header (call, demo_table_row_get_key (h), demo_table_row_get_value (h)); + } + rest_proxy_call_invoke_async (call, NULL, call_returned, self); } static void +on_auth_method_activated (GtkDropDown *dropdown, + GParamSpec *pspec, + gpointer user_data) +{ + DemoRestPage *self = (DemoRestPage *)user_data; + g_autofree gchar *page_name = NULL; + GtkStringObject *obj = NULL; + + g_return_if_fail (DEMO_IS_REST_PAGE (self)); + + obj = GTK_STRING_OBJECT (gtk_drop_down_get_selected_item (dropdown)); + page_name = g_utf8_strdown (gtk_string_object_get_string (obj), -1); + gtk_stack_set_visible_child_name (GTK_STACK (self->authentication_stack), g_strdelimit (page_name, " ", '_')); +} + +static void demo_rest_page_class_init (DemoRestPageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -147,7 +551,35 @@ demo_rest_page_class_init (DemoRestPageClass *klass) gtk_widget_class_bind_template_child (widget_class, DemoRestPage, sourceview); gtk_widget_class_bind_template_child (widget_class, DemoRestPage, body); gtk_widget_class_bind_template_child (widget_class, DemoRestPage, notebook); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, authentication); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, authmethods); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, authentication_stack); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, headers); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, params); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, paramslb); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, headerslb); + + /* basic auth */ + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, basic_username); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, basic_password); + /* digest auth */ + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, digest_username); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, digest_password); + /* oauth 1 auth */ + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth1_client_identifier); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth1_client_secret); + /* oauth 2 auth */ + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth2_client_identifier); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth2_client_secret); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth2_auth_url); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth2_token_url); + gtk_widget_class_bind_template_child (widget_class, DemoRestPage, oauth2_redirect_url); + + /* callbacks */ gtk_widget_class_bind_template_callback (widget_class, on_send_clicked); + gtk_widget_class_bind_template_callback (widget_class, on_auth_method_activated); + gtk_widget_class_bind_template_callback (widget_class, on_oauth1_get_access_token_clicked); + gtk_widget_class_bind_template_callback (widget_class, on_oauth2_get_access_token_clicked); } static void @@ -162,6 +594,9 @@ demo_rest_page_init (DemoRestPage *self) gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (buffer), style); gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (self->notebook), self->body, "Body"); + gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (self->notebook), self->params, "Params"); + gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (self->notebook), self->authentication, "Authentication"); + gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (self->notebook), self->headers, "Headers"); } GtkWidget * diff --git a/examples/demo/demo-rest-page.ui b/examples/demo/demo-rest-page.ui index 417a96c..09d7c06 100644 --- a/examples/demo/demo-rest-page.ui +++ b/examples/demo/demo-rest-page.ui @@ -53,6 +53,339 @@ </child> </object> </child> + <child> + <object class="GtkScrolledWindow" id="params"> + <child> + <object class="DemoTable" id="paramslb"> + <property name="title">New Param...</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkBox" id="authentication"> + <property name="orientation">horizontal</property> + <property name="margin-start">6</property> + <property name="margin-end">6</property> + <property name="margin-top">6</property> + <property name="margin-bottom">6</property> + <property name="spacing">6</property> + <child> + <object class="GtkDropDown" id="authmethods"> + <property name="valign">start</property> + <property name="width-request">100</property> + <signal name="notify::selected-item" handler="on_auth_method_activated" swapped="no" object="DemoRestPage"/> + <property name="model"> + <object class="GtkStringList" id="auth_method_options"> + <items> + <item>No Auth</item> + <item>Basic</item> + <item>Digest</item> + <item>OAuth1</item> + <item>OAuth2</item> + </items> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStack" id="authentication_stack"> + <property name="hexpand">true</property> + <child> + <object class="GtkStackPage"> + <property name="name">no_auth</property> + <property name="child"> + <object class="GtkBox"/> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">basic</property> + <property name="child"> + <object class="GtkGrid"> + <property name="column-spacing">8</property> + <property name="column-homogeneous">true</property> + <property name="row-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="label">Username:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="basic_username"> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Password:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="basic_password"> + <property name="visibility">false</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + </object> + </child> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">digest</property> + <property name="child"> + <object class="GtkGrid"> + <property name="column-spacing">8</property> + <property name="column-homogeneous">true</property> + <property name="row-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="label">Username:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="digest_username"> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Password:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="digest_password"> + <property name="visibility">false</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + </object> + </child> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">oauth1</property> + <property name="child"> + <object class="GtkGrid"> + <property name="column-spacing">8</property> + <property name="column-homogeneous">true</property> + <property name="row-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="label">Client Identifier:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth1_client_identifier"> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Client Secret:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth1_client_secret"> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkButton" id="oauth1_get_access_token"> + <property name="label">Get access token...</property> + <layout> + <property name="column">1</property> + <property name="row">2</property> + </layout> + <signal name="clicked" handler="on_oauth1_get_access_token_clicked" swapped="no" object="DemoRestPage"/> + <style> + <class name="suggested-action"/> + </style> + </object> + </child> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">oauth2</property> + <property name="child"> + <object class="GtkGrid"> + <property name="column-spacing">8</property> + <property name="column-homogeneous">true</property> + <property name="row-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="label">Client Identifier:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth2_client_identifier"> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Client Secret:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth2_client_secret"> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Authorization URL:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth2_auth_url"> + <layout> + <property name="column">1</property> + <property name="row">2</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Token URL:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth2_token_url"> + <layout> + <property name="column">1</property> + <property name="row">3</property> + </layout> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label">Redirect URL:</property> + <property name="xalign">1.0</property> + <layout> + <property name="column">0</property> + <property name="row">4</property> + </layout> + </object> + </child> + <child> + <object class="GtkEntry" id="oauth2_redirect_url"> + <layout> + <property name="column">1</property> + <property name="row">4</property> + </layout> + </object> + </child> + <child> + <object class="GtkButton" id="oauth2_get_access_token"> + <property name="label">Get access token...</property> + <layout> + <property name="column">1</property> + <property name="row">5</property> + </layout> + <signal name="clicked" handler="on_oauth2_get_access_token_clicked" swapped="no" object="DemoRestPage"/> + <style> + <class name="suggested-action"/> + </style> + </object> + </child> + </object> + </property> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkScrolledWindow" id="headers"> + <child> + <object class="DemoTable" id="headerslb"> + <property name="title">New Header...</property> + </object> + </child> + </object> + </child> </object> </child> </template> diff --git a/examples/demo/demo-table-row.c b/examples/demo/demo-table-row.c new file mode 100644 index 0000000..1070a2a --- /dev/null +++ b/examples/demo/demo-table-row.c @@ -0,0 +1,149 @@ +/* demo-table-row.c + * + * Copyright 2022 Günther Wagner <info@gunibert.de> + * + * 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 3 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 <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "demo-table-row.h" + +struct _DemoTableRow +{ + GObject parent_instance; + + gchar *key; + gchar *value; +}; + +G_DEFINE_FINAL_TYPE (DemoTableRow, demo_table_row, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_KEY, + PROP_VALUE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +DemoTableRow * +demo_table_row_new (void) +{ + return g_object_new (DEMO_TYPE_TABLE_ROW, NULL); +} + +static void +demo_table_row_finalize (GObject *object) +{ + DemoTableRow *self = (DemoTableRow *)object; + + g_clear_pointer (&self->key, g_free); + g_clear_pointer (&self->value, g_free); + + G_OBJECT_CLASS (demo_table_row_parent_class)->finalize (object); +} + +static void +demo_table_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + DemoTableRow *self = DEMO_TABLE_ROW (object); + + switch (prop_id) + { + case PROP_KEY: + g_value_set_string (value, self->key); + break; + case PROP_VALUE: + g_value_set_string (value, self->value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +demo_table_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + DemoTableRow *self = DEMO_TABLE_ROW (object); + + switch (prop_id) + { + case PROP_KEY: + self->key = g_value_dup_string (value); + break; + case PROP_VALUE: + self->value = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + +static void +demo_table_row_class_init (DemoTableRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = demo_table_row_finalize; + object_class->get_property = demo_table_row_get_property; + object_class->set_property = demo_table_row_set_property; + + properties [PROP_KEY] = + g_param_spec_string ("key", + "Key", + "Key", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + properties [PROP_VALUE] = + g_param_spec_string ("value", + "Value", + "Value", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +demo_table_row_init (DemoTableRow *self) +{ +} + +gchar * +demo_table_row_get_key (DemoTableRow *self) +{ + g_return_val_if_fail (DEMO_IS_TABLE_ROW (self), NULL); + + return self->key; +} + +gchar * +demo_table_row_get_value (DemoTableRow *self) +{ + g_return_val_if_fail (DEMO_IS_TABLE_ROW (self), NULL); + + return self->value; +} diff --git a/examples/demo/demo-table-row.h b/examples/demo/demo-table-row.h new file mode 100644 index 0000000..ba93656 --- /dev/null +++ b/examples/demo/demo-table-row.h @@ -0,0 +1,35 @@ +/* demo-table-row.h + * + * Copyright 2022 Günther Wagner <info@gunibert.de> + * + * 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 3 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 <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define DEMO_TYPE_TABLE_ROW (demo_table_row_get_type()) + +G_DECLARE_FINAL_TYPE (DemoTableRow, demo_table_row, DEMO, TABLE_ROW, GObject) + +DemoTableRow *demo_table_row_new (void); +gchar *demo_table_row_get_key (DemoTableRow *self); +gchar *demo_table_row_get_value (DemoTableRow *self); + +G_END_DECLS diff --git a/examples/demo/demo-table-row.ui b/examples/demo/demo-table-row.ui new file mode 100644 index 0000000..7f71888 --- /dev/null +++ b/examples/demo/demo-table-row.ui @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk" version="4.0"/> + <object class="GtkListBoxRow" id="row"> + <child> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="valign">start</property> + <child> + <object class="GtkEntry" id="key"> + <property name="hexpand">true</property> + <style> + <class name="flat"/> + <class name="frame"/> + </style> + </object> + </child> + <child> + <object class="GtkEntry" id="value"> + <property name="hexpand">true</property> + <style> + <class name="flat"/> + <class name="frame"/> + </style> + </object> + </child> + <style> + <class name="linked"/> + </style> + </object> + </child> + <style> + <class name="headerrow"/> + </style> + </object> +</interface> diff --git a/examples/demo/demo-table.c b/examples/demo/demo-table.c new file mode 100644 index 0000000..3591c5c --- /dev/null +++ b/examples/demo/demo-table.c @@ -0,0 +1,210 @@ +/* demo-table.c + * + * Copyright 2022 Günther Wagner <info@gunibert.de> + * + * 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 3 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 <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "demo-table.h" +#include "demo-table-row.h" + +struct _DemoTable +{ + GtkWidget parent_instance; + char *title; + + GtkListBox *lb; + GListModel *model; + + GtkWidget *dummy_row; +}; + +G_DEFINE_FINAL_TYPE (DemoTable, demo_table, GTK_TYPE_WIDGET) + +enum { + PROP_0, + PROP_TITLE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +GtkWidget * +demo_table_new (char *title) +{ + return g_object_new (DEMO_TYPE_TABLE, "title", title, NULL); +} + +static void +demo_table_finalize (GObject *object) +{ + DemoTable *self = (DemoTable *)object; + + g_clear_pointer (&self->title, g_free); + + G_OBJECT_CLASS (demo_table_parent_class)->finalize (object); +} + +static void +demo_table_dispose (GObject *object) +{ + DemoTable *self = (DemoTable *)object; + + gtk_widget_unparent (GTK_WIDGET (self->lb)); + + G_OBJECT_CLASS (demo_table_parent_class)->dispose (object); +} + +static void +demo_table_activated (GtkListBox *box, + GtkListBoxRow *row, + gpointer user_data) +{ + DemoTable *self = (DemoTable *)user_data; + + g_return_if_fail (DEMO_IS_TABLE (self)); + + if (GTK_WIDGET (row) == self->dummy_row) + { + GListStore *s = G_LIST_STORE (self->model); + g_list_store_append (s, demo_table_row_new ()); + } +} + +static GtkWidget * +create_row (gpointer item, + gpointer user_data) +{ + GtkBuilder *builder; + + builder = gtk_builder_new_from_resource ("/org/gnome/RestDemo/demo-table-row.ui"); + + GObject *key_entry = gtk_builder_get_object (builder, "key"); + g_object_bind_property (key_entry, "text", item, "key", G_BINDING_DEFAULT); + + GObject *value_entry = gtk_builder_get_object (builder, "value"); + g_object_bind_property (value_entry, "text", item, "value", G_BINDING_DEFAULT); + return GTK_WIDGET (gtk_builder_get_object (builder, "row")); +} + +static GtkWidget * +create_dummy_row (DemoTable *self) +{ + GtkWidget *row; + GtkWidget *label; + + g_return_val_if_fail (DEMO_IS_TABLE (self), NULL); + + label = g_object_new (GTK_TYPE_LABEL, + "label", self->title, + "visible", TRUE, + "xalign", 0.0f, + NULL); + g_object_bind_property (self, "title", label, "label", G_BINDING_DEFAULT); + gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label"); + + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "child", label, + "visible", TRUE, + NULL); + + return row; +} + +static void +demo_table_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + DemoTable *self = DEMO_TABLE (object); + + switch (prop_id) + { + case PROP_TITLE: + g_value_set_string (value, self->title); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +demo_table_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + DemoTable *self = DEMO_TABLE (object); + + switch (prop_id) + { + case PROP_TITLE: + g_clear_pointer (&self->title, g_free); + self->title = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +demo_table_class_init (DemoTableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = demo_table_finalize; + object_class->dispose = demo_table_dispose; + object_class->set_property = demo_table_set_property; + object_class->get_property = demo_table_get_property; + + properties [PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "Title", + "", + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_TITLE, + properties [PROP_TITLE]); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); +} + +static void +demo_table_init (DemoTable *self) +{ + self->lb = GTK_LIST_BOX (gtk_list_box_new ()); + gtk_widget_set_hexpand (GTK_WIDGET (self->lb), TRUE); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->lb), GTK_SELECTION_NONE); + gtk_widget_set_parent (GTK_WIDGET (self->lb), GTK_WIDGET (self)); + g_signal_connect (self->lb, "row-activated", G_CALLBACK (demo_table_activated), self); + + self->model = G_LIST_MODEL (g_list_store_new (DEMO_TYPE_TABLE_ROW)); + gtk_list_box_bind_model (self->lb, self->model, create_row, self, NULL); + + self->dummy_row = create_dummy_row (self); + gtk_list_box_append (self->lb, self->dummy_row); +} + +GListModel * +demo_table_get_model (DemoTable *self) +{ + g_return_val_if_fail (DEMO_IS_TABLE (self), NULL); + + return self->model; +} diff --git a/examples/demo/demo-table.h b/examples/demo/demo-table.h new file mode 100644 index 0000000..0b570db --- /dev/null +++ b/examples/demo/demo-table.h @@ -0,0 +1,34 @@ +/* demo-table.h + * + * Copyright 2022 Günther Wagner <info@gunibert.de> + * + * 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 3 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 <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define DEMO_TYPE_TABLE (demo_table_get_type()) + +G_DECLARE_FINAL_TYPE (DemoTable, demo_table, DEMO, TABLE, GtkWidget) + +GtkWidget *demo_table_new (char *title); +GListModel *demo_table_get_model (DemoTable *self); + +G_END_DECLS diff --git a/examples/demo/demo-window.c b/examples/demo/demo-window.c index 32681d5..63c48d8 100644 --- a/examples/demo/demo-window.c +++ b/examples/demo/demo-window.c @@ -38,14 +38,6 @@ demo_window_new (GtkApplication *app) } static void -demo_window_finalize (GObject *object) -{ - DemoWindow *self = (DemoWindow *)object; - - G_OBJECT_CLASS (demo_window_parent_class)->finalize (object); -} - -static void on_add_tab_clicked (GtkButton *btn, gpointer user_data) { @@ -66,10 +58,8 @@ on_add_tab_clicked (GtkButton *btn, static void demo_window_class_init (DemoWindowClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - object_class->finalize = demo_window_finalize; gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/RestDemo/demo-window.ui"); gtk_widget_class_bind_template_child (widget_class, DemoWindow, tabview); gtk_widget_class_bind_template_callback (widget_class, on_add_tab_clicked); diff --git a/examples/demo/meson.build b/examples/demo/meson.build index dae36b3..43d77e9 100644 --- a/examples/demo/meson.build +++ b/examples/demo/meson.build @@ -2,6 +2,8 @@ demo_sources = [ 'demo-main.c', 'demo-window.c', 'demo-rest-page.c', + 'demo-table.c', + 'demo-table-row.c', ] demo_deps = [ diff --git a/examples/demo/org.gnome.RestDemo.gresource.xml b/examples/demo/org.gnome.RestDemo.gresource.xml index ecde4f3..e58e441 100644 --- a/examples/demo/org.gnome.RestDemo.gresource.xml +++ b/examples/demo/org.gnome.RestDemo.gresource.xml @@ -3,5 +3,7 @@ <gresource prefix="/org/gnome/RestDemo"> <file>demo-window.ui</file> <file>demo-rest-page.ui</file> + <file>demo-table-row.ui</file> + <file>style.css</file> </gresource> </gresources> diff --git a/examples/demo/org.gnome.RestDemo.json b/examples/demo/org.gnome.RestDemo.json index 1ce49cf..bd0fde4 100644 --- a/examples/demo/org.gnome.RestDemo.json +++ b/examples/demo/org.gnome.RestDemo.json @@ -24,6 +24,17 @@ ], "modules" : [ { + "name" : "libsoup", + "builddir" : true, + "buildsystem" : "meson", + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/GNOME/libsoup.git" + } + ] + }, + { "name" : "librest-demo", "builddir" : true, "buildsystem" : "meson", @@ -34,7 +45,8 @@ } ], "config-opts" : [ - "-Dgtk_doc=false" + "-Dgtk_doc=false", + "-Dsoup2=false" ] } ], diff --git a/examples/demo/style.css b/examples/demo/style.css new file mode 100644 index 0000000..266daf3 --- /dev/null +++ b/examples/demo/style.css @@ -0,0 +1,3 @@ +.headerrow { + padding: 0px; +} |