1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
8 #include <stdlib.h> /* atoi */
13 #include "pollcreator.h"
17 typedef struct _PollDlg 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; \
35 static GType new_type = 0; \
37 const GTypeInfo new_info = { \
38 sizeof (parentclass), \
41 (GClassInitFunc)classinitf, \
44 sizeof (newtypeclass), \
46 (GInstanceInitFunc) instanceinitf, \
48 new_type = g_type_register_static(parenttype, \
49 #newtypeclass, &new_info, 0); \
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
)
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: "),
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
),
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
),
95 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
101 add_question(PollDlg
*pdlg
, PollQuestion
*pq
) {
103 gtk_list_store_append(pdlg
->questions
.store
, &iter
);
104 gtk_list_store_set(pdlg
->questions
.store
, &iter
,
111 add_multi_cb(PollDlg
*pdlg
) {
113 pq
= pollmultidlg_run(GTK_WINDOW(pdlg
), NULL
);
114 if (pq
) add_question(pdlg
, pq
);
117 add_scale_cb(PollDlg
*pdlg
) {
119 pq
= pollscaledlg_run(GTK_WINDOW(pdlg
), NULL
);
120 if (pq
) add_question(pdlg
, pq
);
123 add_text_cb(PollDlg
*pdlg
) {
125 pq
= polltextdlg_run(GTK_WINDOW(pdlg
), NULL
);
126 if (pq
) add_question(pdlg
, pq
);
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());
156 edit_question_cb(PollDlg
*pdlg
) {
157 GtkTreeSelection
*sel
;
162 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg
->questions
.view
));
164 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
166 gtk_tree_model_get(model
, &iter
, 1, &pq
, -1);
171 pq
= pollmultidlg_run(GTK_WINDOW(pdlg
),
172 (PollQuestionMulti
*)pq
);
175 pq
= polltextdlg_run(GTK_WINDOW(pdlg
),
176 (PollQuestionText
*)pq
);
179 pq
= pollscaledlg_run(GTK_WINDOW(pdlg
),
180 (PollQuestionScale
*)pq
);
186 gtk_list_store_set(pdlg
->questions
.store
, &iter
,
192 remove_question_cb(PollDlg
*pdlg
) {
193 GtkTreeSelection
*sel
;
198 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg
->questions
.view
));
199 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
201 gtk_tree_model_get(model
, &iter
, 1, &pq
, -1);
202 poll_question_free(pq
);
203 gtk_list_store_remove(pdlg
->questions
.store
, &iter
);
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
);
233 make_content(PollDlg
*pdlg
) {
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);
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);
250 polldlg_init(GtkWidget
*w
) {
251 PollDlg
*pdlg
= POLLDLG(w
);
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
),
261 gtk_box_pack_start(GTK_BOX(vbox
), make_content(pdlg
),
264 gtk_dialog_add_buttons(GTK_DIALOG(pdlg
),
265 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
266 _("_Generate"), GTK_RESPONSE_OK
,
269 jam_dialog_set_contents(GTK_DIALOG(pdlg
), vbox
);
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
);
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
);
293 _validate_text(PollQText
*pqt
, GtkWidget
*anserrl
, gboolean
*rc
) {
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>"));
303 gtk_widget_show(anserrl
);
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>"));
314 gtk_widget_show(anserrl
);
322 _validate_scale(PollScaleDlg
*psdlg
, GtkWidget
*anserrl
, gboolean
*rc
) {
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 "
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>"));
344 gtk_widget_show(anserrl
);
348 gtk_label_set_markup(GTK_LABEL(anserrl
),
349 _("<b>[\"By\" cannot be zero.]</b>"));
351 gtk_widget_show(anserrl
);
357 generate_multi(PollQuestion
*pq
, GString
*ptext
) {
358 PollQuestionMulti
*pqm
= (PollQuestionMulti
*)pq
;
361 static char* qtype_to_string
[] = {
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");
377 generate_text(PollQuestion
*pq
, GString
*ptext
) {
378 PollQuestionText
*pqt
= (PollQuestionText
*)pq
;
380 g_string_append_printf(ptext
, "<lj-pq type=\"text\"");
383 g_string_append_printf(ptext
, " size=\"%d\"", pqt
->size
);
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
);
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
);
400 poll_to_text(Poll
*poll
) {
404 static char* psec_to_string
[] = {
410 ptext
= g_string_sized_new(2048);
413 g_string_append(ptext
, "<lj-poll ");
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
]);
424 for (l
= poll
->questions
; l
; l
= l
->next
) {
425 PollQuestion
*pq
= l
->data
;
431 generate_multi(pq
, ptext
);
434 generate_text (pq
, ptext
);
437 generate_scale(pq
, ptext
);
442 g_string_append(ptext
, "</lj-poll>\n");
448 poll_read(PollDlg
*pdlg
) {
450 GtkTreeModel
*model
= GTK_TREE_MODEL(pdlg
->questions
.store
);
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. */
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
));
476 poll_free(Poll
*poll
) {
478 g_slist_foreach(poll
->questions
, (GFunc
)poll_question_free
, NULL
);
479 g_slist_free(poll
->questions
);
484 polltext_generate(PollDlg
*pdlg
) {
488 poll
= poll_read(pdlg
);
489 str
= poll_to_text(poll
);
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. */
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
));