removed 'never-worked' blogger shit
[k8lowj.git] / src / pollcreator.c
blob3f4e821ae85526b60472e4a7d7e7413d022a3148
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include "gtk-all.h"
8 #include <stdlib.h> /* atoi */
10 #include "conf.h"
11 #include "jam.h"
12 #include "poll.h"
13 #include "pollcreator.h"
14 #include "util-gtk.h"
15 #include "util.h"
17 typedef struct _PollDlg PollDlg;
19 struct _PollDlg {
20 GtkDialog dlg; /* Parent widget */
22 GtkWidget *pollname, *viewers, *voters;
23 JamReorderable questions;
26 #define POLLDLG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), polldlg_get_type(), PollDlg))
28 /* macro to generate prototypes for type and instance init functions, as
29 * well as the actual type function, in one swell foop. */
30 #define MAKETYPE(fname, newtypeclass, classinitf, instanceinitf, parenttype, parentclass) \
31 static void instanceinitf(GtkWidget *w); \
32 static GType fname (void) G_GNUC_CONST; \
33 static GType \
34 fname(void) { \
35 static GType new_type = 0; \
36 if (!new_type) { \
37 const GTypeInfo new_info = { \
38 sizeof (parentclass), \
39 NULL, \
40 NULL, \
41 (GClassInitFunc)classinitf, \
42 NULL, \
43 NULL, \
44 sizeof (newtypeclass), \
45 0, \
46 (GInstanceInitFunc) instanceinitf, \
47 }; \
48 new_type = g_type_register_static(parenttype, \
49 #newtypeclass, &new_info, 0); \
50 } \
51 return new_type; \
54 #define MENU_ADD(menu, mnemonic) \
55 gtk_menu_shell_append(GTK_MENU_SHELL(menu), \
56 gtk_menu_item_new_with_mnemonic(mnemonic));
58 /* Poll Creator container */
59 MAKETYPE(polldlg_get_type, PollDlg, NULL, polldlg_init,
60 GTK_TYPE_DIALOG, GtkDialogClass)
62 static GtkWidget*
63 make_pollmeta(PollDlg *pdlg) {
64 GtkWidget *hbox, *vbox, *menu;
66 vbox = gtk_vbox_new(FALSE, 6);
68 pdlg->pollname = gtk_entry_new();
69 gtk_box_pack_start(GTK_BOX(vbox),
70 labelled_box_new(_("Poll _name: "),
71 pdlg->pollname),
72 FALSE, FALSE, 0);
74 hbox = gtk_hbox_new(FALSE, 18);
76 pdlg->voters = gtk_option_menu_new();
77 menu = gtk_menu_new();
78 MENU_ADD(menu, _("All users"));
79 MENU_ADD(menu, _("Friends"));
80 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg->voters), menu);
81 gtk_box_pack_start(GTK_BOX(hbox),
82 labelled_box_new(_("V_oters:"), pdlg->voters),
83 FALSE, FALSE, 0);
85 pdlg->viewers = gtk_option_menu_new();
86 menu = gtk_menu_new();
87 MENU_ADD(menu, _("All users"));
88 MENU_ADD(menu, _("Friends"));
89 MENU_ADD(menu, _("Nobody"));
90 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg->viewers), menu);
91 gtk_box_pack_end(GTK_BOX(hbox),
92 labelled_box_new(_("_Results visible to:"), pdlg->viewers),
93 FALSE, FALSE, 0);
95 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
97 return vbox;
100 static void
101 add_question(PollDlg *pdlg, PollQuestion *pq) {
102 GtkTreeIter iter;
103 gtk_list_store_append(pdlg->questions.store, &iter);
104 gtk_list_store_set(pdlg->questions.store, &iter,
105 0, pq->question,
106 1, pq,
107 -1);
110 static void
111 add_multi_cb(PollDlg *pdlg) {
112 PollQuestion *pq;
113 pq = pollmultidlg_run(GTK_WINDOW(pdlg), NULL);
114 if (pq) add_question(pdlg, pq);
116 static void
117 add_scale_cb(PollDlg *pdlg) {
118 PollQuestion *pq;
119 pq = pollscaledlg_run(GTK_WINDOW(pdlg), NULL);
120 if (pq) add_question(pdlg, pq);
122 static void
123 add_text_cb(PollDlg *pdlg) {
124 PollQuestion *pq;
125 pq = polltextdlg_run(GTK_WINDOW(pdlg), NULL);
126 if (pq) add_question(pdlg, pq);
129 static void
130 add_question_cb(PollDlg *pdlg) {
131 GtkWidget *menu, *item;
133 menu = gtk_menu_new();
135 item = gtk_menu_item_new_with_label(_("Multiple Choice Question"));
136 g_signal_connect_swapped(G_OBJECT(item), "activate",
137 G_CALLBACK(add_multi_cb), pdlg);
138 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
140 item = gtk_menu_item_new_with_label(_("Range Question"));
141 g_signal_connect_swapped(G_OBJECT(item), "activate",
142 G_CALLBACK(add_scale_cb), pdlg);
143 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
145 item = gtk_menu_item_new_with_label(_("Text Question"));
146 g_signal_connect_swapped(G_OBJECT(item), "activate",
147 G_CALLBACK(add_text_cb), pdlg);
148 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
150 gtk_widget_show_all(menu);
152 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
153 0 /* don't know button */, gtk_get_current_event_time());
155 static void
156 edit_question_cb(PollDlg *pdlg) {
157 GtkTreeSelection *sel;
158 GtkTreeModel *model;
159 GtkTreeIter iter;
160 PollQuestion *pq;
162 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg->questions.view));
164 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
165 return;
166 gtk_tree_model_get(model, &iter, 1, &pq, -1);
167 switch (pq->type) {
168 case PQ_RADIO:
169 case PQ_COMBO:
170 case PQ_CHECK:
171 pq = pollmultidlg_run(GTK_WINDOW(pdlg),
172 (PollQuestionMulti*)pq);
173 break;
174 case PQ_TEXT:
175 pq = polltextdlg_run(GTK_WINDOW(pdlg),
176 (PollQuestionText*)pq);
177 break;
178 case PQ_SCALE:
179 pq = pollscaledlg_run(GTK_WINDOW(pdlg),
180 (PollQuestionScale*)pq);
181 break;
182 default:
183 return;
185 if (pq)
186 gtk_list_store_set(pdlg->questions.store, &iter,
187 0, pq->question,
188 1, pq,
189 -1);
191 static void
192 remove_question_cb(PollDlg *pdlg) {
193 GtkTreeSelection *sel;
194 GtkTreeModel *model;
195 GtkTreeIter iter;
196 PollQuestion *pq;
198 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg->questions.view));
199 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
200 return;
201 gtk_tree_model_get(model, &iter, 1, &pq, -1);
202 poll_question_free(pq);
203 gtk_list_store_remove(pdlg->questions.store, &iter);
206 static void
207 make_list(PollDlg *pdlg) {
208 GtkCellRenderer *renderer;
209 GtkTreeViewColumn *column;
211 pdlg->questions.store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
212 jam_reorderable_make(&pdlg->questions);
213 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pdlg->questions.view), FALSE);
214 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pdlg->questions.view), TRUE);
215 g_object_unref(pdlg->questions.store);
217 renderer = gtk_cell_renderer_text_new();
218 column = gtk_tree_view_column_new();
219 gtk_tree_view_column_pack_start(column, renderer, TRUE);
220 gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
221 gtk_tree_view_append_column(GTK_TREE_VIEW(pdlg->questions.view), column);
223 g_signal_connect_data(G_OBJECT(pdlg->questions.add), "clicked",
224 G_CALLBACK(add_question_cb), pdlg,
225 NULL, G_CONNECT_AFTER|G_CONNECT_SWAPPED);
226 g_signal_connect_swapped(G_OBJECT(pdlg->questions.edit), "clicked",
227 G_CALLBACK(edit_question_cb), pdlg);
228 g_signal_connect_swapped(G_OBJECT(pdlg->questions.remove), "clicked",
229 G_CALLBACK(remove_question_cb), pdlg);
232 static GtkWidget*
233 make_content(PollDlg *pdlg) {
234 GtkWidget *vbox;
235 GtkWidget *label;
237 vbox = gtk_vbox_new(FALSE, 6);
238 label = gtk_label_new_with_mnemonic(_("Poll _Questions:"));
239 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
240 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
242 make_list(pdlg);
243 gtk_label_set_mnemonic_widget(GTK_LABEL(label), pdlg->questions.view);
244 gtk_box_pack_start(GTK_BOX(vbox), pdlg->questions.box, TRUE, TRUE, 0);
246 return vbox;
249 static void
250 polldlg_init(GtkWidget *w) {
251 PollDlg *pdlg = POLLDLG(w);
252 GtkWidget *vbox;
254 gtk_window_set_title(GTK_WINDOW(pdlg), _("Poll Creator"));
256 vbox = gtk_vbox_new(FALSE, 12);
258 gtk_box_pack_start(GTK_BOX(vbox), make_pollmeta(pdlg),
259 FALSE, FALSE, 0);
261 gtk_box_pack_start(GTK_BOX(vbox), make_content(pdlg),
262 TRUE, TRUE, 0);
264 gtk_dialog_add_buttons(GTK_DIALOG(pdlg),
265 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
266 _("_Generate"), GTK_RESPONSE_OK,
267 NULL);
269 jam_dialog_set_contents(GTK_DIALOG(pdlg), vbox);
272 static GtkWidget*
273 polldlg_new(GtkWidget *parent) {
274 PollDlg *pdlg = POLLDLG(g_object_new(polldlg_get_type(), NULL));
275 gtk_window_set_transient_for(GTK_WINDOW(pdlg), GTK_WINDOW(parent));
276 return GTK_WIDGET(pdlg);
279 #if 0
280 static PQType
281 _get_pqtype_from_name(G_CONST_RETURN gchar *text) {
282 struct pqtypeinfo *p = (struct pqtypeinfo *)pqtype;
283 while (strcmp(text, p->textname)) {
284 if (p->id == PQ_UNDEF)
285 g_error("unknown type [%s]", text);
287 p++;
289 return p->id;
292 static void
293 _validate_text(PollQText *pqt, GtkWidget *anserrl, gboolean *rc) {
294 gchar *data = NULL;
295 gchar *tmp = NULL;
297 data = _gEText(pqt->SizeLJEntry);
298 if (data && *data != '\0' &&
299 g_ascii_strcasecmp(data, tmp = g_strdup_printf("%d", atoi(data)))) {
300 gtk_label_set_markup(GTK_LABEL(anserrl),
301 _("<b>[Text parameters must be integer.]</b>"));
302 *rc = FALSE;
303 gtk_widget_show(anserrl);
305 g_free(tmp);
306 tmp = NULL;
308 data = _gEText(pqt->MaxLJEntry);
309 if (data && *data != '\0' &&
310 g_ascii_strcasecmp(data, tmp = g_strdup_printf("%d", atoi(data)))) {
311 gtk_label_set_markup(GTK_LABEL(anserrl),
312 _("<b>[Text parameters must be integer.]</b>"));
313 *rc = FALSE;
314 gtk_widget_show(anserrl);
316 g_free(tmp);
318 #endif
320 #if 0
321 static void
322 _validate_scale(PollScaleDlg *psdlg, GtkWidget *anserrl, gboolean *rc) {
323 gint from, to, by;
324 from = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(psdlg->FromSpin));
325 to = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(psdlg->ToSpin));
326 by = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(psdlg->BySpin));
328 /* make sure 20 elements cover scale range */
329 if (( (by > 0) && ((from + 19*by) < to) ) ||
330 ( (by < 0) && ((from + 19*by) > to)) ) {
331 gtk_label_set_markup(GTK_LABEL(anserrl),
332 _("<b>[Too many elements in scale range. Either "
333 "move \"From\" and \"To\" values closer or increase "
334 "\"By\".]</b>"));
335 *rc = FALSE;
336 gtk_widget_show(anserrl);
339 if (( (by > 0) && (from > to) ) ||
340 ( (by < 0) && (from < to) )) {
341 gtk_label_set_markup(GTK_LABEL(anserrl),
342 _("<b>[\"By\" has wrong sign.]</b>"));
343 *rc = FALSE;
344 gtk_widget_show(anserrl);
347 if (by == 0) {
348 gtk_label_set_markup(GTK_LABEL(anserrl),
349 _("<b>[\"By\" cannot be zero.]</b>"));
350 *rc = FALSE;
351 gtk_widget_show(anserrl);
354 #endif
356 static void
357 generate_multi(PollQuestion *pq, GString *ptext) {
358 PollQuestionMulti *pqm = (PollQuestionMulti*)pq;
359 GSList *l;
361 static char* qtype_to_string[] = {
362 "radio",
363 "drop",
364 "check"
367 g_string_append_printf(ptext, "<lj-pq type=\"%s\">\n",
368 qtype_to_string[pq->type]);
369 g_string_append_printf(ptext, "%s\n", pq->question);
371 for (l = pqm->answers; l; l = l->next)
372 g_string_append_printf(ptext, "<lj-pi>%s</lj-pi>\n", (char*)l->data);
373 g_string_append(ptext, "</lj-pq>\n\n");
376 static void
377 generate_text(PollQuestion *pq, GString *ptext) {
378 PollQuestionText *pqt = (PollQuestionText*)pq;
380 g_string_append_printf(ptext, "<lj-pq type=\"text\"");
382 if (pqt->size)
383 g_string_append_printf(ptext, " size=\"%d\"", pqt->size);
384 if (pqt->width)
385 g_string_append_printf(ptext, " maxlength=\"%d\"", pqt->width);
387 g_string_append_printf(ptext, ">\n%s\n</lj-pq>\n\n", pq->question);
390 static void
391 generate_scale(PollQuestion *pq, GString *ptext) {
392 PollQuestionScale *pqs = (PollQuestionScale*)pq;
393 g_string_append_printf(ptext,
394 "<lj-pq type=\"scale\" from=\"%d\" to=\"%d\" by=\"%d\">"
395 "\n%s\n</lj-pq>\n\n",
396 pqs->from, pqs->to, pqs->by, pq->question);
399 static GString*
400 poll_to_text(Poll *poll) {
401 GString *ptext;
402 GSList *l;
404 static char* psec_to_string[] = {
405 "all",
406 "friends",
407 "none"
410 ptext = g_string_sized_new(2048);
412 /* poll open tag */
413 g_string_append(ptext, "<lj-poll ");
414 if (poll->name)
415 g_string_append_printf(ptext, "name=\"%s\" ", poll->name);
417 /* ...with metadata */
418 g_string_append_printf(ptext, "whoview=\"%s\" ",
419 psec_to_string[poll->viewers]);
420 g_string_append_printf(ptext, "whovote=\"%s\">\n\n",
421 psec_to_string[poll->voters]);
423 /* poll questions */
424 for (l = poll->questions; l; l = l->next) {
425 PollQuestion *pq = l->data;
427 switch (pq->type) {
428 case PQ_RADIO:
429 case PQ_CHECK:
430 case PQ_COMBO:
431 generate_multi(pq, ptext);
432 break;
433 case PQ_TEXT:
434 generate_text (pq, ptext);
435 break;
436 case PQ_SCALE:
437 generate_scale(pq, ptext);
438 break;
442 g_string_append(ptext, "</lj-poll>\n");
444 return ptext;
447 static Poll*
448 poll_read(PollDlg *pdlg) {
449 Poll *poll;
450 GtkTreeModel *model = GTK_TREE_MODEL(pdlg->questions.store);
451 GtkTreeIter iter;
453 poll = g_new0(Poll, 1);
455 poll->name = gtk_editable_get_chars(GTK_EDITABLE(pdlg->pollname), 0, -1);
456 if (poll->name && !poll->name[0])
457 string_replace(&poll->name, NULL);
459 poll->viewers = gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg->viewers));
460 poll->voters = gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg->voters));
462 if (gtk_tree_model_get_iter_first(model, &iter)) {
463 /* this is probably O(n^2) or something like that.
464 * but there hopefully won't be that many answers. */
465 do {
466 PollQuestion *pq;
467 gtk_tree_model_get(model, &iter, 1, &pq, -1);
468 poll->questions = g_slist_append(poll->questions, pq);
469 } while (gtk_tree_model_iter_next(model, &iter));
472 return poll;
475 static void
476 poll_free(Poll *poll) {
477 g_free(poll->name);
478 g_slist_foreach(poll->questions, (GFunc)poll_question_free, NULL);
479 g_slist_free(poll->questions);
480 g_free(poll);
483 static GString*
484 polltext_generate(PollDlg *pdlg) {
485 Poll *poll;
486 GString *str;
488 poll = poll_read(pdlg);
489 str = poll_to_text(poll);
490 poll_free(poll);
492 return str;
495 /* The only public function in this module.
497 * Run the Poll Creator dialog. If a poll is made, paste it
498 * into the main LogJam entry at the current location. */
499 void
500 run_poll_creator_dlg(JamWin *jw) {
501 GString *polltext = NULL;
502 PollDlg *pdlg = POLLDLG(polldlg_new(GTK_WIDGET(jw)));
503 GtkTextBuffer *buffer = jam_doc_get_text_buffer(jw->doc);
505 if (gtk_dialog_run(GTK_DIALOG(pdlg)) == GTK_RESPONSE_OK) {
506 polltext = polltext_generate(pdlg);
507 gtk_text_buffer_delete_selection(buffer, FALSE, FALSE);
508 gtk_text_buffer_insert_at_cursor(buffer,
509 polltext->str, polltext->len);
510 g_string_free(polltext, TRUE);
513 gtk_widget_destroy(GTK_WIDGET(pdlg));