chiark / gitweb /
login details box for disobedience. a bit unfriendly but does work.
authorRichard Kettlewell <rjk@greenend.org.uk>
Fri, 19 Oct 2007 18:24:13 +0000 (19:24 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Fri, 19 Oct 2007 18:24:13 +0000 (19:24 +0100)
disobedience/Makefile.am
disobedience/choose.c
disobedience/disobedience.c
disobedience/disobedience.h
disobedience/login.c [new file with mode: 0644]
disobedience/menu.c
disobedience/misc.c
disobedience/properties.c
disobedience/queue.c
lib/configuration.c

index d8137cdbecf192e92ae787870f3ea5dd8ebbbdbc..1f9bd88ecf797c0418a83b9f02dcd3ebeae144aa 100644 (file)
@@ -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)
index b7f77c481a824df2e0b2e35ea02121b81ca1f86c..8aa11e2117cd0fe478dc7cb8a8070b16c9402655 100644 (file)
@@ -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*/, "<root>", "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*/, "<root>", "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();
index 2a39a150aadb264ba32adc229964303a603af3ea..6271560a796887fec6cdac7e9622d65b7bfe1166 100644 (file)
@@ -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"));
index 26c2a6593d2331117f509b7da39528d3abeb75fb..37bdda41cd7ebd96db19053d4ddf5f0e8fbd0aa8 100644 (file)
@@ -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 (file)
index 0000000..729597b
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/stat.h>
+
+/** @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:
+*/
index ad79a9206931fe60d97a3618835ac804d0c5b2f7..db51ba8f5783cfb7a1026895d97a5520aacd48ae 100644 (file)
@@ -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 *)"<Branch>", 0 },
+    { (char *)"/File/Login", (char *)"<CTRL>L", login, 0,
+      0, 0 },
     { (char *)"/File/Quit Disobedience", (char *)"<CTRL>Q", quit_program, 0,
       (char *)"<StockItem>", GTK_STOCK_QUIT },
     { (char *)"/Edit", 0,  0, 0, (char *)"<Branch>", 0 },
index 3221e182f8287f2a948723f55f68197a513292da..1cb6d5a0439a839ff4b2dd1f937ffd3b9d8197bb 100644 (file)
@@ -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
index 3c252924f038026ab077bd839ab13647599e0ccc..aff768eb5290fb19541593a5b165c5b492403854 100644 (file)
@@ -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
index 80ca4a6e5534eed01d9246d8b227a557b3898345..dc36ceabed8b68493bef5fdd0eb27b3a017442d4 100644 (file)
@@ -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);
 }
index 35573c5c15dc6d03c26248315cc509132f21facb..c1bb8fcab323d6d77f2e87f68169f7be0d76b301 100644 (file)
@@ -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;
 }