#include "mainwindow.h" #include "mdiary.h" const gchar *default_template[] = {"# Title\n" "Date: ", "\0", "\n" "Summary: \n" "Tags: \n" "\n" "Text", NULL}; static gboolean mainWindow_workspace_entry_visible(GtkTreeModel *model, GtkTreeIter *iter, struct mainWindow *mainWindow) { GRegex *regex; GMatchInfo *match_info; gchar *searchString; gchar *temp; gboolean ret = FALSE; searchString = (gchar *)gtk_entry_get_text(GTK_ENTRY(mainWindow->workspaceSearch)); if (searchString[0]) { regex = g_regex_new(searchString, G_REGEX_CASELESS, 0, NULL); gtk_tree_model_get(model, iter, WSCOL_PATH, &temp, -1); if (g_regex_match(regex, temp, 0, &match_info) && g_match_info_matches(match_info)) ret = TRUE; g_free(temp); } else { ret = TRUE; } return ret; } void mainWindow_workspace_search_changed(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; gtk_tree_model_filter_refilter(mainWindow->workspaceListFiltered); } /** * @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; gint64 time; if (mainWindow->filterSettings.hide_all) return FALSE; /* 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); g_regex_unref(regex); 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); /* The addition in braces allows for the whole day instead of just its first second. */ return time >= (mainWindow->filterSettings.time_start) && time <= mainWindow->filterSettings.time_end + (60 * 60 * 23 + 60 * 59 + 59); } 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; gint64 time1; gint64 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; } void mainWindow_add_recent_workspace(struct mainWindow *mainWindow, gchar *path, gboolean append) { GtkTreeIter iter, iter_delete; GRegex *regex; GMatchInfo *match_info; gchar *temp; regex = g_regex_new("(.*)\\/(.*)", G_REGEX_RAW, 0, NULL); if (g_regex_match(regex, path, 0, &match_info) && g_regex_get_capture_count(regex) > 1 && g_match_info_matches(match_info)) { g_match_info_fetch(match_info, 1); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mainWindow->workspaceListStore), &iter)) { do { memcpy(&iter_delete, &iter, sizeof(iter)); /* scan for last */ gtk_tree_model_get(GTK_TREE_MODEL(mainWindow->workspaceListStore), &iter, WSCOL_PATH, &temp, -1); if (!g_strcmp0(temp, path)) { g_free(temp); gtk_list_store_remove(mainWindow->workspaceListStore, &iter); break; } g_free(temp); } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(mainWindow->workspaceListStore), &iter)); } temp = g_strdup_printf("%s\n%s", g_match_info_fetch(match_info, 2), g_match_info_fetch(match_info, 1)); if (append) gtk_list_store_append(mainWindow->workspaceListStore, &iter); else gtk_list_store_prepend(mainWindow->workspaceListStore, &iter); gtk_list_store_set(mainWindow->workspaceListStore, &iter, WSCOL_LABEL, temp, WSCOL_PATH, path, -1); g_free(temp); if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(mainWindow->workspaceListStore), NULL) > FILE_LIST_MAX_LEN) gtk_list_store_remove(mainWindow->workspaceListStore, &iter_delete); } g_regex_unref(regex); } /** * @brief mainWindow_wschooser_configure_treeView configures the GtkTreeView for workspace selection * @param mainWindow */ static void mainWindow_configure_workspaceTreeView(struct mainWindow *mainWindow) { GtkCellRenderer *renderer; mainWindow->workspaceListStore = gtk_list_store_new(WSCOL_COUNT, G_TYPE_STRING, G_TYPE_STRING); mainWindow->workspaceListFiltered = GTK_TREE_MODEL_FILTER( gtk_tree_model_filter_new( GTK_TREE_MODEL(mainWindow->workspaceListStore), NULL)); gtk_tree_model_filter_set_visible_func(mainWindow->workspaceListFiltered, (GtkTreeModelFilterVisibleFunc) mainWindow_workspace_entry_visible, mainWindow, NULL); gtk_tree_view_set_model(mainWindow->workspaceListView, GTK_TREE_MODEL(mainWindow->workspaceListFiltered)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(mainWindow->workspaceListView, -1, "", renderer, "markup", WSCOL_LABEL, NULL); } /** * @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, 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); } static void mainWindow_configure_entry_completion(struct mainWindow *mainWindow) { GtkTreeSortable *sortable; GtkTreeModelSort *sorted; GtkTreeModel *model; mainWindow->completionListStore = gtk_list_store_new(1, G_TYPE_STRING); sorted = GTK_TREE_MODEL_SORT(gtk_tree_model_sort_new_with_model( GTK_TREE_MODEL(mainWindow->completionListStore))); model = GTK_TREE_MODEL(sorted); /* TODO: Unref mainWindow->completionListStore and filtered, sorted and model? */ sortable = GTK_TREE_SORTABLE(sorted); gtk_entry_completion_set_model(mainWindow->searchEntryCompletion, model); gtk_entry_completion_set_text_column(mainWindow->searchEntryCompletion, 0); gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING); } /** * @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->labelDateToDate, "activate-link", (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->entryListView, "row-activated", (GCallback) mainWindow_entryActivated, 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); g_signal_connect(mainWindow->dateStart, "icon-press", (GCallback) mainWindow_dateIconPress, mainWindow); g_signal_connect(mainWindow->dateEnd, "icon-press", (GCallback) mainWindow_dateIconPress, mainWindow); g_signal_connect(mainWindow->buttonOtherWorkspace, "clicked", (GCallback) mainWindow_otherWorkspaceClicked, mainWindow); g_signal_connect(mainWindow->workspaceListView, "row-activated", (GCallback) mainWindow_workspaceListClicked, mainWindow); g_signal_connect(mainWindow->workspaceSearch, "changed", (GCallback) mainWindow_workspace_search_changed, mainWindow); g_signal_connect(mainWindow->buttonNew, "clicked", (GCallback) mainWindow_new_entry_clicked, mainWindow); g_signal_connect(mainWindow->buttonDecrypt, "clicked", (GCallback) mainWindow_decrypt_gpg_clicked, 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_webkit_set_enabled sets the active text renderer. * @param mainWindow the mainWindow * @param state */ void mainWindow_renderer_set(struct mainWindow *mainWindow, enum renderer_mode state) { if (mainWindow->renderer == state) return; switch (state) { case RENDERER_TEXT: gtk_combo_box_set_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode), "text"); break; case RENDERER_BASIC: gtk_combo_box_set_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode), "basic"); break; case RENDERER_HTML: gtk_combo_box_set_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode), "html"); break; default: mainWindow_renderer_set(mainWindow, RENDERER_TEXT); return; } if (state == RENDERER_HTML) { if (!mainWindow->webkitView) { mainWindow->webkitView = webkit_web_view_new(); gtk_widget_show(mainWindow->webkitView); mainWindow->webkitScrollArea = gtk_scrolled_window_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(mainWindow->webkitScrollArea), mainWindow->webkitView); gtk_stack_add_titled(GTK_STACK(mainWindow->stackRenderMode), mainWindow->webkitScrollArea, "webkit", "webkit"); gtk_widget_show(mainWindow->webkitScrollArea); } gtk_stack_set_visible_child(GTK_STACK(mainWindow->stackRenderMode), mainWindow->webkitScrollArea); } else { gtk_stack_set_visible_child(GTK_STACK(mainWindow->stackRenderMode), mainWindow->entryTextScrollArea); } mainWindow->renderer = state; } static void mainWindow_settings_combo_changed(GtkComboBox *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; if (!strcmp(gtk_combo_box_get_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode)), "text")) mainWindow_renderer_set(mainWindow, RENDERER_TEXT); else if (!strcmp(gtk_combo_box_get_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode)), "basic")) mainWindow_renderer_set(mainWindow, RENDERER_BASIC); else if (!strcmp(gtk_combo_box_get_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboDisplayMode)), "html")) mainWindow_renderer_set(mainWindow, RENDERER_HTML); mainWindow_entrySelected(NULL, user_data); } static void mainWindow_settings_comboNewDateFormat_changed(GtkComboBox *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; mainWindow_set_new_file_date_format(mainWindow, gtk_combo_box_get_active_id(GTK_COMBO_BOX(mainWindow->settingsDialog.comboNewDateFormat))); } const gchar *newDateFormats[] = {"%d.%m.%Y %H:%M", "%Y-%m-%d %H:%M", NULL}; void mainWindow_set_new_file_date_format(struct mainWindow *mainWindow, gchar *fmt) { if (!fmt) { fmt = newDateFormats[0]; } g_free(mainWindow->newFileDateFormat); mainWindow->newFileDateFormat = g_strdup(fmt); gtk_combo_box_set_active_id(mainWindow->settingsDialog.comboNewDateFormat, fmt); } static gboolean mainWindow_settings_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { gtk_widget_hide(widget); return TRUE; } void mainWindow_settings_show(struct mainWindow *mainWindow) { gtk_widget_show(mainWindow->settingsDialog.settingsDialog); } static void mainWindow_configure_settings_dialog(struct mainWindow *mainWindow) { GDateTime *datetime; int i; gchar *text; gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(mainWindow->settingsDialog.comboDisplayMode), "text", "Text only"); gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(mainWindow->settingsDialog.comboDisplayMode), "basic", "Basic formatting"); gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(mainWindow->settingsDialog.comboDisplayMode), "html", "Full HTML with images"); datetime = g_date_time_new_now_local(); for (i= 0; newDateFormats[i]; ++i) { text = g_date_time_format(datetime, newDateFormats[i]); gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(mainWindow->settingsDialog.comboNewDateFormat), newDateFormats[i], text); g_free(text); } g_date_time_unref(datetime); g_signal_connect(mainWindow->settingsDialog.comboDisplayMode, "changed", (GCallback) mainWindow_settings_combo_changed, mainWindow); g_signal_connect(mainWindow->settingsDialog.comboNewDateFormat, "changed", (GCallback) mainWindow_settings_comboNewDateFormat_changed, mainWindow); g_signal_connect(mainWindow->settingsDialog.settingsDialog, "delete-event", (GCallback) mainWindow_settings_delete_event, mainWindow); } /** * @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->mainPane = GTK_WIDGET(gtk_builder_get_object(builder, "mainPane")); 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->entryTextScrollArea = GTK_WIDGET(gtk_builder_get_object(builder, "entryTextScrollArea")); 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->headerBar = GTK_WIDGET(gtk_builder_get_object(builder, "headerBar")); mainWindow->labelDateToDate = GTK_WIDGET(gtk_builder_get_object(builder, "label_date_to_date")); mainWindow->buttonNew = GTK_WIDGET(gtk_builder_get_object(builder, "buttonNew")); mainWindow->buttonDecrypt = GTK_WIDGET(gtk_builder_get_object(builder, "buttonDecrypt")); mainWindow->stackRenderMode = GTK_WIDGET(gtk_builder_get_object(builder, "stackRenderMode")); 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->searchEntryCompletion = GTK_ENTRY_COMPLETION(gtk_builder_get_object( builder, "searchEntryCompletion")); mainWindow->buttonOtherWorkspace = GTK_WIDGET(gtk_builder_get_object(builder, "buttonOtherWorkspace")); mainWindow->workspaceSearch = GTK_WIDGET(gtk_builder_get_object(builder, "searchWorkspace")); mainWindow->popoverWorkspace = GTK_WIDGET(gtk_builder_get_object(builder, "popoverWorkspace")); mainWindow->workspaceListView = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeWorkspace")); mainWindow->workspaceListSelection = GTK_TREE_SELECTION(gtk_builder_get_object(builder, "treeWorkspaceSelection")); mainWindow->currentWorkspaceUrl = NULL; mainWindow->settingsDialog.settingsDialog = GTK_WIDGET(gtk_builder_get_object(builder, "settingsDialog")); mainWindow->settingsDialog.comboDisplayMode = GTK_WIDGET(gtk_builder_get_object(builder, "comboDisplayMode")); mainWindow->settingsDialog.comboNewDateFormat = GTK_WIDGET(gtk_builder_get_object(builder, "comboNewDateFormat")); mainWindow->filterSettings.time_start = 0; mainWindow->filterSettings.time_end = -1; mainWindow->filterSettings.hide_all = TRUE; mainWindow->time_earliest = 0; mainWindow->time_latest = 0; mainWindow->renderer = RENDERER_NONE; mainWindow->newFileDateFormat = NULL; gtk_builder_connect_signals(builder, NULL); g_object_unref(G_OBJECT(builder)); mainWindow_configure_treeView(mainWindow); mainWindow_configure_settings_dialog(mainWindow); mainWindow->webkitView = NULL; mainWindow->webkitScrollArea = NULL; mainWindow_connect_signals(mainWindow); mainWindow_configure_workspaceTreeView(mainWindow); mainWindow_init_to_default(mainWindow); mainWindow_clearSearch(NULL, mainWindow); mainWindow_configure_entry_completion(mainWindow); mainWindow_set_meta_information(mainWindow, 0, 0, TRUE); mainWindow_set_new_file_date_format(mainWindow, NULL); mainWindow_renderer_set(mainWindow, RENDERER_TEXT); return mainWindow; } /** * @brief mainWindow_set_meta_information sets some meta info for a nice GUI. It also resets the search. * @param mainWindow * @param time_earliest is the reset value for the filter start date * @param time_latest is the reset value for the filter end date */ void mainWindow_set_meta_information(struct mainWindow *mainWindow, guint time_earliest, guint time_latest, gboolean hide_all) { mainWindow->time_earliest = time_earliest; mainWindow->time_latest = time_latest; mainWindow->filterSettings.hide_all = hide_all; gtk_widget_set_sensitive(mainWindow->mainPane, !hide_all); mainWindow_clearSearch(NULL, mainWindow); } /** * @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; gtk_calendar_get_date(GTK_CALENDAR(mainWindow->calendarRange), &year, &month, &day); text = g_strdup_printf("%d-%02d-%02d", year, month + 1, day); if (mainWindow->selectedDateEntry == mainWindow->labelDateToDate) { gtk_entry_set_text(GTK_ENTRY(mainWindow->dateStart), text); gtk_entry_set_text(GTK_ENTRY(mainWindow->dateEnd), text); } else if (mainWindow->selectedDateEntry) { gtk_entry_set_text(GTK_ENTRY(mainWindow->selectedDateEntry), text); } g_free(text); } static void mainWindow_set_text(struct mainWindow *mainWindow, gchar *text, gchar *file_url) { gchar *tempfile_path = "/tmp/mdiary.tmp.md"; gchar *cmd = NULL; gchar *cmd_stdout = NULL; gchar *cmd_stderr = NULL; gint exit_status = 0; gchar *base_url = NULL; GFileOutputStream *stream = NULL; GFile *file = NULL; GtkTextIter iter; GtkTextIter iter_end; if (mainWindow->renderer == RENDERER_TEXT) { gtk_text_buffer_set_text(mainWindow->textBuffer, text, strlen(text)); } else if (mainWindow->renderer == RENDERER_BASIC) { cmd_stdout = md_pango_render(text, strlen(text)); gtk_text_buffer_set_text(mainWindow->textBuffer, "", 0); gtk_text_buffer_get_start_iter(mainWindow->textBuffer, &iter); gtk_text_buffer_insert_markup(mainWindow->textBuffer, &iter, cmd_stdout, strlen(cmd_stdout)); g_free(cmd_stdout); gtk_text_buffer_get_start_iter(mainWindow->textBuffer, &iter); gtk_text_buffer_get_end_iter(mainWindow->textBuffer, &iter_end); if (strlen(gtk_text_buffer_get_text(mainWindow->textBuffer, &iter, &iter_end, 0)) == 0) { g_warning("Could not render markdown for file. Showing plain text instead.\n"); gtk_text_buffer_set_text(mainWindow->textBuffer, text, strlen(text)); } } else { if (text[0] != '\0') { base_url = g_strdup_printf("file://%s", file_url); file = g_file_new_for_path(tempfile_path); stream = g_file_replace( file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL); g_output_stream_write(G_OUTPUT_STREAM(stream), text, strlen(text), NULL, NULL); g_output_stream_flush(G_OUTPUT_STREAM(stream), NULL, NULL); cmd = g_strdup_printf("markdown %s", tempfile_path); if (g_spawn_command_line_sync(cmd, &cmd_stdout, &cmd_stderr, &exit_status, NULL) && exit_status == 0) { webkit_web_view_load_html(WEBKIT_WEB_VIEW(mainWindow->webkitView), cmd_stdout, base_url); } else { g_warning("Could not render markdown for file. Showing plain text instead.\n"); webkit_web_view_load_html(WEBKIT_WEB_VIEW(mainWindow->webkitView), text, base_url); } g_free(base_url); } else { webkit_web_view_load_html(WEBKIT_WEB_VIEW(mainWindow->webkitView), text, ""); } if (stream) { g_output_stream_close(G_OUTPUT_STREAM(stream), NULL, NULL); g_object_unref(stream); } if (file) { g_file_delete(file, NULL, NULL); g_object_unref(file); } g_free(cmd); g_free(cmd_stdout); g_free(cmd_stderr); } } /** * @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 *file_url; 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_tree_model_get(model, &iter, COL_URL, &file_url, -1); mainWindow_set_text(mainWindow, text, file_url); g_free(text); g_free(file_url); 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 { mainWindow_set_text(mainWindow, "", ""); gtk_label_set_text(GTK_LABEL(mainWindow->labelSummary), "Please select an entry in the list above."); } } /** * @brief mainWindow_openExternalEditor opens the supplied path with the default system editor * @param mainWindow * @param path is the file to open */ void mainWindow_openExternalEditor(gchar *path) { GFile *file; GAppInfo *info; GList *files = NULL; g_info("Opening entry in external editor."); file = g_file_new_for_path(path); info = g_file_query_default_handler(file, NULL, NULL); files = g_list_append(files, (gpointer) file); g_app_info_launch(info, files, NULL, NULL); g_list_free(files); // Content is freed individually g_object_unref(info); g_object_unref(file); } /** * @brief mainWindow_entryActivated is the callback when the user doubleclicks or presses enter on an entry * @param widget is the entryListView * @param user_data struct mainWindow * */ void mainWindow_entryActivated(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; GtkTreeView *treeview = (GtkTreeView*) widget; gchar *name; model = gtk_tree_view_get_model(treeview); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COL_URL, &name, -1); mainWindow_openExternalEditor(name); g_free(name); } } /** * @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); } gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget ? widget : mainWindow->dateStart), GTK_ENTRY_ICON_SECONDARY, "edit-clear-symbolic"); } 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); } gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget ? widget : mainWindow->dateStart), GTK_ENTRY_ICON_SECONDARY, "edit-clear-symbolic"); } 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; gchar *temp; 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); temp = g_date_time_format(datetime, "%Y-%m-%d"); gtk_entry_set_text(GTK_ENTRY(widget), temp); g_free(temp); } 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; GDateTime *datetime; gchar *temp; if (widget == mainWindow->buttonClearSearch) widget = NULL; if (!widget || widget == mainWindow->searchEntry) gtk_entry_set_text(GTK_ENTRY(mainWindow->searchEntry), ""); if (!widget || widget == mainWindow->dateStart) { datetime = g_date_time_new_from_unix_local(mainWindow->time_earliest); temp = g_date_time_format(datetime, "%Y-%m-%d"); gtk_entry_set_text(GTK_ENTRY(mainWindow->dateStart), temp); g_date_time_unref(datetime); g_free(temp); gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget ? widget : mainWindow->dateStart), GTK_ENTRY_ICON_SECONDARY, ""); } if (!widget || widget == mainWindow->dateEnd) { datetime = g_date_time_new_from_unix_local(mainWindow->time_latest); temp = g_date_time_format(datetime, "%Y-%m-%d"); gtk_entry_set_text(GTK_ENTRY(mainWindow->dateEnd), temp); g_date_time_unref(datetime); g_free(temp); gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget ? widget : mainWindow->dateEnd), GTK_ENTRY_ICON_SECONDARY, ""); } } void mainWindow_dateIconPress(GtkWidget *widget, gint icon_pos, gint event, gpointer user_data) { mainWindow_clearSearch(widget, user_data); } static void mainWindow_show_error(struct mainWindow *mainWindow, gchar *text) { GtkWidget *dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(mainWindow->mainWindow), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Error: %s", text); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } void mainWindow_switch_workspace(struct mainWindow *mainWindow, gchar *path, gboolean gpg_enabled) { struct mdiary_scanner *mdiary_scanner; gchar *temp; g_print("Switching workspace to %s.\n", path); gtk_popover_popdown(GTK_POPOVER(mainWindow->popoverWorkspace)); mainWindow_set_meta_information(mainWindow, 0, 0, TRUE); mdiary_reset_store(mainWindow->entryListStore, mainWindow->completionListStore); mdiary_scanner = mdiary_scanner_new(gpg_enabled); g_free(mainWindow->currentWorkspaceUrl); mainWindow->currentWorkspaceUrl = g_strdup(path); if (mdiary_scan_to_store(mdiary_scanner, path, mainWindow->entryListStore, mainWindow->completionListStore)) { mainWindow_set_meta_information(mainWindow, mdiary_scanner->time_earliest, mdiary_scanner->time_latest, FALSE); mainWindow_add_recent_workspace(mainWindow, path, FALSE); gtk_header_bar_set_subtitle(GTK_HEADER_BAR(mainWindow->headerBar), path); if (mdiary_scanner->entries_encrypted > 0 && !gpg_enabled) gtk_widget_set_visible(mainWindow->buttonDecrypt, TRUE); else gtk_widget_set_visible(mainWindow->buttonDecrypt, FALSE); if (mdiary_scanner->entries_failed) { temp = g_strdup_printf("%d entr%s could not be read.\n" "See the console log for more information.", mdiary_scanner->entries_failed, mdiary_scanner->entries_failed > 1 ? "ies" : "y"); mainWindow_show_error(mainWindow, temp); gtk_widget_set_visible(mainWindow->buttonDecrypt, TRUE); g_free(temp); } } else { gtk_header_bar_set_subtitle(GTK_HEADER_BAR(mainWindow->headerBar), "No workspace opened."); mainWindow_show_error(mainWindow, "Could not open workspace or no entries were found in it."); } mdiary_scanner_free(mdiary_scanner); } void mainWindow_workspaceListClicked(GtkWidget *widget, GtkTreePath *tree_path, GtkTreeViewColumn *column, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *selection; gchar *path; gtk_popover_popdown(GTK_POPOVER(mainWindow->popoverWorkspace)); selection = gtk_tree_view_get_selection(mainWindow->workspaceListView); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, WSCOL_PATH, &path, -1); mainWindow_switch_workspace(mainWindow, path, FALSE); g_free(path); } } /** * @brief mainWindow_otherWorkspaceClicked is called when the user clicks the Other Workspace button. * @param widget N/A * @param event N/A * @param user_data struct mainWindow * */ void mainWindow_otherWorkspaceClicked(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GtkWidget *dialog; gint res; dialog = gtk_file_chooser_dialog_new("Choose Workspace", GTK_WINDOW(mainWindow->mainWindow), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "Cancel", GTK_RESPONSE_CANCEL, "Select", GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); res = gtk_dialog_run(GTK_DIALOG(dialog)); if (res == GTK_RESPONSE_ACCEPT) { char *directory; directory = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); mainWindow_switch_workspace(mainWindow, directory, FALSE); g_free(directory); } gtk_widget_destroy(dialog); } /** * @brief mainWindow_otherWorkspaceClicked is called when the user clicks the New Entry button. * @param widget N/A * @param user_data struct mainWindow * */ void mainWindow_new_entry_clicked(GtkWidget *widget, gpointer user_data) { void close_dialog(GtkWidget *widget, gpointer dlg) { gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK); } struct mainWindow *mainWindow = (struct mainWindow *)user_data; GtkWidget *dialog; GtkWidget *textInput; gchar *path; FILE *fd; gchar *default_path; GDateTime *datetime; int i; dialog = gtk_message_dialog_new(GTK_WINDOW(mainWindow->mainWindow), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "Please enter the filename of the entry"); textInput = gtk_entry_new(); datetime = g_date_time_new_now_local(); default_path = g_date_time_format(datetime, "%Y/%Y-%m-%d"); gtk_entry_set_text(GTK_ENTRY(textInput), default_path); g_free(default_path); gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), textInput, true, true, 4); g_signal_connect(textInput, "activate", (GCallback) close_dialog, dialog); gtk_widget_show(textInput); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { path = (gchar*) gtk_entry_get_text(GTK_ENTRY(textInput)); if (strlen(path) > 0) { if (path[0] != '/') path = g_strdup_printf("%s/%s.md", mainWindow->currentWorkspaceUrl, path); else path = g_strdup_printf("%s.md", path); fd = fopen(path, "r"); if (fd) { fclose(fd); g_warning("File '%s' already exists.", path); } else { fd = fopen(path, "w"); if (fd) { for (i = 0; default_template[i] != NULL; ++i) { if (default_template[i][0] == '\0') { default_path = g_date_time_format(datetime, mainWindow->newFileDateFormat); fwrite(default_path, sizeof(char), strlen(default_path), fd); g_free(default_path); } else { fwrite(default_template[i], sizeof(char), strlen(default_template[i]), fd); } } fclose(fd); mainWindow_openExternalEditor(path); } else { g_warning("Could not open file '%s' for writing.", path); } } } } g_date_time_unref(datetime); gtk_widget_destroy(GTK_WIDGET(dialog)); } void mainWindow_decrypt_gpg_clicked(GtkWidget *widget, gpointer user_data) { struct mainWindow *mainWindow = (struct mainWindow *)user_data; GtkTreeIter iter; gchar *temp; if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mainWindow->workspaceListStore), &iter)) { gtk_tree_model_get(GTK_TREE_MODEL(mainWindow->workspaceListStore), &iter, WSCOL_PATH, &temp, -1); mainWindow_switch_workspace(mainWindow, temp, TRUE); g_free(temp); } else { g_print("ERROR: This should never happen...\n"); } }