diff options
author | Vivien Kraus <vivien@planete-kraus.eu> | 2021-10-06 18:06:12 +0200 |
---|---|---|
committer | Vivien Kraus <vivien@planete-kraus.eu> | 2021-10-12 22:27:38 +0200 |
commit | 3ba93ce1fccbc54d4695d55011ce856018c1b2cd (patch) | |
tree | b3668d95661643c55a2d6bf663ab58b5e3889a18 | |
parent | f53954f07104237497e9c121bffe0a3814116691 (diff) |
gui: add a primitive browser widget
-rw-r--r-- | po/POTFILES.in | 14 | ||||
-rw-r--r-- | po/disfluid.pot | 131 | ||||
-rw-r--r-- | po/fr.po | 172 | ||||
-rw-r--r-- | src/scm/webid-oidc/client/application.scm | 313 | ||||
-rw-r--r-- | src/ui/Makefile.am | 8 | ||||
-rw-r--r-- | src/ui/error-page.glade | 108 | ||||
-rw-r--r-- | src/ui/link-widget.glade | 79 | ||||
-rw-r--r-- | src/ui/loaded-page.glade | 99 | ||||
-rw-r--r-- | src/ui/loading-page.glade | 34 | ||||
-rw-r--r-- | src/ui/new-page.glade | 37 | ||||
-rw-r--r-- | src/ui/updated-page.glade | 121 |
11 files changed, 998 insertions, 118 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in index bde8044..a55dcd4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -38,11 +38,11 @@ src/scm/webid-oidc/client/accounts.scm src/scm/webid-oidc/client/application.scm src/scm/webid-oidc/client/client.scm src/scm/webid-oidc/client/gui.scm -src/scm/webid-oidc/client/gui/application.scm -src/scm/webid-oidc/client/gui/application-hooks.scm src/scm/webid-oidc/client/gui/account-widget.scm -src/scm/webid-oidc/client/gui/accounts-widget.scm src/scm/webid-oidc/client/gui/accounts-widget-logic.scm +src/scm/webid-oidc/client/gui/accounts-widget.scm +src/scm/webid-oidc/client/gui/application-hooks.scm +src/scm/webid-oidc/client/gui/application.scm src/scm/webid-oidc/client/gui/authorization-prompt.scm src/scm/webid-oidc/client/gui/authorizations-widget.scm src/scm/webid-oidc/client/gui/client-widget.scm @@ -84,4 +84,10 @@ src/scm/webid-oidc/web-i18n.scm src/ui/account-widget.glade src/ui/authorization-prompt.glade src/ui/client-widget.glade -src/ui/main-window.glade
\ No newline at end of file +src/ui/error-page.glade +src/ui/link-widget.glade +src/ui/loaded-page.glade +src/ui/loading-page.glade +src/ui/main-window.glade +src/ui/new-page.glade +src/ui/updated-page.glade
\ No newline at end of file diff --git a/po/disfluid.pot b/po/disfluid.pot index c8e9654..274f6b3 100644 --- a/po/disfluid.pot +++ b/po/disfluid.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: disfluid SNAPSHOT\n" "Report-Msgid-Bugs-To: vivien@planete-kraus.eu\n" -"POT-Creation-Date: 2021-10-12 18:30+0200\n" +"POT-Creation-Date: 2021-10-12 22:25+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -345,7 +345,7 @@ msgid "The application you are trying to authorize behaved unexpectedly." msgstr "" #: src/scm/webid-oidc/authorization-page-unsafe.scm:126 -#: src/scm/webid-oidc/resource-server.scm:316 +#: src/scm/webid-oidc/resource-server.scm:317 msgid "reason-phrase|Found" msgstr "" @@ -491,7 +491,7 @@ msgstr "" msgid "#:grant-types should be a list of symbols" msgstr "" -#: src/scm/webid-oidc/client.scm:283 src/scm/webid-oidc/resource-server.scm:176 +#: src/scm/webid-oidc/client.scm:283 src/scm/webid-oidc/resource-server.scm:177 msgid "reason-phrase|Not Modified" msgstr "" @@ -615,22 +615,37 @@ msgstr "" msgid "The issuer should be a string or URI." msgstr "" -#: src/scm/webid-oidc/client/application.scm:242 +#: src/scm/webid-oidc/client/application.scm:259 #, scheme-format msgid "Add an account on ~a" msgstr "" -#: src/scm/webid-oidc/client/application.scm:257 +#: src/scm/webid-oidc/client/application.scm:274 #, scheme-format msgid "" "You already have an account for ~a issued by ~a and it is currently selected." msgstr "" -#: src/scm/webid-oidc/client/application.scm:276 +#: src/scm/webid-oidc/client/application.scm:293 #, scheme-format msgid "You already have an account for ~a issued by ~a." msgstr "" +#: src/scm/webid-oidc/client/application.scm:493 +#, scheme-format +msgid "Loading ~a..." +msgstr "" + +#: src/scm/webid-oidc/client/application.scm:572 +#, scheme-format +msgid "Updating ~a (expected ETag ~a)" +msgstr "" + +#: src/scm/webid-oidc/client/application.scm:619 +#, scheme-format +msgid "Deleting ~a (expected ETag ~a)" +msgstr "" + #: src/scm/webid-oidc/client/client.scm:91 msgid "" "Client ID and redirect URIs should be URIs, and key pair should be a key " @@ -642,16 +657,16 @@ msgstr "" msgid "The application state changed: it is now ~a.\n" msgstr "" -#: src/scm/webid-oidc/client/gui/application.scm:116 -msgid "Coming soon!" +#: src/scm/webid-oidc/client/gui/accounts-widget-logic.scm:66 +msgid "Stub: please enter an URI or a host name...\n" msgstr "" #: src/scm/webid-oidc/client/gui/accounts-widget.scm:87 msgid "Please add an account." msgstr "" -#: src/scm/webid-oidc/client/gui/accounts-widget-logic.scm:66 -msgid "Stub: please enter an URI or a host name...\n" +#: src/scm/webid-oidc/client/gui/application.scm:116 +msgid "Coming soon!" msgstr "" #: src/scm/webid-oidc/client/gui/authorization-prompt.scm:75 @@ -1027,7 +1042,7 @@ msgid "The port should be a number between 0 and 65535.\n" msgstr "" #: src/scm/webid-oidc/hello-world.scm:159 -#: src/scm/webid-oidc/resource-server.scm:337 +#: src/scm/webid-oidc/resource-server.scm:338 msgid "reason-phrase|Unauthorized" msgstr "" @@ -1040,7 +1055,7 @@ msgid "<p>This page requires authentication with Solid.</p>" msgstr "" #: src/scm/webid-oidc/hello-world.scm:179 -#: src/scm/webid-oidc/resource-server.scm:345 +#: src/scm/webid-oidc/resource-server.scm:346 msgid "reason-phrase|Method Not Allowed" msgstr "" @@ -2079,64 +2094,64 @@ msgstr "" msgid "the refresh token is bound to key ~s, which is not that one" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:58 +#: src/scm/webid-oidc/resource-server.scm:59 msgid "" "You need to pass #:server-uri URI where URI is the public URI of the server, " "as a (web uri)." msgstr "" -#: src/scm/webid-oidc/resource-server.scm:85 +#: src/scm/webid-oidc/resource-server.scm:86 #, scheme-format msgid "~a: authentication failure: ~a\n" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:89 +#: src/scm/webid-oidc/resource-server.scm:90 #, scheme-format msgid "~a: authentication failure\n" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:161 -#: src/scm/webid-oidc/resource-server.scm:368 +#: src/scm/webid-oidc/resource-server.scm:162 +#: src/scm/webid-oidc/resource-server.scm:369 msgid "reason-phrase|Precondition Failed" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:198 +#: src/scm/webid-oidc/resource-server.scm:199 msgid "The owner is not defined." msgstr "" -#: src/scm/webid-oidc/resource-server.scm:248 -#: src/scm/webid-oidc/resource-server.scm:271 +#: src/scm/webid-oidc/resource-server.scm:249 +#: src/scm/webid-oidc/resource-server.scm:272 msgid "Bad Request" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:282 +#: src/scm/webid-oidc/resource-server.scm:283 msgid "reason-phrase|Created" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:305 +#: src/scm/webid-oidc/resource-server.scm:306 #, scheme-format msgid "~a: ignoring a group that cannot be fetched: ~a\n" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:309 +#: src/scm/webid-oidc/resource-server.scm:310 #, scheme-format msgid "~a: ignoring a group that cannot be fetched\n" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:333 +#: src/scm/webid-oidc/resource-server.scm:334 #: src/scm/webid-oidc/token-endpoint.scm:104 msgid "reason-phrase|Forbidden" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:354 +#: src/scm/webid-oidc/resource-server.scm:355 msgid "reason-phrase|Conflict" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:361 +#: src/scm/webid-oidc/resource-server.scm:362 msgid "reason-phrase|Unsupported Media Type" msgstr "" -#: src/scm/webid-oidc/resource-server.scm:375 +#: src/scm/webid-oidc/resource-server.scm:376 msgid "reason-phrase|Not Acceptable" msgstr "" @@ -2333,10 +2348,54 @@ msgstr "" msgid "Undo" msgstr "" -#: src/ui/client-widget.glade:149 +#: src/ui/client-widget.glade:149 src/ui/updated-page.glade:102 msgid "Update" msgstr "" +#: src/ui/error-page.glade:45 +msgid "The request failed:" +msgstr "" + +#: src/ui/error-page.glade:57 +msgid "404" +msgstr "" + +#: src/ui/error-page.glade:73 +msgid "Not Found" +msgstr "" + +#: src/ui/link-widget.glade:20 +msgid "https://example.com" +msgstr "" + +#: src/ui/link-widget.glade:39 +msgid "rel" +msgstr "" + +#: src/ui/link-widget.glade:51 +msgid "=" +msgstr "" + +#: src/ui/link-widget.glade:63 +msgid "type" +msgstr "" + +#: src/ui/loaded-page.glade:40 +msgid "text/turtle" +msgstr "" + +#: src/ui/loaded-page.glade:52 +msgid "@prefix rdf: ..." +msgstr "" + +#: src/ui/loaded-page.glade:67 +msgid "Delete" +msgstr "" + +#: src/ui/loaded-page.glade:80 +msgid "Update…" +msgstr "" + #: src/ui/main-window.glade:29 msgid "Explore" msgstr "" @@ -2352,3 +2411,19 @@ msgstr "" #: src/ui/main-window.glade:73 msgid "Settings" msgstr "" + +#: src/ui/new-page.glade:27 +msgid "Enter an URI in the URI bar above to start your journey." +msgstr "" + +#: src/ui/updated-page.glade:20 +msgid "Content type:" +msgstr "" + +#: src/ui/updated-page.glade:55 +msgid "Content:" +msgstr "" + +#: src/ui/updated-page.glade:89 +msgid "Discard edits" +msgstr "" @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: webid-oidc 0.0.0\n" "Report-Msgid-Bugs-To: vivien@planete-kraus.eu\n" -"POT-Creation-Date: 2021-10-12 18:30+0200\n" -"PO-Revision-Date: 2021-10-11 16:45+0200\n" +"POT-Creation-Date: 2021-10-12 22:25+0200\n" +"PO-Revision-Date: 2021-10-12 22:26+0200\n" "Last-Translator: Vivien Kraus <vivien@planete-kraus.eu>\n" "Language-Team: French <vivien@planete-kraus.eu>\n" "Language: fr\n" @@ -377,7 +377,7 @@ msgstr "" "L’application que vous essayez d’autoriser se comporte de façon inattendue." #: src/scm/webid-oidc/authorization-page-unsafe.scm:126 -#: src/scm/webid-oidc/resource-server.scm:316 +#: src/scm/webid-oidc/resource-server.scm:317 msgid "reason-phrase|Found" msgstr "Trouvé" @@ -533,7 +533,7 @@ msgstr "#:response-types doit être une liste de symboles" msgid "#:grant-types should be a list of symbols" msgstr "#:grant-types doit être une liste de symboles" -#: src/scm/webid-oidc/client.scm:283 src/scm/webid-oidc/resource-server.scm:176 +#: src/scm/webid-oidc/client.scm:283 src/scm/webid-oidc/resource-server.scm:177 msgid "reason-phrase|Not Modified" msgstr "Non Modifié" @@ -664,12 +664,12 @@ msgstr "Le sujet doit être une chaîne de caractères ou une URI." msgid "The issuer should be a string or URI." msgstr "L’émetteur doit être une chaîne de caractères ou une URI." -#: src/scm/webid-oidc/client/application.scm:242 +#: src/scm/webid-oidc/client/application.scm:259 #, scheme-format msgid "Add an account on ~a" msgstr "Ajouter un compte sur ~a" -#: src/scm/webid-oidc/client/application.scm:257 +#: src/scm/webid-oidc/client/application.scm:274 #, scheme-format msgid "" "You already have an account for ~a issued by ~a and it is currently selected." @@ -677,11 +677,26 @@ msgstr "" "Vous avez déjà un compte pour ~a émis par ~a et il est actuellement " "sélectionné." -#: src/scm/webid-oidc/client/application.scm:276 +#: src/scm/webid-oidc/client/application.scm:293 #, scheme-format msgid "You already have an account for ~a issued by ~a." msgstr "Vous avez déjà un compte pour ~a émis par ~a." +#: src/scm/webid-oidc/client/application.scm:493 +#, scheme-format +msgid "Loading ~a..." +msgstr "Chargement de ~a…" + +#: src/scm/webid-oidc/client/application.scm:572 +#, scheme-format +msgid "Updating ~a (expected ETag ~a)" +msgstr "Mise à jour de ~a (ETag attendu ~a)" + +#: src/scm/webid-oidc/client/application.scm:619 +#, scheme-format +msgid "Deleting ~a (expected ETag ~a)" +msgstr "Suppression de ~a (ETag attendu ~a)" + #: src/scm/webid-oidc/client/client.scm:91 msgid "" "Client ID and redirect URIs should be URIs, and key pair should be a key " @@ -695,17 +710,17 @@ msgstr "" msgid "The application state changed: it is now ~a.\n" msgstr "L’état de l’application a changé : c’est maintenant ~a.\n" -#: src/scm/webid-oidc/client/gui/application.scm:116 -msgid "Coming soon!" -msgstr "C’est pour bientôt !" +#: src/scm/webid-oidc/client/gui/accounts-widget-logic.scm:66 +msgid "Stub: please enter an URI or a host name...\n" +msgstr "Bouchon : veuillez entrer une URI ou un nom d’hôte…\n" #: src/scm/webid-oidc/client/gui/accounts-widget.scm:87 msgid "Please add an account." msgstr "Veuillez ajouter un compte." -#: src/scm/webid-oidc/client/gui/accounts-widget-logic.scm:66 -msgid "Stub: please enter an URI or a host name...\n" -msgstr "Bouchon : veuillez entrer une URI ou un nom d’hôte…\n" +#: src/scm/webid-oidc/client/gui/application.scm:116 +msgid "Coming soon!" +msgstr "C’est pour bientôt !" #: src/scm/webid-oidc/client/gui/authorization-prompt.scm:75 #, scheme-format @@ -1130,7 +1145,7 @@ msgid "The port should be a number between 0 and 65535.\n" msgstr "Le port doit être un nombre entre 0 et 65535.\n" #: src/scm/webid-oidc/hello-world.scm:159 -#: src/scm/webid-oidc/resource-server.scm:337 +#: src/scm/webid-oidc/resource-server.scm:338 msgid "reason-phrase|Unauthorized" msgstr "Non Autorisé" @@ -1143,7 +1158,7 @@ msgid "<p>This page requires authentication with Solid.</p>" msgstr "<p>Cette page requiert une authentification avec Solid.</p>" #: src/scm/webid-oidc/hello-world.scm:179 -#: src/scm/webid-oidc/resource-server.scm:345 +#: src/scm/webid-oidc/resource-server.scm:346 msgid "reason-phrase|Method Not Allowed" msgstr "Méthode Non Autorisée" @@ -2458,7 +2473,7 @@ msgid "the refresh token is bound to key ~s, which is not that one" msgstr "" "le jeton de rafraîchissement est lié à la clé ~s, ce n’est pas celle utilisée" -#: src/scm/webid-oidc/resource-server.scm:58 +#: src/scm/webid-oidc/resource-server.scm:59 msgid "" "You need to pass #:server-uri URI where URI is the public URI of the server, " "as a (web uri)." @@ -2466,60 +2481,58 @@ msgstr "" "Vous devez passer #:server-uri URI où URI est l’URI publique du serveur, " "comme dans (web uri)." -#: src/scm/webid-oidc/resource-server.scm:85 +#: src/scm/webid-oidc/resource-server.scm:86 #, scheme-format msgid "~a: authentication failure: ~a\n" msgstr "~a : échec d’authentificationn : ~a\n" -#: src/scm/webid-oidc/resource-server.scm:89 +#: src/scm/webid-oidc/resource-server.scm:90 #, scheme-format msgid "~a: authentication failure\n" msgstr "~a : échec d’authentification\n" -#: src/scm/webid-oidc/resource-server.scm:161 -#: src/scm/webid-oidc/resource-server.scm:368 +#: src/scm/webid-oidc/resource-server.scm:162 +#: src/scm/webid-oidc/resource-server.scm:369 msgid "reason-phrase|Precondition Failed" msgstr "Échec de Précondition" -#: src/scm/webid-oidc/resource-server.scm:198 +#: src/scm/webid-oidc/resource-server.scm:199 msgid "The owner is not defined." msgstr "Le propriétaire n’est pas défini." -#: src/scm/webid-oidc/resource-server.scm:248 -#: src/scm/webid-oidc/resource-server.scm:271 -#, fuzzy -#| msgid "Bad request" +#: src/scm/webid-oidc/resource-server.scm:249 +#: src/scm/webid-oidc/resource-server.scm:272 msgid "Bad Request" msgstr "Requête invalide" -#: src/scm/webid-oidc/resource-server.scm:282 +#: src/scm/webid-oidc/resource-server.scm:283 msgid "reason-phrase|Created" msgstr "Créé" -#: src/scm/webid-oidc/resource-server.scm:305 +#: src/scm/webid-oidc/resource-server.scm:306 #, scheme-format msgid "~a: ignoring a group that cannot be fetched: ~a\n" msgstr "~a : j’ignore un groupe qui n’a pas pu être téléchargé : ~a\n" -#: src/scm/webid-oidc/resource-server.scm:309 +#: src/scm/webid-oidc/resource-server.scm:310 #, scheme-format msgid "~a: ignoring a group that cannot be fetched\n" msgstr "~a : j’ignore un groupe qui ne peut pas être téléchargé\n" -#: src/scm/webid-oidc/resource-server.scm:333 +#: src/scm/webid-oidc/resource-server.scm:334 #: src/scm/webid-oidc/token-endpoint.scm:104 msgid "reason-phrase|Forbidden" msgstr "Interdit" -#: src/scm/webid-oidc/resource-server.scm:354 +#: src/scm/webid-oidc/resource-server.scm:355 msgid "reason-phrase|Conflict" msgstr "Conflit" -#: src/scm/webid-oidc/resource-server.scm:361 +#: src/scm/webid-oidc/resource-server.scm:362 msgid "reason-phrase|Unsupported Media Type" msgstr "Type de Média Non Supporté" -#: src/scm/webid-oidc/resource-server.scm:375 +#: src/scm/webid-oidc/resource-server.scm:376 msgid "reason-phrase|Not Acceptable" msgstr "Inacceptable" @@ -2725,10 +2738,54 @@ msgstr "Générer une paire de clés" msgid "Undo" msgstr "Annuler" -#: src/ui/client-widget.glade:149 +#: src/ui/client-widget.glade:149 src/ui/updated-page.glade:102 msgid "Update" msgstr "Mettre à jour" +#: src/ui/error-page.glade:45 +msgid "The request failed:" +msgstr "La requête a échoué :" + +#: src/ui/error-page.glade:57 +msgid "404" +msgstr "404" + +#: src/ui/error-page.glade:73 +msgid "Not Found" +msgstr "Non Trouvé" + +#: src/ui/link-widget.glade:20 +msgid "https://example.com" +msgstr "https://exemple.com" + +#: src/ui/link-widget.glade:39 +msgid "rel" +msgstr "rel" + +#: src/ui/link-widget.glade:51 +msgid "=" +msgstr "=" + +#: src/ui/link-widget.glade:63 +msgid "type" +msgstr "type" + +#: src/ui/loaded-page.glade:40 +msgid "text/turtle" +msgstr "text/turtle" + +#: src/ui/loaded-page.glade:52 +msgid "@prefix rdf: ..." +msgstr "@prefix rdf: ..." + +#: src/ui/loaded-page.glade:67 +msgid "Delete" +msgstr "Supprimer" + +#: src/ui/loaded-page.glade:80 +msgid "Update…" +msgstr "Mise à jour…" + #: src/ui/main-window.glade:29 msgid "Explore" msgstr "Explorer" @@ -2745,6 +2802,55 @@ msgstr "Comptes" msgid "Settings" msgstr "Paramètres" +#: src/ui/new-page.glade:27 +msgid "Enter an URI in the URI bar above to start your journey." +msgstr "Entrez une URI dans la bare d’URI pour commencer votre voyage." + +#: src/ui/updated-page.glade:20 +msgid "Content type:" +msgstr "Type de contenu :" + +#: src/ui/updated-page.glade:55 +msgid "Content:" +msgstr "Contenu :" + +#: src/ui/updated-page.glade:89 +msgid "Discard edits" +msgstr "Rejeter les modifications" + +#~ msgid "the page URI (#:uri) should be a string encoding an URI or an URI" +#~ msgstr "" +#~ "l’URI de la page doit être une chaîne de caractères encodant une URI ou " +#~ "une URI" + +#~ msgid "" +#~ "the error code (#:code) should be an integer and the reason phrase (#:" +#~ "reason-phrase) should be a string" +#~ msgstr "" +#~ "le code d’erreur (#:code) doit être un entier et l’explication (#:reason-" +#~ "phrase) doit être une chaîne de caractères" + +#~ msgid "" +#~ "the etag (#:etag) should be a string, the links (#:links) should be a " +#~ "list of links, the content-type (#:content-type) should be a symbol, and " +#~ "the content (#:content) should be a string or a bytevector" +#~ msgstr "" +#~ "l’etag (#:etag) doit être une chaîne de caractères, les liens (#:links) " +#~ "doivent être une liste de liens, le type de contenu (#:content-type) doit " +#~ "être un symbole, et le contenu (#:content) doit être une chaîne de " +#~ "caractères ou un vecteur d’octets" + +#~ msgid "" +#~ "the desired links (#:desired-links) should be an alist from URI to " +#~ "alists, the desired content-type (#:desired-content-type) should be a " +#~ "symbol, and the desired content (#:desired-content) should be a string or " +#~ "a bytevector" +#~ msgstr "" +#~ "les liens désirés (#:desired-links) doivent être une aliste d’URIs vers " +#~ "des alistes, le type de contenu désiré (#:desired-content-type) doit être " +#~ "un symbole, et le ccontenu désiré (#:desired-content-type) doit être unne " +#~ "chaîne de caractères ou un vecteur d’octets" + #~ msgid "Disfluid" #~ msgstr "Disfluid" diff --git a/src/scm/webid-oidc/client/application.scm b/src/scm/webid-oidc/client/application.scm index 5185cfb..58d1dad 100644 --- a/src/scm/webid-oidc/client/application.scm +++ b/src/scm/webid-oidc/client/application.scm @@ -28,6 +28,7 @@ #:use-module (srfi srfi-9) #:use-module (srfi srfi-19) #:use-module (webid-oidc errors) + #:use-module (webid-oidc http-link) #:use-module ((webid-oidc parameters) #:prefix p:) #:use-module ((webid-oidc stubs) #:prefix stubs:) #:use-module ((webid-oidc oidc-id-token) #:prefix id:) @@ -52,7 +53,15 @@ authorization-uri reason continuation <application-state> - main-account other-accounts client error-messages authorization-prompts running-jobs pages + main-account other-accounts client error-messages authorization-prompts running-jobs page + + <page> + <new-page> + <page-with-uri> uri + <loading-page> + <error-page> code reason-phrase + <loaded-page> etag links content-type content + <updated-page> desired-links desired-content-type desired-content add-account choose-account @@ -60,9 +69,15 @@ set-accounts set-client fold-authorization-prompts - add-page set-page-uri - close-page + edit-page + remove-link + add-link + change-content-type + change-content + discard-updates + commit-updates + delete-page ) #:declarative? #t) @@ -120,10 +135,12 @@ #:init-keywords #:running-jobs #:getter running-jobs #:init-value '()) - (pages - #:init-keyword #:pages - #:getter pages - #:init-value '())) + (page + #:init-keyword #:page + #:getter page + #:init-thunk + (lambda () + (make <new-page>)))) (define-method (equal? (x <application-state>) (y <application-state>)) (and (equal? (main-account x) (main-account y)) @@ -132,7 +149,7 @@ (equal? (error-messages x) (error-messages y)) (equal? (authorization-prompts x) (authorization-prompts y)) (equal? (running-jobs x) (running-jobs y)) - (equal? (pages x) (pages y)))) + (equal? (page x) (page y)))) (define-method (display (state <application-state>) port) (format port "#<<application-state> main-account=~a other-accounts=~a client=~a error-messages=~a authorization-prompts=~a running-jobs=~a>" @@ -351,8 +368,7 @@ with-unresolved-prompt)))) (iter prompts seed state))))))) -(define-class <page> () - (identifier #:init-keyword #:identifier #:getter identifier)) +(define-class <page> ()) (define-method (equal? (x <page>) (y <page>)) (and (equal? (identifier x) (identifier y)))) @@ -372,64 +388,257 @@ (reason-phrase #:init-keyword #:reason-phrase #:getter reason-phrase)) (define-method (equal? (x <error-page>) (y <error-page>)) - (and (equal? (code x) (code y)) + (and (equal? (uri x) (uri y)) + (equal? (code x) (code y)) (equal? (reason-phrase x) (reason-phrase y)))) (define-class <loaded-page> (<page-with-uri>) (etag #:init-keyword #:etag #:getter etag) - (links #:init-keyword #:links #:getter links)) + (links #:init-keyword #:links #:getter links) + (content-type #:init-keyword #:content-type #:getter content-type) + (content #:init-keyword #:content #:getter content)) (define-method (equal? (x <loaded-page>) (y <loaded-page>)) - (and (equal? (etag x) (etag y)) - (equal? (links x) (links y)))) + (and (equal? (uri x) (uri y)) + (equal? (etag x) (etag y)) + (equal? (links x) (links y)) + (equal? (content-type x) (content-type y)) + (equal? (content x) (content y)))) -(define-class <rdf-page> (<loaded-page>) - (triples #:init-keyword #:triples #:getter triples)) +(define-class <updated-page> (<loaded-page>) + (desired-links #:init-keyword #:desired-links #:getter desired-links) + (desired-content-type #:init-keyword #:desired-content-type #:getter desired-content-type) + (desired-content #:init-keyword #:desired-content #:getter desired-content)) + +(define-method (equal? (x <updated-page>) (y <updated-page>)) + (and (equal? (uri x) (uri y)) + (equal? (etag x) (etag y)) + (equal? (links x) (links y)) + (equal? (content-type x) (content-type y)) + (equal? (content x) (content y)) + (equal? (desired-links x) (desired-links y)) + (equal? (desired-content-type x) (desired-content-type y)) + (equal? (desired-content x) (desired-content y)))) + +(define-method (edit-page (page <page>)) + page) + +(define-method (edit-page (page <loaded-page>)) + (make <updated-page> + #:uri (uri page) + #:etag (etag page) + #:links (links page) + #:content-type (content-type page) + #:content (content page) + #:desired-links (links page) + #:desired-content-type (content-type page) + #:desired-content (content page))) + +(define-method (edit-page (page <updated-page>)) + page) + +(define-method (edit-page (state <application-state>)) + (let ((ret (shallow-clone state))) + (slot-set! ret 'page (edit-page (page ret))) + ret)) -(define-method (equal? (x <rdf-page>) (y <rdf-page>)) - (and (equal? (triples x) (triples y)))) +(define-method (remove-link (page <page>) target key value) + (let ((ret (edit-page page))) + (when (is-a? ret <updated-page>) + (let ((filtered + (map (match-lambda + (((? (cute equal? <> target)) attributes ...) + `(,target + ,(filter + (match-lambda + ((the-key . the-value) + (and (equal? the-key key) + (equal? the-value value)))) + attributes))) + (other other)) + (links ret)))) + (slot-set! ret 'desired-links + (filter (match-lambda + (((? uri?) ()) + #f) + (else #t)) + filtered)))) + ret)) -(define-class <non-rdf-page> (<loaded-page>) - (content-type #:init-keyword #:content-type #:getter content-type) - (content #:init-keyword #:content #:getter content)) +(define-method (remove-link (state <application-state>) target key value) + (let ((ret (shallow-clone state))) + (slot-set! ret 'page (remove-link (page ret) target key value)) + ret)) -(define-method (equal? (x <non-rdf-page>) (y <non-rdf-page>)) - (and (equal? (content-type x) (content-type y)) - (equal? (content x) (content y)))) +(define-method (add-link (page <page>) target key value) + (let ((ret (edit-page page))) + (when (is-a? ret <updated-page>) + (slot-set! ret 'desired-links + `((,target (,key . ,value)) + ,@(desired-links page)))) + ret)) + +(define-method (add-link (state <application-state>) target key value) + (let ((ret (shallow-clone state))) + (slot-set! ret 'page (add-link (page ret) (string->uri target) key value)) + ret)) -(define-method (add-page (state <application-state>) (identifier <string>)) +(define-method (set-page-uri (state <application-state>) uri) + (let ((ret (shallow-clone state)) + (new-page (make <loading-page> + #:uri uri))) + (slot-set! ret 'page new-page) + (add-job + ret + (format #f (G_ "Loading ~a...") (uri->string uri)) + (lambda () + (declare-link-header!) + (let ((account (main-account state)) + (client (client state))) + (parameterize ((client:client client)) + (receive (updated-account response response-body) + (client:request account (uri new-page)) + (lambda (previous-state) + (let ((ret (shallow-clone previous-state))) + ;; If the main client hasn’t changed, update it + (when (equal? (main-account previous-state) account) + (slot-set! ret 'main-account updated-account)) + ;; If the page hasn’t changed, update it + (when (equal? (page previous-state) + new-page) + (slot-set! ret 'page + (if (eqv? (response-code response) 200) + (make <loaded-page> + #:uri uri + #:etag + (match (response-etag response) + ((value . #f) value) + (else #f)) + #:links + (response-links response) + #:content-type + (response-content-type response) + #:content + (or (false-if-exception (bytevector->string response-body)) + response-body)) + (make <error-page> + #:uri uri + #:code (response-code response) + #:reason-phrase (response-reason-phrase response))))) + ret))))))))) + +(define-method (change-content-type (state <application-state>) content-type) (let ((ret (shallow-clone state))) - (slot-set! ret 'pages - `(,(make <new-page> #:identifier identifier) - ,@(pages state))) + (slot-set! + ret 'page + (let ((p (edit-page (page ret)))) + (slot-set! p 'desired-content-type content-type) + p)) ret)) -(define-method (set-page-uri (state <application-state>) (id <string>) uri) +(define-method (change-content (state <application-state>) content) (let ((ret (shallow-clone state))) - (slot-set! ret 'pages - (let replace-page ((pages (pages state)) - (untouched-pages '())) - (match pages - (() - (reverse untouched-pages)) - ((hd tl ...) - (let ((replaced - (if (equal? (identifier hd) id) - (make <loading-page> - #:identifier id - #:uri uri) - hd))) - (replace-page - tl - `(,replaced - ,@(untouched-pages)))))))) - ;; TODO: add a job to load the page… + (slot-set! + ret 'page + (let ((p (edit-page (page ret)))) + (slot-set! p 'desired-content content) + p)) ret)) -(define-method (close-page (state <application-state>) (id <string>)) +(define-method (discard-updates (page <page>)) + page) + +(define-method (discard-updates (page <updated-page>)) + (make <loaded-page> + #:uri (uri page) + #:etag (etag page) + #:links (links page) + #:content-type (content-type page) + #:content (content page))) + +(define-method (discard-updates (state <application-state>)) (let ((ret (shallow-clone state))) - (slot-set! ret 'pages - (filter (lambda (page) - (not (equal? (identifier page) id))) - (pages state))) + (slot-set! ret 'page (discard-updates (page state))) ret)) + +(define-method (commit-updates (state <application-state>)) + (let ((loading (shallow-clone state))) + (slot-set! loading 'page + (make <loading-page> + #:uri (uri (page state)))) + (if (is-a? (page state) <updated-page>) + (add-job + loading + (format #f (G_ "Updating ~a (expected ETag ~a)") + (uri->string (uri (page state))) + (etag (page state))) + (lambda () + (let ((account (main-account state)) + (client (client state))) + (parameterize ((client:client client)) + (receive (updated-account response response-body) + (client:request account (uri (page state)) + #:method 'PUT + #:headers `(,@(let ((etag (etag (page state)))) + (if etag + `((if-match . ((,etag . #f)))) + '())) + ,@(map + (lambda (link) + `(link . ,link)) + (desired-links (page state))) + (content-type . (,(desired-content-type (page state))))) + #:body (desired-content (page state))) + (lambda (previous-state) + (let ((ret (shallow-clone previous-state))) + (when (equal? (main-account previous-state) account) + (slot-set! ret 'main-account updated-account)) + (when (equal? (page previous-state) + (page loading)) + (slot-set! ret 'page + (if (eqv? (response-code response) 200) + (make <loaded-page> + #:uri (uri (page state)) + #:etag (match (response-etag response) + ((etag . #f) etag) + (else #f)) + #:links (desired-links (page state)) + #:content-type (desired-content-type (page state)) + #:content (desired-content (page state))) + (make <error-page> + #:uri (uri (page state)) + #:code (response-code response) + #:reason-phrase (response-reason-phrase response))))) + ret))))))) + state))) + +(define-method (delete-page (state <application-state>)) + (if (is-a? (page state) <page-with-uri>) + (add-job + state + (format #f (G_ "Deleting ~a (expected ETag ~a)") + (uri (page state)) + (and (is-a? (page state) <loaded-page>) + (etag (page state)))) + (lambda () + (let ((account (main-account state)) + (client (client state))) + (parameterize ((client:client client)) + (receive (updated-account response response-body) + (client:request account (uri (page state)) + #:method 'DELETE + #:headers `(,@(let ((etag (and (is-a? (page state) <loaded-page>) + (etag (page state))))) + (if etag + `((if-match . (,(etag (page state)) . #f))) + '())))) + (lambda (previous-state) + (let ((ret (shallow-clone previous-state))) + (when (equal? (main-account previous-state) account) + (slot-set! ret 'main-account updated-account)) + (when (equal? (page previous-state) + (page state)) + (slot-set! ret 'page + (make <new-page>))) + ret))))))) + state)) diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am index cbd7b0f..0d04326 100644 --- a/src/ui/Makefile.am +++ b/src/ui/Makefile.am @@ -20,4 +20,10 @@ dist_uipkgdata_DATA = \ %reldir%/account-widget.glade \ %reldir%/accounts-widget.glade \ %reldir%/authorization-prompt.glade \ - %reldir%/main-window.glade + %reldir%/main-window.glade \ + %reldir%/error-page.glade \ + %reldir%/link-widget.glade \ + %reldir%/loaded-page.glade \ + %reldir%/loading-page.glade \ + %reldir%/new-page.glade \ + %reldir%/updated-page.glade diff --git a/src/ui/error-page.glade b/src/ui/error-page.glade new file mode 100644 index 0000000..a3d7a98 --- /dev/null +++ b/src/ui/error-page.glade @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkBox" id="error_page"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkEntry" id="uri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="input_purpose">url</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">The request failed:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="code"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">404</property> + <attributes> + <attribute name="weight" value="bold"/> + <attribute name="size" value="32768"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="reason_phrase"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Not Found</property> + <attributes> + <attribute name="weight" value="bold"/> + <attribute name="size" value="16384"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> +</interface> diff --git a/src/ui/link-widget.glade b/src/ui/link-widget.glade new file mode 100644 index 0000000..cce37c4 --- /dev/null +++ b/src/ui/link-widget.glade @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkBox" id="link_widget"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLinkButton" id="target_iri"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="relief">none</property> + <property name="uri">http://glade.gnome.org</property> + <child> + <object class="GtkLabel" id="target_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">https://example.com</property> + <property name="ellipsize">end</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">rel</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">=</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="relation_type"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">type</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> diff --git a/src/ui/loaded-page.glade b/src/ui/loaded-page.glade new file mode 100644 index 0000000..9517d2f --- /dev/null +++ b/src/ui/loaded-page.glade @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkBox" id="loaded_page"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkEntry" id="uri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="input_purpose">url</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="links_list"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="content_type"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">text/turtle</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="content"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">@prefix rdf: ...</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkButton" id="button_delete"> + <property name="label" translatable="yes">Delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button_update"> + <property name="label" translatable="yes">Update…</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + </object> +</interface> diff --git a/src/ui/loading-page.glade b/src/ui/loading-page.glade new file mode 100644 index 0000000..203084f --- /dev/null +++ b/src/ui/loading-page.glade @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkBox" id="loading_page"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkEntry" id="uri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="input_purpose">url</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinner"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="active">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> diff --git a/src/ui/new-page.glade b/src/ui/new-page.glade new file mode 100644 index 0000000..b9703ff --- /dev/null +++ b/src/ui/new-page.glade @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkBox" id="new_page"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkEntry" id="uri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="input_purpose">url</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">12</property> + <property name="margin_end">12</property> + <property name="label" translatable="yes">Enter an URI in the URI bar above to start your journey.</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> diff --git a/src/ui/updated-page.glade b/src/ui/updated-page.glade new file mode 100644 index 0000000..e174c55 --- /dev/null +++ b/src/ui/updated-page.glade @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface> + <requires lib="gtk+" version="3.22"/> + <object class="GtkTextBuffer" id="content_buffer"/> + <object class="GtkBox" id="updated_page"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Content type:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="content_type_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Content:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkTextView"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="buffer">content_buffer</property> + <property name="monospace">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkButton" id="discard_button"> + <property name="label" translatable="yes">Discard edits</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="update_button"> + <property name="label" translatable="yes">Update</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + </object> +</interface> |