#include "mainwindow.h" #include "mdiary.h" /** * @brief mainWindow_list_entry_visible checks whether the entry should be displayed. * @param model N/A * @param iter N/A * @param mainWindow struct mainWindow * * @return True when it should displayed, else false. */ static gboolean mainWindow_list_entry_visible(GtkTreeModel *model, GtkTreeIter *iter, struct mainWindow *mainWindow) { GRegex *regex; GMatchInfo *match_info; gchar *searchString; gchar *temp; gboolean search_matched = 0; GDateTime *datetime; guint time; /* Check text search */ searchString = (gchar *)gtk_entry_get_text(GTK_ENTRY(mainWindow->searchEntry)); if (searchString[0] != 0 && ( mainWindow->filterSettings.search_title || mainWindow->filterSettings.search_tags || mainWindow->filterSettings.search_text)) { regex = g_regex_new(searchString, G_REGEX_CASELESS, 0, NULL); gtk_tree_model_get(model, iter, COL_TITLE, &temp, -1); if (mainWindow->filterSettings.search_title && g_regex_match(regex, temp, 0, &match_info) && g_match_info_matches(match_info)) search_matched = 1; g_free(temp); gtk_tree_model_get(model, iter, COL_TAGS_TEXT, &temp, -1); if (!search_matched && mainWindow->filterSettings.search_tags && g_regex_match(regex, temp, 0, &match_info) && g_match_info_matches(match_info)) search_matched = 1; g_free(temp); gtk_tree_model_get(model, iter, COL_SUMMARY, &temp, -1); if (!search_matched && mainWindow->filterSettings.search_summary && g_regex_match(regex, temp, 0, &match_info) && g_match_info_matches(match_info)) search_matched = 1; g_free(temp); gtk_tree_model_get(model, iter, COL_TEXT, &temp, -1); if (!search_matched && mainWindow->filterSettings.search_text && g_regex_match(regex, temp, 0, &match_info) && g_match_info_matches(match_info)) search_matched = 1; g_free(temp); if (!search_matched) return 0; } /* Check time span */ gtk_tree_model_get(model, iter, COL_TIMESTAMP, &datetime, -1); if (datetime) { time = g_date_time_to_unix(datetime); return time >= mainWindow->filterSettings.time_start && time <= mainWindow->filterSettings.time_end; } else { return 0; } } /** * @brief mainWindow_sort_date_compare_func Compares the two GDateTimes from the main store. * @param model N/A * @param a N/A * @param b N/A * @param userdata N/A * @return N/A */ static gint mainWindow_sort_date_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) { GDateTime *datetime1; GDateTime *datetime2; guint time1; guint time2; gtk_tree_model_get(model, a, COL_TIMESTAMP, &datetime1, -1); gtk_tree_model_get(model, b, COL_TIMESTAMP, &datetime2, -1); time1 = g_date_time_to_unix(datetime1); time2 = g_date_time_to_unix(datetime2); if (time1 == time2) return 0; else if (time1 > time2) return 1; else return -1; } /** * @brief mainWindow_configure_treeView configures GtkTreeView * @param mainWindow struct mainWindow * */ static void mainWindow_configure_treeView(struct mainWindow *mainWindow) { GtkCellRenderer *renderer; GtkTreeSortable *sortable; GtkTreeViewColumn *col; GtkTreeModelSort *sorted; GtkTreeModel *model; mainWindow->entryListStore = gtk_list_store_new(COL_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING); mainWindow->entryListFiltered = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new( GTK_TREE_MODEL(mainWindow->entryListStore), NULL)); sorted = GTK_TREE_MODEL_SORT(gtk_tree_model_sort_new_with_model( GTK_TREE_MODEL(mainWindow->entryListFiltered))); gtk_tree_model_filter_set_visible_func(mainWindow->entryListFiltered, (GtkTreeModelFilterVisibleFunc) mainWindow_list_entry_visible, mainWindow, NULL); model = GTK_TREE_MODEL(sorted); gtk_tree_view_set_model(mainWindow->entryListView, model); /* TODO: Unref mainWindow->entryListStore and filtered, sorted and model? */ sortable = GTK_TREE_SORTABLE(sorted); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(mainWindow->entryListView, -1, "Title", renderer, "text", COL_TITLE, NULL); col = gtk_tree_view_get_column(mainWindow->entryListView, COL_TITLE); gtk_tree_view_column_set_resizable(col, 1); gtk_tree_view_column_set_sort_column_id(col, COL_TITLE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(mainWindow->entryListView, -1, "Date", renderer, "text", COL_DATE_TEXT, NULL); col = gtk_tree_view_get_column(mainWindow->entryListView, COL_DATE_TEXT); gtk_tree_view_column_set_resizable(col, 1); gtk_tree_sortable_set_sort_func(sortable, COL_DATE_TEXT, mainWindow_sort_date_compare_func, GINT_TO_POINTER(COL_DATE_TEXT), NULL); gtk_tree_view_column_set_sort_column_id(col, COL_DATE_TEXT); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(mainWindow->entryListView, -1, "Tags", renderer, "text", COL_TAGS_TEXT, NULL); col = gtk_tree_view_get_column(mainWindow->entryListView, COL_TAGS_TEXT); gtk_tree_view_column_set_resizable(col, 1); gtk_tree_view_column_set_sort_column_id(col, COL_TAGS_TEXT); gtk_tree_sortable_set_sort_column_id(sortable, COL_DATE_TEXT, GTK_SORT_DESCENDING); } /** * @brief mainWindow_connect_signals connects are g signals * @param mainWindow struct mainWindow * */ static void mainWindow_connect_signals(struct mainWindow *mainWindow) { g_signal_connect(mainWindow->dateStart, "focus-in-event", (GCallback) mainWindow_showPopover, mainWindow); g_signal_connect(mainWindow->dateEnd, "focus-in-event", (GCallback) mainWindow_showPopover, mainWindow); g_signal_connect(mainWindow->calendarRange, "day-selected", (GCallback) mainWindow_calendarSelected, mainWindow); g_signal_connect_swapped(mainWindow->calendarRange, "day-selected-double-click", (GCallback) gtk_popover_popdown, mainWindow->popoverDate); g_signal_connect(mainWindow->entryListSelection, "changed", (GCallback) mainWindow_entrySelected, mainWindow); g_signal_connect(mainWindow->dateStart, "changed", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->dateEnd, "changed", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->dateStart, "focus-out-event", (GCallback) mainWindow_checkDate, mainWindow); g_signal_connect(mainWindow->dateEnd, "focus-out-event", (GCallback) mainWindow_checkDate, mainWindow); g_signal_connect(mainWindow->searchEntry, "changed", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->checkSearchTitle, "toggled", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->checkSearchTags, "toggled", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->checkSearchText, "toggled", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->checkSearchSummary, "toggled", (GCallback) mainWindow_filterChanged, mainWindow); g_signal_connect(mainWindow->buttonClearSearch, "clicked", (GCallback) mainWindow_clearSearch, mainWindow); } /** * @brief mainWindow_init_to_default initializes all non-date related UI elements to sensible values. * @param mainWindow */ static void mainWindow_init_to_default(struct mainWindow *mainWindow) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mainWindow->checkSearchTitle), 1); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mainWindow->checkSearchSummary), 1); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mainWindow->checkSearchTags), 1); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mainWindow->checkSearchText), 0); } /** * @brief mainWindow_new creates a new mainWindow. * @return Returns struct mainWindow * */ struct mainWindow *mainWindow_new() { GtkBuilder *builder; struct mainWindow *mainWindow; builder = gtk_builder_new_from_file("glade/mainWindow.glade"); mainWindow = malloc(sizeof(struct mainWindow)); mainWindow->mainWindow = GTK_WIDGET(gtk_builder_get_object(builder, "mainWindow")); mainWindow->buttonChooseWorkspace = GTK_WIDGET(gtk_builder_get_object(builder, "buttonChooseWorkspace")); mainWindow->searchEntry = GTK_WIDGET(gtk_builder_get_object(builder, "searchEntry")); mainWindow->dateStart = GTK_WIDGET(gtk_builder_get_object(builder, "dateStart")); mainWindow->dateEnd = GTK_WIDGET(gtk_builder_get_object(builder, "dateEnd")); mainWindow->entryText = GTK_WIDGET(gtk_builder_get_object(builder, "entryText")); mainWindow->popoverDate = GTK_WIDGET(gtk_builder_get_object(builder, "popoverDate")); mainWindow->calendarRange = GTK_WIDGET(gtk_builder_get_object(builder, "calendarRange")); mainWindow->checkSearchTitle = GTK_WIDGET(gtk_builder_get_object(builder, "checkSearchTitle")); mainWindow->checkSearchSummary = GTK_WIDGET(gtk_builder_get_object(builder, "checkSearchSummary")); mainWindow->checkSearchTags = GTK_WIDGET(gtk_builder_get_object(builder, "checkSearchTags")); mainWindow->checkSearchText = GTK_WIDGET(gtk_builder_get_object(builder, "checkSearchText")); mainWindow->labelSummary = GTK_WIDGET(gtk_builder_get_object(builder, "labelSummary")); mainWindow->buttonClearSearch = GTK_WIDGET(gtk_builder_get_object(builder, "buttonClearSearch")); mainWindow->entryListView = GTK_TREE_VIEW(gtk_builder_get_object(builder, "entryListView")); mainWindow->entryListSelection = GTK_TREE_SELECTION(gtk_builder_get_object(builder, "entryListSelection")); mainWindow->textBuffer = GTK_TEXT_BUFFER(gtk_builder_get_object(builder, "entryTextBuffer")); mainWindow->filterSettings.time_start = 0; mainWindow->filterSettings.time_end = -1; gtk_builder_connect_signals(builder, NULL); g_object_unref(G_OBJECT(builder)); mainWindow_configure_treeView(mainWindow); mainWindow_connect_signals(mainWindow); mainWindow_init_to_default(mainWindow); mainWindow_clearSearch(NULL, mainWindow); return mainWindow; } /** * @brief mainWindow_chooseWorkspaceClicked is called when the user clicks the Choose Workspace button. * @param widget N/A * @param event N/A * @param user_data struct mainWindow * */ void mainWindow_chooseWorkspaceClicked(GtkWidget *widget, gint event, gpointer user_data) { printf("Choose workspace.\n"); fflush(stdout); } /** * @brief mainWindow_showPopover is called from a dateEntry when it is focused. * @param widget is the text entry. * @param event N/A * @param user_data struct mainWindow * */ void mainWindow_showPopover(GtkWidget *widget, GdkEvent *event, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GDateTime *datetime; mainWindow->selectedDateEntry = NULL; datetime = g_date_time_new_from_unix_local((widget == mainWindow->dateStart ? mainWindow->filterSettings.time_start : mainWindow->filterSettings.time_end)); if (datetime) { gtk_calendar_select_month(GTK_CALENDAR(mainWindow->calendarRange), g_date_time_get_month(datetime) - 1, g_date_time_get_year(datetime)); gtk_calendar_select_day(GTK_CALENDAR(mainWindow->calendarRange), g_date_time_get_day_of_month(datetime)); g_date_time_unref(datetime); } /* gtk_editable_select_region(GTK_EDITABLE(widget), 0, 255); */ mainWindow->selectedDateEntry = widget; if (!gtk_widget_get_visible(mainWindow->popoverDate)) gtk_popover_popup(GTK_POPOVER(mainWindow->popoverDate)); gtk_popover_set_relative_to(GTK_POPOVER(mainWindow->popoverDate), widget); } /** * @brief mainWindow_calendarSelected is the callback when the user selects a date in the calendar. * @param widget is the calendar. * @param user_data struct mainWindow * */ void mainWindow_calendarSelected(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; guint year, month, day; gchar *text; if (mainWindow->selectedDateEntry) { gtk_calendar_get_date(GTK_CALENDAR(mainWindow->calendarRange), &year, &month, &day); text = g_strdup_printf("%d-%02d-%02d", year, month + 1, day); gtk_entry_set_text(GTK_ENTRY(mainWindow->selectedDateEntry), text); g_free(text); } } /** * @brief mainWindow_entrySelected is the callback when the user selects a diary entry. * @param widget N/A * @param user_data struct mainWindow * */ void mainWindow_entrySelected(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; gchar *text; gchar *title; gchar *summary; gchar *temp; selection = gtk_tree_view_get_selection(mainWindow->entryListView); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, COL_TEXT, &text, -1); gtk_text_buffer_set_text(mainWindow->textBuffer, text, g_utf8_strlen(text, -1)); g_free(text); gtk_tree_model_get(model, &iter, COL_SUMMARY, &summary, -1); gtk_tree_model_get(model, &iter, COL_TITLE, &title, -1); temp = g_strdup_printf("%s: %s", title, summary); gtk_label_set_markup(GTK_LABEL(mainWindow->labelSummary), temp); g_free(title); g_free(summary); g_free(temp); } else { gtk_text_buffer_set_text(mainWindow->textBuffer, "", 0); gtk_label_set_text(GTK_LABEL(mainWindow->labelSummary), "Please select an entry in the list above."); } } /** * @brief mainWindow_filterChanged updates the filter backend values * @param widget The widget that caused the update request. Can be NULL to update all. * @param user_data struct mainWindow * */ void mainWindow_filterChanged(GtkWidget *widget, gpointer user_data) { GDateTime *datetime; struct mainWindow *mainWindow = (struct mainWindow *)user_data; if (!widget || widget == mainWindow->dateStart) { datetime = mdiary_get_date_from_string((gchar *)gtk_entry_get_text( GTK_ENTRY(mainWindow->dateStart))); if (datetime) { mainWindow->filterSettings.time_start = g_date_time_to_unix(datetime); g_date_time_unref(datetime); } } if (!widget || widget == mainWindow->dateEnd) { datetime = mdiary_get_date_from_string((gchar *)gtk_entry_get_text( GTK_ENTRY(mainWindow->dateEnd))); if (datetime) { mainWindow->filterSettings.time_end = g_date_time_to_unix(datetime); g_date_time_unref(datetime); } } mainWindow->filterSettings.search_title = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(mainWindow->checkSearchTitle)); mainWindow->filterSettings.search_tags = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(mainWindow->checkSearchTags)); mainWindow->filterSettings.search_text = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(mainWindow->checkSearchText)); mainWindow->filterSettings.search_summary = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(mainWindow->checkSearchSummary)); gtk_tree_model_filter_refilter(mainWindow->entryListFiltered); } /** * @brief mainWindow_checkDate checks the date in a text box and resets it if necessary. * @param widget is the GtkEntry to be checked. * @param event N/A * @param user_data struct mainWindow * */ void mainWindow_checkDate(GtkWidget *widget, gint event, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GDateTime *datetime; datetime = mdiary_get_date_from_string((char *)gtk_entry_get_text(GTK_ENTRY(widget))); if (!datetime) { datetime = g_date_time_new_from_unix_local(widget == mainWindow->dateStart ? mainWindow->filterSettings.time_start : mainWindow->filterSettings.time_end); gtk_entry_set_text(GTK_ENTRY(widget), g_date_time_format(datetime, "%Y-%m-%d")); } g_date_time_unref(datetime); } /** * @brief mainWindow_clearSearch resets all search UI elements * @param widget * @param event * @param user_data */ void mainWindow_clearSearch(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; gtk_entry_set_text(GTK_ENTRY(mainWindow->searchEntry), ""); /** * These need to be set to oldest and newest after indexing date */ gtk_entry_set_text(GTK_ENTRY(mainWindow->dateStart), "2016-01-01"); gtk_entry_set_text(GTK_ENTRY(mainWindow->dateEnd), "2018-01-01"); }