From 73f1b9f30c98dc525a5b6a540f6f135855d640a0 Mon Sep 17 00:00:00 2001 Message-Id: <73f1b9f30c98dc525a5b6a540f6f135855d640a0.1714863736.git.mdw@distorted.org.uk> From: Mark Wooding Date: Fri, 19 Oct 2007 19:24:13 +0100 Subject: [PATCH] login details box for disobedience. a bit unfriendly but does work. Organization: Straylight/Edgeware From: Richard Kettlewell --- disobedience/Makefile.am | 2 +- disobedience/choose.c | 14 ++- disobedience/disobedience.c | 31 +++++- disobedience/disobedience.h | 24 ++++ disobedience/login.c | 215 ++++++++++++++++++++++++++++++++++++ disobedience/menu.c | 11 +- disobedience/misc.c | 27 +++++ disobedience/properties.c | 28 +++-- disobedience/queue.c | 3 + lib/configuration.c | 2 + 10 files changed, 336 insertions(+), 21 deletions(-) create mode 100644 disobedience/login.c diff --git a/disobedience/Makefile.am b/disobedience/Makefile.am index d8137cd..1f9bd88 100644 --- a/disobedience/Makefile.am +++ b/disobedience/Makefile.am @@ -25,7 +25,7 @@ AM_CFLAGS=$(GLIB_CFLAGS) $(GTK_CFLAGS) disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c \ choose.c misc.c style.h control.c properties.c menu.c \ - log.c progress.c \ + log.c progress.c login.c \ ../lib/memgc.c disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) disobedience_LDFLAGS=$(GTK_LIBS) diff --git a/disobedience/choose.c b/disobedience/choose.c index b7f77c4..8aa11e2 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -1288,6 +1288,15 @@ static const struct tabtype tabtype_choose = { /* Public entry points ----------------------------------------------------- */ +/** @brief Called to entirely reset the choose screen */ +static void choose_reset(void) { + if(root) + undisplay_tree(root); + root = newnode(0/*parent*/, "", "All files", "", + CN_EXPANDABLE, fill_root_node); + expand_node(root, 0); /* will call redisplay_tree */ +} + /** @brief Create a track choice widget */ GtkWidget *choose_widget(void) { int n; @@ -1354,9 +1363,8 @@ GtkWidget *choose_widget(void) { * namespace */ NW(layout); chooselayout = gtk_layout_new(0, 0); - root = newnode(0/*parent*/, "", "All files", "", - CN_EXPANDABLE, fill_root_node); - expand_node(root, 0); /* will call redisplay_tree */ + choose_reset(); + register_reset(choose_reset); /* Create the popup menus */ NW(menu); track_menu = gtk_menu_new(); diff --git a/disobedience/disobedience.c b/disobedience/disobedience.c index 2a39a15..6271560 100644 --- a/disobedience/disobedience.c +++ b/disobedience/disobedience.c @@ -49,6 +49,9 @@ GtkWidget *tabs; /** @brief Main client */ disorder_eclient *client; +/** @brief Log client */ +disorder_eclient *logclient; + /** @brief Last reported state * * This is updated by log_state(). @@ -75,6 +78,12 @@ static int nop_in_flight; /** @brief Global tooltip group */ GtkTooltips *tips; +/** @brief Linked list of functions to call when we reset login parameters */ +static struct reset_callback_node { + struct reset_callback_node *next; + reset_callback *callback; +} *resets; + /* Window creation --------------------------------------------------------- */ /* Note that all the client operations kicked off from here will only complete @@ -345,9 +354,28 @@ static void version(void) { exit(0); } +/* reset state */ +void reset(void) { + struct reset_callback_node *r; + + /* reset the clients */ + disorder_eclient_close(client); + disorder_eclient_close(logclient); + for(r = resets; r; r = r->next) + r->callback(); +} + +/** @brief Register a reset callback */ +void register_reset(reset_callback *callback) { + struct reset_callback_node *const r = xmalloc(sizeof *r); + + r->next = resets; + r->callback = callback; + resets = r; +} + int main(int argc, char **argv) { int n; - disorder_eclient *logclient; mem_init(); /* garbage-collect PCRE's memory */ @@ -401,6 +429,7 @@ int main(int argc, char **argv) { maybe_send_nop, 0/*data*/, 0/*notify*/); + register_reset(properties_reset); /* Start monitoring the log */ disorder_eclient_log(logclient, &log_callbacks, 0); D(("enter main loop")); diff --git a/disobedience/disobedience.h b/disobedience/disobedience.h index 26c2a65..37bdda4 100644 --- a/disobedience/disobedience.h +++ b/disobedience/disobedience.h @@ -90,6 +90,13 @@ struct tabtype { void (*selectall_activate)(GtkWidget *tab); }; +/** @brief Button definitions */ +struct button { + const gchar *stock; + void (*clicked)(GtkButton *button, gpointer userdata); + const char *tip; +}; + /* Variables --------------------------------------------------------------- */ extern GMainLoop *mainloop; @@ -121,6 +128,8 @@ void popup_protocol_error(int code, void properties(int ntracks, const char **tracks); /* Pop up a properties window for a list of tracks */ +void properties_reset(void); + GtkWidget *scroll_widget(GtkWidget *child, const char *name); /* Wrap a widget up for scrolling */ @@ -131,6 +140,8 @@ GdkPixbuf *find_image(const char *name); void popup_error(const char *msg); /* Pop up an error message */ +void fpopup_error(const char *fmt, ...); + struct progress_window *progress_window_new(const char *title); /* Pop up a progress window */ @@ -141,11 +152,22 @@ void progress_window_progress(struct progress_window *pw, GtkWidget *iconbutton(const char *path, const char *tip); +GtkWidget *create_buttons(const struct button *buttons, + size_t nbuttons); + void register_monitor(monitor_callback *callback, void *u, unsigned long mask); /* Register a state monitor */ +/** @brief Type signature for a reset callback */ +typedef void reset_callback(void); + +void register_reset(reset_callback *callback); +/* Register a reset callback */ + +void reset(void); + void all_update(void); /* Update everything */ @@ -204,6 +226,8 @@ GtkWidget *choose_widget(void); void choose_update(void); /* Called when we think the choose tree might need updating */ +void login_box(void); + /* Widget leakage debugging rubbish ---------------------------------------- */ #if MDEBUG diff --git a/disobedience/login.c b/disobedience/login.c new file mode 100644 index 0000000..729597b --- /dev/null +++ b/disobedience/login.c @@ -0,0 +1,215 @@ +/* + * This file is part of DisOrder + * Copyright (C) 2007 Richard Kettlewell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file disobedience/login.c + * @brief Login box for Disobedience + */ + +#include "disobedience.h" +#include "split.h" +#include "filepart.h" +#include +#include + +/** @brief One field in the login window */ +struct login_window_item { + /** @brief Description label */ + const char *description; + + /** @brief Return the current value */ + const char *(*get)(void); + + /** @brief Set a new value */ + void (*set)(const char *value); + + /** @brief Flags + * + * - @ref LWI_HIDDEN - this is a password + */ + unsigned flags; + +}; + +/** @brief This is a password */ +#define LWI_HIDDEN 0x0001 + +/** @brief Current login window */ +static GtkWidget *login_window; + +/** @brief Set connection defaults */ +static void default_connect(void) { + if(!config->connect.n) { + config->connect.n = 2; + config->connect.s = xcalloc(2, sizeof (char *)); + config->connect.s[0] = xstrdup("localhost"); + config->connect.s[1] = xstrdup("9999"); /* whatever */ + } +} + +static const char *get_hostname(void) { return config->connect.s[0]; } +static const char *get_service(void) { return config->connect.s[1]; } +static const char *get_username(void) { return config->username; } +static const char *get_password(void) { return config->password; } + +static void set_hostname(const char *s) { config->connect.s[0] = (char *)s; } +static void set_service(const char *s) { config->connect.s[1] = (char *)s; } +static void set_username(const char *s) { config->username = s; } +static void set_password(const char *s) { config->password = s; } + +/** @brief Table used to generate the form */ +static const struct login_window_item lwis[] = { + { "Hostname", get_hostname, set_hostname, 0 }, + { "Service", get_service, set_service, 0 }, + { "User name", get_username, set_username, 0 }, + { "Password", get_password, set_password, LWI_HIDDEN }, +}; +#define NLWIS (sizeof lwis / sizeof *lwis) + +static GtkWidget *lwi_entry[NLWIS]; + +static void update_config(void) { + size_t n; + + for(n = 0; n < NLWIS; ++n) + lwis[n].set(gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]))); +} + +static void login_ok(GtkButton attribute((unused)) *button, + gpointer attribute((unused)) userdata) { + update_config(); + reset(); +} + +static void login_save(GtkButton attribute((unused)) *button, + gpointer attribute((unused)) userdata) { + char *path = config_userconf(0, 0), *tmp; + FILE *fp; + + update_config(); + byte_xasprintf(&tmp, "%s.tmp", path); + /* Make sure the directory exists; don't care if it already exists. */ + mkdir(d_dirname(tmp), 02700); + /* Write out the file */ + if(!(fp = fopen(tmp, "w"))) { + fpopup_error("error opening %s: %s", tmp, strerror(errno)); + return; + } + if(fprintf(fp, "username %s\n" + "password %s\n" + "connect %s %s\n", + quoteutf8(config->username), + quoteutf8(config->password), + quoteutf8(config->connect.s[0]), + quoteutf8(config->connect.s[1])) < 0) { + fpopup_error("error writing to %s: %s", tmp, strerror(errno)); + fclose(fp); + return; + } + if(fclose(fp) < 0) { + fpopup_error("error closing %s: %s", tmp, strerror(errno)); + return; + } + /* Rename into place */ + if(rename(tmp, path) < 0) { + fpopup_error("error renaming %s: %s", tmp, strerror(errno)); + return; + } +} + +static void login_cancel(GtkButton attribute((unused)) *button, + gpointer attribute((unused)) userdata) { + gtk_widget_destroy(login_window); +} + +/* Buttons that appear at the bottom of the window */ +static const struct button buttons[] = { + { + GTK_STOCK_OK, + login_ok, + "Login with these settings", + }, + { + GTK_STOCK_SAVE, + login_save, + "Save these settings", + }, + { + GTK_STOCK_CANCEL, + login_cancel, + "Discard all changes and close window" + }, +}; + +#define NBUTTONS (int)(sizeof buttons / sizeof *buttons) + +/** @brief Pop up a login box */ +void login_box(void) { + GtkWidget *table, *label, *entry, *buttonbox, *vbox; + size_t n; + + /* If there's one already then bring it to the front */ + if(login_window) { + gtk_window_present(GTK_WINDOW(login_window)); + return; + } + default_connect(); + /* Create a new login window */ + login_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(login_window, "destroy", + G_CALLBACK(gtk_widget_destroyed), &login_window); + gtk_window_set_title(GTK_WINDOW(login_window), "Login Details"); + /* Construct the form */ + table = gtk_table_new(NLWIS + 1/*rows*/, 2/*columns*/, FALSE/*homogenous*/); + for(n = 0; n < NLWIS; ++n) { + label = gtk_label_new(lwis[n].description); + gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/); + gtk_table_attach(GTK_TABLE(table), label, + 0, 1, /* left/right_attach */ + n, n+1, /* top/bottom_attach */ + GTK_FILL, 0, /* x/yoptions */ + 1, 1); /* x/ypadding */ + entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry), + lwis[n].flags & LWI_HIDDEN ? FALSE : TRUE); + gtk_entry_set_text(GTK_ENTRY(entry), lwis[n].get()); + gtk_table_attach(GTK_TABLE(table), entry, + 1, 2, /* left/right_attach */ + n, n+1, /* top/bottom_attach */ + GTK_EXPAND|GTK_FILL, 0, /* x/yoptions */ + 1, 1); /* x/ypadding */ + lwi_entry[n] = entry; + } + buttonbox = create_buttons(buttons, NBUTTONS); + vbox = gtk_vbox_new(FALSE, 1); + gtk_box_pack_start(GTK_BOX(vbox), table, + TRUE/*expand*/, TRUE/*fill*/, 1/*padding*/); + gtk_box_pack_start(GTK_BOX(vbox), buttonbox, + FALSE/*expand*/, FALSE/*fill*/, 1/*padding*/); + gtk_container_add(GTK_CONTAINER(login_window), vbox); + gtk_widget_show_all(login_window); +} + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/disobedience/menu.c b/disobedience/menu.c index ad79a92..db51ba8 100644 --- a/disobedience/menu.c +++ b/disobedience/menu.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2006 Richard Kettlewell + * Copyright (C) 2006, 2007 Richard Kettlewell * * 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 @@ -69,6 +69,13 @@ static void properties_item(gpointer attribute((unused)) callback_data, t->properties_activate(tab); } +/** @brief Called when the login option is activated */ +static void login(gpointer attribute((unused)) callback_data, + guint attribute((unused)) callback_action, + GtkWidget attribute((unused)) *menu_item) { + login_box(); +} + /** @brief Update menu state * * Determines option sensitivity according to the current tab and adjusts the @@ -130,6 +137,8 @@ static void about_popup_got_version(void attribute((unused)) *v, GtkWidget *menubar(GtkWidget *w) { static const GtkItemFactoryEntry entries[] = { { (char *)"/File", 0, 0, 0, (char *)"", 0 }, + { (char *)"/File/Login", (char *)"L", login, 0, + 0, 0 }, { (char *)"/File/Quit Disobedience", (char *)"Q", quit_program, 0, (char *)"", GTK_STOCK_QUIT }, { (char *)"/Edit", 0, 0, 0, (char *)"", 0 }, diff --git a/disobedience/misc.c b/disobedience/misc.c index 3221e18..1cb6d5a 100644 --- a/disobedience/misc.c +++ b/disobedience/misc.c @@ -106,6 +106,17 @@ void popup_error(const char *msg) { gtk_widget_destroy(w); } +/** @brief Pop up an error message */ +void fpopup_error(const char *fmt, ...) { + va_list ap; + char *msg; + + va_start(ap, fmt); + byte_xvasprintf(&msg, fmt, ap); + va_end(ap); + popup_error(msg); +} + /** @brief Create a button with an icon in it * @param path (relative) path to image * @param tooltip Tooltip or NULL to not set one @@ -130,6 +141,22 @@ GtkWidget *iconbutton(const char *path, const char *tip) { return button; } +/** @brief Create buttons and pack them into an hbox */ +GtkWidget *create_buttons(const struct button *buttons, + size_t nbuttons) { + size_t n; + GtkWidget *const hbox = gtk_hbox_new(FALSE, 1); + + for(n = 0; n < nbuttons; ++n) { + GtkWidget *const button = gtk_button_new_from_stock(buttons[n].stock); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(buttons[n].clicked), 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); + gtk_tooltips_set_tip(tips, button, buttons[n].tip, ""); + } + return hbox; +} + /* Local Variables: c-basic-offset:2 diff --git a/disobedience/properties.c b/disobedience/properties.c index 3c25292..aff768e 100644 --- a/disobedience/properties.c +++ b/disobedience/properties.c @@ -127,11 +127,7 @@ static const struct pref { #define NPREFS (int)(sizeof prefs / sizeof *prefs) /* Buttons that appear at the bottom of the window */ -static const struct button { - const gchar *stock; - void (*clicked)(GtkButton *button, gpointer userdata); - const char *tip; -} buttons[] = { +static const struct button buttons[] = { { GTK_STOCK_OK, properties_ok, @@ -174,7 +170,7 @@ static void propagate_clicked(GtkButton attribute((unused)) *button, void properties(int ntracks, const char **tracks) { int n, m; struct prefdata *f; - GtkWidget *hbox, *vbox, *button, *label, *entry, *propagate, *content; + GtkWidget *buttonbox, *vbox, *label, *entry, *propagate, *content; GdkPixbuf *pb; /* If no tracks, do nothign */ @@ -266,21 +262,14 @@ void properties(int ntracks, const char **tracks) { } prefs_unfilled = prefs_total; /* Buttons */ - hbox = gtk_hbox_new(FALSE, 1); - for(n = 0; n < NBUTTONS; ++n) { - button = gtk_button_new_from_stock(buttons[n].stock); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(buttons[n].clicked), 0); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); - gtk_tooltips_set_tip(tips, button, buttons[n].tip, ""); - } + buttonbox = create_buttons(buttons, NBUTTONS); /* Put it all together */ vbox = gtk_vbox_new(FALSE, 1); gtk_box_pack_start(GTK_BOX(vbox), scroll_widget(properties_table, "properties"), TRUE, TRUE, 1); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 1); + gtk_box_pack_start(GTK_BOX(vbox), buttonbox, FALSE, FALSE, 1); gtk_container_add(GTK_CONTAINER(properties_window), vbox); /* The table only really wants to be vertically scrollable */ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(GTK_WIDGET(properties_table)->parent->parent), @@ -499,6 +488,15 @@ static void properties_cancel(GtkButton attribute((unused)) *button, gtk_widget_destroy(properties_window); } +/** @brief Called on client reset + * + * Destroys the current properties window. + */ +void properties_reset(void) { + if(properties_window) + gtk_widget_destroy(properties_window); +} + /* Local Variables: c-basic-offset:2 diff --git a/disobedience/queue.c b/disobedience/queue.c index 80ca4a6..dc36cea 100644 --- a/disobedience/queue.c +++ b/disobedience/queue.c @@ -1313,6 +1313,7 @@ GtkWidget *queue_widget(void) { g_timeout_add(1000/*ms*/, adjust_sofar, 0); /* Arrange a callback whenever the playing state changes */ register_monitor(playing_update, 0, DISORDER_PLAYING|DISORDER_TRACK_PAUSED); + register_reset(queue_update); /* We pass choose_update() as our notify function since the choose screen * marks tracks that are playing/in the queue. */ return queuelike(&ql_queue, fixup_queue, choose_update, queue_menu, @@ -1367,6 +1368,7 @@ static struct queue_menuitem recent_menu[] = { /** @brief Create the recently-played list */ GtkWidget *recent_widget(void) { D(("recent_widget")); + register_reset(recent_update); return queuelike(&ql_recent, fixup_recent, 0, recent_menu, "recent", maincolumns, NMAINCOLUMNS); } @@ -1399,6 +1401,7 @@ static struct queue_menuitem added_menu[] = { /** @brief Create the newly-added list */ GtkWidget *added_widget(void) { D(("added_widget")); + register_reset(added_update); return queuelike(&ql_added, 0/*fixup*/, 0/*notify*/, added_menu, "added", addedcolumns, NADDEDCOLUMNS); } diff --git a/lib/configuration.c b/lib/configuration.c index 35573c5..c1bb8fc 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -1186,6 +1186,8 @@ char *config_private(void) { char *config_userconf(const char *home, const struct passwd *pw) { char *s; + if(!home && !pw && !(pw = getpwuid(getuid()))) + fatal(0, "cannot determine our username"); byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir); return s; } -- [mdw]