286 lines
8.7 KiB
C
286 lines
8.7 KiB
C
/*
|
|
webdav-gtk A WebDAV client written with gtk-3.0 and libneon
|
|
Copyright (C) 2022 Xavier Del Campo
|
|
|
|
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/>.
|
|
*/
|
|
|
|
#include <backend.h>
|
|
#include <glib.h>
|
|
#include <gtk/gtk.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static backend_session dav_session;
|
|
|
|
/* Based on https://github.com/Miqueas/c-gtk3-examples/blob/main/GtkBuilder.c
|
|
* Under zlib license:
|
|
* https://github.com/Miqueas/C-GTK3-Examples/blob/main/LICENSE
|
|
*/
|
|
|
|
void on_close(GtkAssistant *const assistant, const gpointer data)
|
|
{
|
|
gtk_widget_destroy(GTK_WIDGET(assistant));
|
|
}
|
|
|
|
#define ELEMENTS \
|
|
X(hostname) \
|
|
X(username) \
|
|
X(password)
|
|
|
|
enum
|
|
{
|
|
#define X(x) x,
|
|
ELEMENTS
|
|
#undef X
|
|
|
|
N_ELEMENTS
|
|
};
|
|
|
|
static const gchar *const names[] =
|
|
{
|
|
#define X(x) [x] = #x,
|
|
ELEMENTS
|
|
#undef X
|
|
};
|
|
|
|
void on_text_changed(GtkEntry *const entry, const gpointer user_data)
|
|
{
|
|
gboolean ready = TRUE;
|
|
GtkBuilder *const builder = GTK_BUILDER(user_data);
|
|
|
|
for (size_t i = 0; i < sizeof names / sizeof *names; i++)
|
|
{
|
|
GtkEntry *const e = GTK_ENTRY(gtk_builder_get_object(builder, names[i]));
|
|
|
|
if (!gtk_entry_get_text_length(e))
|
|
ready = FALSE;
|
|
}
|
|
|
|
GtkButton *const connect = GTK_BUTTON(
|
|
gtk_builder_get_object(builder, "connect"));
|
|
|
|
gtk_widget_set_sensitive(GTK_WIDGET(connect), ready);
|
|
}
|
|
|
|
static void conn_status(const char *const status, void *const user_data)
|
|
{
|
|
GtkBuilder *const builder = user_data;
|
|
|
|
g_signal_emit_by_name(builder, "progress-updated",
|
|
(const gchar *)status, user_data);
|
|
}
|
|
|
|
void on_conn_status(GtkBuilder *const builder, const gchar *const status,
|
|
void *const user_data)
|
|
{
|
|
GtkLabel *const label = GTK_LABEL(gtk_builder_get_object(builder,
|
|
"connection_status"));
|
|
|
|
gtk_label_set_text(label, (const gchar *)status);
|
|
}
|
|
|
|
void on_connection_finished(GtkBuilder *const builder,
|
|
const gpointer p, const gchar *const error)
|
|
{
|
|
GtkLabel *const error_label = GTK_LABEL(gtk_builder_get_object(builder,
|
|
"error_label"));
|
|
GtkSpinner *const spinner = GTK_SPINNER(gtk_builder_get_object(builder,
|
|
"spinner"));
|
|
|
|
gtk_label_set_text(error_label, error ? error : "");
|
|
gtk_spinner_stop(spinner);
|
|
|
|
const backend_session session = p;
|
|
|
|
g_debug("session=%p, error=%s (%p)", session, error, error);
|
|
|
|
if (session)
|
|
{
|
|
GtkLabel *const status = GTK_LABEL(gtk_builder_get_object(
|
|
builder, "connection_status"));
|
|
GtkAssistant *const assistant = GTK_ASSISTANT(gtk_builder_get_object(
|
|
builder, "intro_assistant"));
|
|
GtkWidget *const page1 = GTK_WIDGET(gtk_builder_get_object(
|
|
builder, "page1")),
|
|
*const page2 = GTK_WIDGET(gtk_builder_get_object(
|
|
builder, "page2"));
|
|
|
|
gtk_assistant_set_page_complete(assistant, page1, TRUE);
|
|
gtk_assistant_set_page_complete(assistant, page2, TRUE);
|
|
gtk_label_set_text(status, "Session established successfully");
|
|
gtk_label_set_text(error_label, "");
|
|
dav_session = session;
|
|
}
|
|
}
|
|
|
|
struct connect_args
|
|
{
|
|
struct backend_cfg cfg;
|
|
GtkBuilder *builder;
|
|
};
|
|
|
|
static gboolean do_connect(const gpointer args)
|
|
{
|
|
struct connect_args *const ca = args;
|
|
const struct backend_cfg *const cfg = &ca->cfg;
|
|
const char *error;
|
|
const backend_session session = backend_connect(cfg, &error);
|
|
|
|
g_signal_emit_by_name(ca->builder, "connection-finished", session, error);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void connect_free(const gpointer args)
|
|
{
|
|
free(args);
|
|
}
|
|
|
|
static gpointer connect_thread_main(const gpointer data)
|
|
{
|
|
/* Based on GNOME Developer Documentation, "Main Contexts" tutorial. */
|
|
GMainContext *const context = data;
|
|
GMainLoop *loop;
|
|
|
|
g_main_context_push_thread_default(context);
|
|
loop = g_main_loop_new(context, FALSE);
|
|
g_main_loop_run(loop);
|
|
g_main_loop_unref(loop);
|
|
|
|
g_main_context_pop_thread_default(context);
|
|
g_main_context_unref(context);
|
|
return NULL;
|
|
}
|
|
|
|
void on_connect(GtkButton *const button, const gpointer user_data)
|
|
{
|
|
GtkBuilder *const builder = GTK_BUILDER(user_data);
|
|
struct connect_args *const args = calloc(1, sizeof *args);
|
|
|
|
if (!args)
|
|
{
|
|
fprintf(stderr, "%s: calloc(3): %s\n", __func__, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
*args = (const struct connect_args)
|
|
{
|
|
.builder = builder,
|
|
.cfg =
|
|
{
|
|
.hostname = gtk_entry_get_text(GTK_ENTRY(
|
|
gtk_builder_get_object(builder, "hostname"))),
|
|
.username = gtk_entry_get_text(GTK_ENTRY(
|
|
gtk_builder_get_object(builder, "username"))),
|
|
.password = gtk_entry_get_text(GTK_ENTRY(
|
|
gtk_builder_get_object(builder, "password"))),
|
|
.use_encryption = gtk_switch_get_active(GTK_SWITCH(
|
|
gtk_builder_get_object(builder, "tls_enabled"))),
|
|
.conn_status = conn_status,
|
|
.user_data = builder
|
|
}
|
|
};
|
|
|
|
GtkSpinner *const spinner = GTK_SPINNER(gtk_builder_get_object(builder, "spinner"));
|
|
GtkLabel *const status = GTK_LABEL(gtk_builder_get_object(builder, "connection_status"));
|
|
GtkLabel *const error = GTK_LABEL(gtk_builder_get_object(builder, "error_label"));
|
|
|
|
gtk_spinner_start(spinner);
|
|
gtk_label_set_text(status, "");
|
|
gtk_label_set_text(error, "");
|
|
|
|
GMainContext *const context = g_main_context_new();
|
|
|
|
g_thread_new("backend-connect", connect_thread_main,
|
|
g_main_context_ref(context));
|
|
g_main_context_invoke_full(context, G_PRIORITY_DEFAULT, do_connect, args,
|
|
connect_free);
|
|
}
|
|
|
|
static void startup(GtkApplication *const app, const gpointer user_data)
|
|
{
|
|
GtkBuilder *const builder = gtk_builder_new();
|
|
GError *error = NULL;
|
|
|
|
if (!gtk_builder_add_from_file(builder, "ui/intro.ui", &error))
|
|
{
|
|
fprintf(stderr, "gtk_builder_add_from_file: %s\n", error->message);
|
|
return;
|
|
}
|
|
|
|
gtk_builder_connect_signals(builder, NULL);
|
|
|
|
GObject *const assistant = gtk_builder_get_object(builder, "intro_assistant"),
|
|
*const page0 = gtk_builder_get_object(builder, "page0");
|
|
|
|
for (size_t i = 0; i < sizeof names / sizeof *names; i++)
|
|
g_signal_connect(gtk_builder_get_object(builder, names[i]),
|
|
"changed", G_CALLBACK(on_text_changed), builder);
|
|
|
|
g_signal_connect(gtk_builder_get_object(builder, "connect"),
|
|
"released", G_CALLBACK(on_connect), builder);
|
|
|
|
/* Based on https://libsoup.org/gobject/howto-signals.html */
|
|
g_signal_newv("progress-updated",
|
|
GTK_TYPE_BUILDER,
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
NULL, /* closure */
|
|
NULL, /* accumulator */
|
|
NULL, /* accumulator data */
|
|
NULL, /* C marshaller */
|
|
G_TYPE_NONE, /* return type */
|
|
2, /* n_params */
|
|
(GType[]){G_TYPE_STRING, G_TYPE_POINTER});
|
|
|
|
g_signal_newv("connection-finished",
|
|
GTK_TYPE_BUILDER,
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
NULL, /* closure */
|
|
NULL, /* accumulator */
|
|
NULL, /* accumulator data */
|
|
NULL, /* C marshaller */
|
|
G_TYPE_NONE, /* return type */
|
|
2, /* n_params */
|
|
(GType[]){G_TYPE_POINTER, G_TYPE_STRING});
|
|
|
|
g_signal_connect(builder, "progress-updated",
|
|
G_CALLBACK(on_conn_status), builder);
|
|
g_signal_connect(builder, "connection-finished",
|
|
G_CALLBACK(on_connection_finished), builder);
|
|
gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant), GTK_WIDGET(page0), TRUE);
|
|
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(assistant));
|
|
}
|
|
|
|
static void activate(GtkApplication *const app, const gpointer user_data)
|
|
{
|
|
GtkWindow *const window = gtk_application_get_active_window(GTK_APPLICATION(app));
|
|
|
|
gtk_window_present(window);
|
|
}
|
|
|
|
int gui_main(const int argc, char **const argv)
|
|
{
|
|
int ret;
|
|
GtkApplication *const app = gtk_application_new("org.web.dav",
|
|
G_APPLICATION_FLAGS_NONE);
|
|
|
|
g_signal_connect(app, "startup", G_CALLBACK(startup), NULL);
|
|
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
|
ret = g_application_run(G_APPLICATION(app), argc, argv);
|
|
g_object_unref(app);
|
|
|
|
return ret;
|
|
}
|