+/** @brief Text should be visible */
+#define DETAIL_VISIBLE 1
+
+/** @brief Text should be editable */
+#define DETAIL_EDITABLE 2
+
+static GtkWidget *users_detail_generic(GtkWidget *table,
+ int *rowp,
+ const char *title,
+ GtkWidget *selector) {
+ const int row = (*rowp)++;
+ GtkWidget *const label = gtk_label_new(title);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
+ gtk_table_attach(GTK_TABLE(table),
+ label,
+ 0, 1, /* left/right_attach */
+ row, row+1, /* top/bottom_attach */
+ GTK_FILL, /* xoptions */
+ 0, /* yoptions */
+ 1, 1); /* x/ypadding */
+ gtk_table_attach(GTK_TABLE(table),
+ selector,
+ 1, 2, /* left/right_attach */
+ row, row + 1, /* top/bottom_attach */
+ GTK_EXPAND|GTK_FILL, /* xoptions */
+ GTK_FILL, /* yoptions */
+ 1, 1); /* x/ypadding */
+ return selector;
+}
+
+/** @brief Add a row to the user details table
+ * @param table Containin table widget
+ * @param rowp Pointer to row number, incremented
+ * @param title Label for this row
+ * @param value Initial value or NULL
+ * @param flags Flags word
+ * @return The text entry widget
+ */
+static GtkWidget *users_add_detail(GtkWidget *table,
+ int *rowp,
+ const char *title,
+ const char *value,
+ unsigned flags) {
+ GtkWidget *entry = gtk_entry_new();
+
+ gtk_entry_set_visibility(GTK_ENTRY(entry),
+ !!(flags & DETAIL_VISIBLE));
+ gtk_editable_set_editable(GTK_EDITABLE(entry),
+ !!(flags & DETAIL_EDITABLE));
+ if(value)
+ gtk_entry_set_text(GTK_ENTRY(entry), value);
+ return users_detail_generic(table, rowp, title, entry);
+}
+
+/** @brief Add a checkbox for a right
+ * @param table Containing table
+ * @param rowp Pointer to row number, incremented
+ * @param title Label for this row
+ * @param value Right bit (masked but not necessarily normalized)
+ * @return Checkbox widget
+ */
+static GtkWidget *users_add_right(GtkWidget *table,
+ int *rowp,
+ const char *title,
+ rights_type value) {
+ GtkWidget *check = gtk_check_button_new();
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), !!value);
+ return users_detail_generic(table, rowp, title, check);
+}
+
+/** @brief Add a checkbox for a three-right group
+ * @param table Containing table
+ * @param rowp Pointer to row number, incremented
+ * @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)
+ * @param checks Where to store triple of check widgets
+ * @return Checkbox widget
+ */
+static void users_add_right_group(GtkWidget *table,
+ int *rowp,
+ const char *title,
+ rights_type bits,
+ rights_type mask,
+ GtkWidget *checks[3]) {
+ GtkWidget *any = gtk_check_button_new_with_label("Any");
+ GtkWidget *mine = gtk_check_button_new_with_label("Own");
+ GtkWidget *random = gtk_check_button_new_with_label("Random");
+ GtkWidget *hbox = gtk_hbox_new(FALSE, 2);
+
+ /* Discard irrelevant bits */
+ bits &= mask;
+ /* Shift down to bits 0-2; the mask is always 3 contiguous bits */
+ bits /= (mask / 7);
+ /* If _ANY is set then the other two are implied; we'll take them out when we
+ * set. */
+ if(bits & 1)
+ bits = 7;
+ 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(random), !!(bits & 4));
+ 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), random, FALSE, FALSE, 0);
+ if(checks) {
+ checks[0] = any;
+ checks[1] = mine;
+ checks[2] = random;
+ }
+ users_detail_generic(table, rowp, title, hbox);
+}
+