/* -*- 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 #include #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_editable_get_text (GTK_EDITABLE (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; glong summary_length; /* 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)); summary_length = g_utf8_strlen (gtk_editable_get_text (GTK_EDITABLE (dialog->summary_entry)), -1); 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 (summary_length < SUMMARY_LENGTH_MIN) { /* TRANSLATORS: the review is not acceptable */ msg = _("The summary is too short"); all_okay = FALSE; } else if (summary_length > 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)); /* 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)); }