summaryrefslogtreecommitdiffstats
path: root/src/gs-review-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/gs-review-dialog.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/gs-review-dialog.c b/src/gs-review-dialog.c
new file mode 100644
index 0000000..686209e
--- /dev/null
+++ b/src/gs-review-dialog.c
@@ -0,0 +1,231 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_GSPELL
+#include <gspell/gspell.h>
+#endif
+
+#include "gs-review-dialog.h"
+#include "gs-star-widget.h"
+
+#define DESCRIPTION_LENGTH_MAX 3000 /* chars */
+#define DESCRIPTION_LENGTH_MIN 15 /* chars */
+#define SUMMARY_LENGTH_MAX 70 /* chars */
+#define SUMMARY_LENGTH_MIN 3 /* chars */
+#define WRITING_TIME_MIN 5 /* seconds */
+
+struct _GsReviewDialog
+{
+ GtkDialog parent_instance;
+
+ GtkWidget *star;
+ GtkWidget *label_rating_desc;
+ GtkWidget *summary_entry;
+ GtkWidget *post_button;
+ GtkWidget *text_view;
+ guint timer_id;
+};
+
+G_DEFINE_TYPE (GsReviewDialog, gs_review_dialog, GTK_TYPE_DIALOG)
+
+gint
+gs_review_dialog_get_rating (GsReviewDialog *dialog)
+{
+ return gs_star_widget_get_rating (GS_STAR_WIDGET (dialog->star));
+}
+
+void
+gs_review_dialog_set_rating (GsReviewDialog *dialog, gint rating)
+{
+ gs_star_widget_set_rating (GS_STAR_WIDGET (dialog->star), rating);
+}
+
+const gchar *
+gs_review_dialog_get_summary (GsReviewDialog *dialog)
+{
+ return gtk_entry_get_text (GTK_ENTRY (dialog->summary_entry));
+}
+
+gchar *
+gs_review_dialog_get_text (GsReviewDialog *dialog)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->text_view));
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static void
+gs_review_dialog_update_review_comment (GsReviewDialog *dialog)
+{
+ const gchar *msg = NULL;
+ gint perc;
+
+ /* update the rating description */
+ perc = gs_star_widget_get_rating (GS_STAR_WIDGET (dialog->star));
+ if (perc == 20) {
+ /* TRANSLATORS: lighthearted star rating description;
+ * A really bad application */
+ msg = _("Hate it");
+ } else if (perc == 40) {
+ /* TRANSLATORS: lighthearted star rating description;
+ * Not a great application */
+ msg = _("Don’t like it");
+ } else if (perc == 60) {
+ /* TRANSLATORS: lighthearted star rating description;
+ * A fairly-good application */
+ msg = _("It’s OK");
+ } else if (perc == 80) {
+ /* TRANSLATORS: lighthearted star rating description;
+ * A good application */
+ msg = _("Like it");
+ } else if (perc == 100) {
+ /* TRANSLATORS: lighthearted star rating description;
+ * A really awesome application */
+ msg = _("Love it");
+ } else {
+ /* just reserve space */
+ msg = "";
+ }
+ gtk_label_set_label (GTK_LABEL (dialog->label_rating_desc), msg);
+}
+
+static void
+gs_review_dialog_changed_cb (GsReviewDialog *dialog)
+{
+ GtkTextBuffer *buffer;
+ gboolean all_okay = TRUE;
+ const gchar *msg = NULL;
+
+ /* update review text */
+ gs_review_dialog_update_review_comment (dialog);
+
+ /* require rating, summary and long review */
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->text_view));
+ if (dialog->timer_id != 0) {
+ /* TRANSLATORS: the review can't just be copied and pasted */
+ msg = _("Please take more time writing the review");
+ all_okay = FALSE;
+ } else if (gs_star_widget_get_rating (GS_STAR_WIDGET (dialog->star)) == 0) {
+ /* TRANSLATORS: the review is not acceptable */
+ msg = _("Please choose a star rating");
+ all_okay = FALSE;
+ } else if (gtk_entry_get_text_length (GTK_ENTRY (dialog->summary_entry)) < SUMMARY_LENGTH_MIN) {
+ /* TRANSLATORS: the review is not acceptable */
+ msg = _("The summary is too short");
+ all_okay = FALSE;
+ } else if (gtk_entry_get_text_length (GTK_ENTRY (dialog->summary_entry)) > SUMMARY_LENGTH_MAX) {
+ /* TRANSLATORS: the review is not acceptable */
+ msg = _("The summary is too long");
+ all_okay = FALSE;
+ } else if (gtk_text_buffer_get_char_count (buffer) < DESCRIPTION_LENGTH_MIN) {
+ /* TRANSLATORS: the review is not acceptable */
+ msg = _("The description is too short");
+ all_okay = FALSE;
+ } else if (gtk_text_buffer_get_char_count (buffer) > DESCRIPTION_LENGTH_MAX) {
+ /* TRANSLATORS: the review is not acceptable */
+ msg = _("The description is too long");
+ all_okay = FALSE;
+ }
+
+ /* tell the user what's happening */
+ gtk_widget_set_tooltip_text (dialog->post_button, msg);
+
+ /* can the user submit this? */
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, all_okay);
+}
+
+static gboolean
+gs_review_dialog_timeout_cb (gpointer user_data)
+{
+ GsReviewDialog *dialog = GS_REVIEW_DIALOG (user_data);
+ dialog->timer_id = 0;
+ gs_review_dialog_changed_cb (dialog);
+ return FALSE;
+}
+
+static void
+gs_review_dialog_init (GsReviewDialog *dialog)
+{
+ GtkTextBuffer *buffer;
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+
+#ifdef HAVE_GSPELL
+ /* allow checking spelling */
+ {
+ GspellEntry *gspell_entry;
+ GspellTextView *gspell_view;
+
+ gspell_entry = gspell_entry_get_from_gtk_entry (GTK_ENTRY (dialog->summary_entry));
+ gspell_entry_basic_setup (gspell_entry);
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (dialog->text_view));
+ gspell_text_view_basic_setup (gspell_view);
+ }
+#endif
+
+ /* require the user to spend at least 30 seconds on writing a review */
+ dialog->timer_id = g_timeout_add_seconds (WRITING_TIME_MIN,
+ gs_review_dialog_timeout_cb,
+ dialog);
+
+ /* update UI */
+ g_signal_connect_swapped (dialog->star, "rating-changed",
+ G_CALLBACK (gs_review_dialog_changed_cb), dialog);
+ g_signal_connect_swapped (dialog->summary_entry, "notify::text",
+ G_CALLBACK (gs_review_dialog_changed_cb), dialog);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->text_view));
+ g_signal_connect_swapped (buffer, "changed",
+ G_CALLBACK (gs_review_dialog_changed_cb), dialog);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
+}
+
+static void
+gs_review_row_dispose (GObject *object)
+{
+ GsReviewDialog *dialog = GS_REVIEW_DIALOG (object);
+ if (dialog->timer_id > 0) {
+ g_source_remove (dialog->timer_id);
+ dialog->timer_id = 0;
+ }
+ G_OBJECT_CLASS (gs_review_dialog_parent_class)->dispose (object);
+}
+
+static void
+gs_review_dialog_class_init (GsReviewDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gs_review_row_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-review-dialog.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, star);
+ gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, label_rating_desc);
+ gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, summary_entry);
+ gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, text_view);
+ gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, post_button);
+}
+
+GtkWidget *
+gs_review_dialog_new (void)
+{
+ return GTK_WIDGET (g_object_new (GS_TYPE_REVIEW_DIALOG,
+ "use-header-bar", TRUE,
+ NULL));
+}