first try to journal_store_get_latest_id() for sqlite
[k8lowj.git] / src / pollcreator.c
blob6534e0d1a2fd7a351dbecc3b0b9d7055e3bf8a42
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
3 */
4 #include "gtk-all.h"
5 #include "util-gtk.h"
7 #include <stdlib.h>
9 #include "conf.h"
10 #include "jam.h"
11 #include "pollcreator.h"
12 #include "poll.h"
13 #include "util.h"
16 typedef struct _PollDlg PollDlg;
19 struct _PollDlg {
20 GtkDialog dlg; /* Parent widget */
21 GtkWidget *pollname, *viewers, *voters;
22 JamReorderable questions;
26 #define POLLDLG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), polldlg_get_type(), PollDlg))
29 /* macro to generate prototypes for type and instance init functions, as
30 * well as the actual type function, in one swell foop. */
31 #define MAKETYPE(fname, newtypeclass, classinitf, instanceinitf, parenttype, parentclass) \
32 static void instanceinitf(GtkWidget *w); \
33 static GType fname (void) G_GNUC_CONST; \
34 static GType \
35 fname(void) { \
36 static GType new_type = 0; \
37 if (!new_type) { \
38 const GTypeInfo new_info = { \
39 sizeof (parentclass), \
40 NULL, \
41 NULL, \
42 (GClassInitFunc)classinitf, \
43 NULL, \
44 NULL, \
45 sizeof (newtypeclass), \
46 0, \
47 (GInstanceInitFunc) instanceinitf, \
48 }; \
49 new_type = g_type_register_static(parenttype, \
50 #newtypeclass, &new_info, 0); \
51 } \
52 return new_type; \
55 #define MENU_ADD(menu, mnemonic) gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_menu_item_new_with_mnemonic(mnemonic));
58 /* Poll Creator container */
59 MAKETYPE(polldlg_get_type, PollDlg, NULL, polldlg_init, GTK_TYPE_DIALOG, GtkDialogClass)
62 static GtkWidget *make_pollmeta (PollDlg *pdlg) {
63 GtkWidget *hbox, *vbox, *menu;
65 vbox = gtk_vbox_new(FALSE, 6);
67 pdlg->pollname = gtk_entry_new();
68 gtk_box_pack_start(GTK_BOX(vbox), labelled_box_new(_("Poll _name: "), pdlg->pollname), FALSE, FALSE, 0);
70 hbox = gtk_hbox_new(FALSE, 18);
72 pdlg->voters = gtk_option_menu_new();
73 menu = gtk_menu_new();
74 MENU_ADD(menu, _("All users"));
75 MENU_ADD(menu, _("Friends"));
76 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg->voters), menu);
77 gtk_box_pack_start(GTK_BOX(hbox), labelled_box_new(_("V_oters:"), pdlg->voters), FALSE, FALSE, 0);
79 pdlg->viewers = gtk_option_menu_new();
80 menu = gtk_menu_new();
81 MENU_ADD(menu, _("All users"));
82 MENU_ADD(menu, _("Friends"));
83 MENU_ADD(menu, _("Nobody"));
84 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg->viewers), menu);
85 gtk_box_pack_end(GTK_BOX(hbox), labelled_box_new(_("_Results visible to:"), pdlg->viewers), FALSE, FALSE, 0);
87 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
89 return vbox;
93 static void add_question (PollDlg *pdlg, PollQuestion *pq) {
94 GtkTreeIter iter;
95 gtk_list_store_append(pdlg->questions.store, &iter);
96 gtk_list_store_set(pdlg->questions.store, &iter, 0, pq->question, 1, pq, -1);
100 static void add_multi_cb (PollDlg *pdlg) {
101 PollQuestion *pq;
102 pq = pollmultidlg_run(GTK_WINDOW(pdlg), NULL);
103 if (pq) add_question(pdlg, pq);
107 static void add_scale_cb (PollDlg *pdlg) {
108 PollQuestion *pq;
109 pq = pollscaledlg_run(GTK_WINDOW(pdlg), NULL);
110 if (pq) add_question(pdlg, pq);
114 static void add_text_cb (PollDlg *pdlg) {
115 PollQuestion *pq;
116 pq = polltextdlg_run(GTK_WINDOW(pdlg), NULL);
117 if (pq) add_question(pdlg, pq);
121 static void add_question_cb (PollDlg *pdlg) {
122 GtkWidget *menu, *item;
124 menu = gtk_menu_new();
126 item = gtk_menu_item_new_with_label(_("Multiple Choice Question"));
127 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(add_multi_cb), pdlg);
128 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
130 item = gtk_menu_item_new_with_label(_("Range Question"));
131 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(add_scale_cb), pdlg);
132 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
134 item = gtk_menu_item_new_with_label(_("Text Question"));
135 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(add_text_cb), pdlg);
136 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
138 gtk_widget_show_all(menu);
140 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0 /* don't know button */ , gtk_get_current_event_time());
144 static void edit_question_cb (PollDlg *pdlg) {
145 GtkTreeSelection *sel;
146 GtkTreeModel *model;
147 GtkTreeIter iter;
148 PollQuestion *pq;
150 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg->questions.view));
152 if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return;
153 gtk_tree_model_get(model, &iter, 1, &pq, -1);
154 switch (pq->type) {
155 case PQ_RADIO:
156 case PQ_COMBO:
157 case PQ_CHECK:
158 pq = pollmultidlg_run(GTK_WINDOW(pdlg), (PollQuestionMulti *) pq);
159 break;
160 case PQ_TEXT:
161 pq = polltextdlg_run(GTK_WINDOW(pdlg), (PollQuestionText *) pq);
162 break;
163 case PQ_SCALE:
164 pq = pollscaledlg_run(GTK_WINDOW(pdlg), (PollQuestionScale *) pq);
165 break;
166 default:
167 return;
169 if (pq) gtk_list_store_set(pdlg->questions.store, &iter, 0, pq->question, 1, pq, -1);
173 static void remove_question_cb (PollDlg *pdlg) {
174 GtkTreeSelection *sel;
175 GtkTreeModel *model;
176 GtkTreeIter iter;
177 PollQuestion *pq;
178 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg->questions.view));
179 if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return;
180 gtk_tree_model_get(model, &iter, 1, &pq, -1);
181 poll_question_free(pq);
182 gtk_list_store_remove(pdlg->questions.store, &iter);
186 static void make_list (PollDlg *pdlg) {
187 GtkCellRenderer *renderer;
188 GtkTreeViewColumn *column;
190 pdlg->questions.store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
191 jam_reorderable_make(&pdlg->questions);
192 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pdlg->questions.view), FALSE);
193 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pdlg->questions.view), TRUE);
194 g_object_unref(pdlg->questions.store);
196 renderer = gtk_cell_renderer_text_new();
197 column = gtk_tree_view_column_new();
198 gtk_tree_view_column_pack_start(column, renderer, TRUE);
199 gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
200 gtk_tree_view_append_column(GTK_TREE_VIEW(pdlg->questions.view), column);
202 g_signal_connect_data(G_OBJECT(pdlg->questions.add), "clicked", G_CALLBACK(add_question_cb), pdlg, NULL, G_CONNECT_AFTER|G_CONNECT_SWAPPED);
203 g_signal_connect_swapped(G_OBJECT(pdlg->questions.edit), "clicked", G_CALLBACK(edit_question_cb), pdlg);
204 g_signal_connect_swapped(G_OBJECT(pdlg->questions.remove), "clicked", G_CALLBACK(remove_question_cb), pdlg);
208 static GtkWidget *make_content (PollDlg *pdlg) {
209 GtkWidget *vbox;
210 GtkWidget *label;
212 vbox = gtk_vbox_new(FALSE, 6);
213 label = gtk_label_new_with_mnemonic(_("Poll _Questions:"));
214 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
215 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
217 make_list(pdlg);
218 gtk_label_set_mnemonic_widget(GTK_LABEL(label), pdlg->questions.view);
219 gtk_box_pack_start(GTK_BOX(vbox), pdlg->questions.box, TRUE, TRUE, 0);
221 return vbox;
225 static void polldlg_init (GtkWidget *w) {
226 PollDlg *pdlg = POLLDLG(w);
227 GtkWidget *vbox;
228 gtk_window_set_title(GTK_WINDOW(pdlg), _("Poll Creator"));
229 vbox = gtk_vbox_new(FALSE, 12);
230 gtk_box_pack_start(GTK_BOX(vbox), make_pollmeta(pdlg), FALSE, FALSE, 0);
231 gtk_box_pack_start(GTK_BOX(vbox), make_content(pdlg), TRUE, TRUE, 0);
232 gtk_dialog_add_buttons(GTK_DIALOG(pdlg), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, _("_Generate"), GTK_RESPONSE_OK, NULL);
233 jam_dialog_set_contents(GTK_DIALOG(pdlg), vbox);
237 static GtkWidget *polldlg_new (GtkWidget *parent) {
238 PollDlg *pdlg = POLLDLG(g_object_new(polldlg_get_type(), NULL));
239 gtk_window_set_transient_for(GTK_WINDOW(pdlg), GTK_WINDOW(parent));
240 return GTK_WIDGET(pdlg);
244 static void generate_multi (PollQuestion *pq, GString *ptext) {
245 static const char *qtype_to_string[] = {
246 "radio",
247 "drop",
248 "check"
250 PollQuestionMulti *pqm = (PollQuestionMulti *)pq;
251 GSList *l;
252 g_string_append_printf(ptext, "<lj-pq type=\"%s\">\n", qtype_to_string[pq->type]);
253 g_string_append_printf(ptext, "%s\n", pq->question);
254 for (l = pqm->answers; l; l = l->next) g_string_append_printf(ptext, "<lj-pi>%s</lj-pi>\n", (char *)l->data);
255 g_string_append(ptext, "</lj-pq>\n\n");
259 static void generate_text (PollQuestion *pq, GString *ptext) {
260 PollQuestionText *pqt = (PollQuestionText *) pq;
261 g_string_append_printf(ptext, "<lj-pq type=\"text\"");
262 if (pqt->size) g_string_append_printf(ptext, " size=\"%d\"", pqt->size);
263 if (pqt->width) g_string_append_printf(ptext, " maxlength=\"%d\"", pqt->width);
264 g_string_append_printf(ptext, ">\n%s\n</lj-pq>\n\n", pq->question);
268 static void generate_scale (PollQuestion *pq, GString *ptext) {
269 PollQuestionScale *pqs = (PollQuestionScale *) pq;
270 g_string_append_printf(ptext, "<lj-pq type=\"scale\" from=\"%d\" to=\"%d\" by=\"%d\">\n%s\n</lj-pq>\n\n", pqs->from, pqs->to, pqs->by, pq->question);
274 static GString *poll_to_text (Poll *poll) {
275 static const char *psec_to_string[] = {
276 "all",
277 "friends",
278 "none"
280 GString *ptext;
281 GSList *l;
282 ptext = g_string_sized_new(2048);
283 /* poll open tag */
284 g_string_append(ptext, "<lj-poll ");
285 if (poll->name) g_string_append_printf(ptext, "name=\"%s\" ", poll->name);
286 /* ...with metadata */
287 g_string_append_printf(ptext, "whoview=\"%s\" ", psec_to_string[poll->viewers]);
288 g_string_append_printf(ptext, "whovote=\"%s\">\n\n", psec_to_string[poll->voters]);
289 /* poll questions */
290 for (l = poll->questions; l; l = l->next) {
291 PollQuestion *pq = l->data;
292 switch (pq->type) {
293 case PQ_RADIO:
294 case PQ_CHECK:
295 case PQ_COMBO:
296 generate_multi(pq, ptext);
297 break;
298 case PQ_TEXT:
299 generate_text(pq, ptext);
300 break;
301 case PQ_SCALE:
302 generate_scale(pq, ptext);
303 break;
306 g_string_append(ptext, "</lj-poll>\n");
307 return ptext;
311 static Poll *poll_read (PollDlg *pdlg) {
312 Poll *poll;
313 GtkTreeModel *model = GTK_TREE_MODEL(pdlg->questions.store);
314 GtkTreeIter iter;
315 poll = g_new0(Poll, 1);
316 poll->name = gtk_editable_get_chars(GTK_EDITABLE(pdlg->pollname), 0, -1);
317 if (poll->name && !poll->name[0]) string_replace(&poll->name, NULL);
318 poll->viewers = gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg->viewers));
319 poll->voters = gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg->voters));
320 if (gtk_tree_model_get_iter_first(model, &iter)) {
321 /* this is probably O(n^2) or something like that.
322 * but there hopefully won't be that many answers. */
323 do {
324 PollQuestion *pq;
325 gtk_tree_model_get(model, &iter, 1, &pq, -1);
326 poll->questions = g_slist_append(poll->questions, pq);
327 } while (gtk_tree_model_iter_next(model, &iter));
329 return poll;
333 static void poll_free (Poll *poll) {
334 g_free(poll->name);
335 g_slist_foreach(poll->questions, (GFunc) poll_question_free, NULL);
336 g_slist_free(poll->questions);
337 g_free(poll);
341 static GString *polltext_generate (PollDlg *pdlg) {
342 Poll *poll;
343 GString *str;
344 poll = poll_read(pdlg);
345 str = poll_to_text(poll);
346 poll_free(poll);
347 return str;
351 /* The only public function in this module.
353 * Run the Poll Creator dialog. If a poll is made, paste it
354 * into the main LogJam entry at the current location. */
355 void run_poll_creator_dlg (JamWin *jw) {
356 GString *polltext = NULL;
357 PollDlg *pdlg = POLLDLG(polldlg_new(GTK_WIDGET(jw)));
358 GtkTextBuffer *buffer = jam_doc_get_text_buffer(jw->doc);
359 if (gtk_dialog_run(GTK_DIALOG(pdlg)) == GTK_RESPONSE_OK) {
360 polltext = polltext_generate(pdlg);
361 gtk_text_buffer_delete_selection(buffer, FALSE, FALSE);
362 gtk_text_buffer_insert_at_cursor(buffer, polltext->str, polltext->len);
363 g_string_free(polltext, TRUE);
365 gtk_widget_destroy(GTK_WIDGET(pdlg));