+/** @brief Add a checkbox for a right
+ * @param title Label for this row
+ * @param value Current value
+ * @param right Right bit
+ */
+static void users_add_right(const char *title,
+ rights_type value,
+ rights_type right) {
+ GtkWidget *check;
+ GtkWidget **checkp = &users_details_rights[leftmost_bit(right)];
+
+ if(!(check = *checkp)) {
+ *checkp = check = gtk_check_button_new_with_label("");
+ users_detail_generic(title, check);
+ }
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), !!(value & right));
+}
+
+/** @brief Set sensitivity of particular mine/random rights bits */
+static void users_details_sensitize(rights_type r) {
+ const int bit = leftmost_bit(r);
+ const GtkWidget *all = users_details_rights[bit];
+ const int sensitive = (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(all))
+ && users_mode != MODE_NONE);
+
+ gtk_widget_set_sensitive(users_details_rights[bit + 1], sensitive);
+ gtk_widget_set_sensitive(users_details_rights[bit + 2], sensitive);
+}
+
+/** @brief Set sensitivity of everything in sight */
+static void users_details_sensitize_all(void) {
+ int n;
+ const char *report = 0;
+
+ for(n = 0; n < 32; ++n)
+ if(users_details_rights[n])
+ gtk_widget_set_sensitive(users_details_rights[n], users_mode != MODE_NONE);
+ gtk_widget_set_sensitive(users_details_name, users_mode != MODE_NONE);
+ gtk_widget_set_sensitive(users_details_email, users_mode != MODE_NONE);
+ gtk_widget_set_sensitive(users_details_password, users_mode != MODE_NONE);
+ gtk_widget_set_sensitive(users_details_password2, users_mode != MODE_NONE);
+ users_details_sensitize(RIGHT_MOVE_ANY);
+ users_details_sensitize(RIGHT_REMOVE_ANY);
+ users_details_sensitize(RIGHT_SCRATCH_ANY);
+ int apply_sensitive = 1;
+ if(users_mode == MODE_NONE)
+ apply_sensitive = 0;
+ else {
+ const char *name = gtk_entry_get_text(GTK_ENTRY(users_details_name));
+ const char *email = gtk_entry_get_text(GTK_ENTRY(users_details_email));
+ const char *pw = gtk_entry_get_text(GTK_ENTRY(users_details_password));
+ const char *pw2 = gtk_entry_get_text(GTK_ENTRY(users_details_password2));
+ /* Username must be filled in */
+ if(!*name) {
+ apply_sensitive = 0;
+ if(!report)
+ report = "Must fill in username";
+ }
+ /* Passwords must be nontrivial and match */
+ if(!*pw) {
+ apply_sensitive = 0;
+ if(!report)
+ report = "Must fill in password";
+ }
+ if(strcmp(pw, pw2)) {
+ apply_sensitive = 0;
+ if(!report)
+ report = "Passwords must match";
+ }
+ /* Email address must be somewhat valid */
+ if(*email) {
+ if(!email_valid(email)) {
+ apply_sensitive = 0;
+ report = "Invalid email address";
+ }
+ }
+ }
+ gtk_widget_set_sensitive(users_apply_button, apply_sensitive);
+ gtk_widget_set_sensitive(users_delete_button, !!users_selected);
+ users_set_report(report);
+}
+
+/** @brief Called when an _ALL widget is toggled
+ *
+ * Modifies sensitivity of the corresponding _MINE and _RANDOM widgets. We
+ * just do the lot rather than trying to figure out which one changed,
+ */
+static void users_any_toggled(GtkToggleButton attribute((unused)) *togglebutton,
+ gpointer attribute((unused)) user_data) {
+ users_details_sensitize_all();
+}
+
+/** @brief Add a checkbox for a three-right group
+ * @param title Label for this row
+ * @param bits Rights bits (not masked or normalized)
+ * @param mask Mask for this group (must be 7*2^n)
+ */
+static void users_add_right_group(const char *title,
+ rights_type bits,
+ rights_type mask) {
+ const uint32_t first = mask / 7;
+ const int bit = leftmost_bit(first);
+ GtkWidget **widgets = &users_details_rights[bit], *any, *mine, *rnd;
+
+ if(!*widgets) {
+ GtkWidget *hbox = gtk_hbox_new(FALSE, 2);
+
+ any = widgets[0] = gtk_check_button_new_with_label("Any");
+ mine = widgets[1] = gtk_check_button_new_with_label("Own");
+ rnd = widgets[2] = gtk_check_button_new_with_label("Random");
+ gtk_box_pack_start(GTK_BOX(hbox), any, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), mine, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), rnd, FALSE, FALSE, 0);
+ users_detail_generic(title, hbox);
+ g_signal_connect(any, "toggled", G_CALLBACK(users_any_toggled), NULL);
+ users_details_rights[bit] = any;
+ users_details_rights[bit + 1] = mine;
+ users_details_rights[bit + 2] = rnd;
+ } else {
+ any = widgets[0];
+ mine = widgets[1];
+ rnd = widgets[2];
+ }
+ /* Discard irrelevant bits */
+ bits &= mask;
+ /* Shift down to bits 0-2; the mask is always 3 contiguous bits */
+ bits >>= bit;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(any), !!(bits & 1));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mine), !!(bits & 2));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rnd), !!(bits & 4));