class OptionsManager:
    def __init__(self, main_window):
        self.main_window = main_window
        self.builder = main_window.builder
        self.journal = main_window.journal
        self.config = self.journal.config

        self.dialog = OptionsDialog(self.builder.get_object("options_dialog"))
        self.dialog.set_transient_for(self.main_window.main_frame)
        self.dialog.set_default_size(600, 300)
        self.dialog.add_category("general", self.builder.get_object("general_vbox"))

    def on_options_dialog(self):
        self.dialog.clear()

        # Make the config globally available
        Option.config = self.config
        Option.main_window = self.main_window

        self.options = []

        if platform.system() == "Linux" and os.path.exists("/usr/bin/rednotebook"):
            logging.debug("Running on Linux. Is installed. Adding autostart option")
            self.options.insert(0, AutostartOption())

        # Most modern Linux distributions do not have a systray anymore.
        # If this option is activated on a system without a systray, the
        # application keeps on running in the background after it has been
        # closed. The option can still be activated in the configuration file.
        if filesystem.has_system_tray():
            self.options.append(
                TickOption(
                    _("Close to system tray"),
                    "closeToTray",
                    tooltip=_("Closing the window will send RedNotebook to the tray"),
                )
            )

        # Automatic switching between preview and edit mode.
        self.options.append(
            TickOption(
                _("Switch between edit and preview mode automatically"),
                "autoSwitchMode",
            )
        )

        # Check for new version
        check_version_option = TickOption(
            _("Check for new version at startup"), "checkForNewVersion"
        )

        self.options.append(TickOption(_("Search as you type"), "instantSearch"))

        def check_version_action(widget):
            utils.check_new_version(
                self.main_window.journal, info.version, startup=False
            )
            # Apply changes from dialog to options window
            check = bool(self.journal.config.read("checkForNewVersion"))
            check_version_option.check_button.set_active(check)

        check_version_button = ActionButton(_("Check now"), check_version_action)
        check_version_option.pack_start(check_version_button, False, False, 0)
        self.options.append(check_version_option)

        self.options.extend(
            [
                # Use separate fonts since the preview often doesn't support the edit font.
                FontOption(_("Edit font:"), "mainFont"),
                TextOption(
                    _("Preview font:"),
                    "previewFont",
                    default=Config.defaults["previewFont"],
                    tooltip=_("Comma-separated font names"),
                ),
                DateFormatOption(
                    _("Date/Time format"),
                    "dateTimeString",
                    tooltip=_("Used by Date/Time button and $date$ template macro."),
                ),
                DateFormatOption(
                    _("Date format"),
                    "exportDateFormat",
                    tooltip=_("Used for dates in titlebar and exports."),
                ),
                IntegerOption(
                    _("Tags in cloud"),
                    "cloudMaxTags",
                    tooltip=_("Maximum number of tags displayed in the cloud"),
                ),
                TextOption(
                    _("Exclude from cloud"),
                    "cloudIgnoreList",
                    tooltip=_(
                        "Do not show these comma separated words and #tags in the clouds"
                    ),
                ),
                TextOption(
                    _("Include small words in cloud"),
                    "cloudIncludeList",
                    tooltip=_("Allow these words with 4 letters or less"),
                ),
            ]
        )

        self.add_all_options()

        response = self.dialog.run()

        if response == Gtk.ResponseType.OK:
            self.save_options()

            # Apply some options
            self.main_window.cloud.update_lists()
            self.main_window.cloud.update(force_update=True)

            visible = self.config.read("closeToTray") == 1
            self.main_window.tray_icon.set_visible(visible)
        else:
            # Reset some options
            self.main_window.set_font(self.config.read("mainFont", editor.DEFAULT_FONT))

        self.dialog.hide()

    def add_all_options(self):
        for option in self.options:
            self.dialog.add_option("general", option)

    def save_options(self):
        logging.debug("Saving Options")
        for option in self.options:
            value = option.get_value()
            if option.option_name is not None:
                logging.debug("Setting {} = {}".format(option.option_name, repr(value)))
                self.config[option.option_name] = value
            else:
                # We don't save the autostart setting in the config file
                option.set()     def _(string):
def get_commandline_parser(): WEEKDAYS = (
    _("Monday"),
    _("Tuesday"),
    _("Wednesday"),
    _("Thursday"),
    _("Friday"),
    _("Saturday"),
    _("Sunday"),
)
MENU_XML = """\
<ui>
<popup action="TemplateMenu">
    <menuitem action="EditWeekday"/>
    <separator name="sep4"/>
    %s
    <separator name="sep5"/>
    <menuitem action="NewTemplate"/>
</popup>
</ui>"""
class TemplateInfo(Gtk.InfoBar):
class TemplateManager:
    def __init__(self):
    def __init__(self, main_window):
    def set_template_menu_sensitive(self, sensitive):
    def _set_widgets_sensitive(self, sensitive):
    def enter_template_mode(self, title, selection):
    def exit_template_mode(self):
    def edit(self, title):
    def _replace_macros(self, text):
    def on_save_insert(self, button):
    def on_save(self, button):
    def on_close(self, button):
    def on_new_template(self, action):
        def respond(widget):
        def on_text_changed(entry):
    def _get_weekday_number(self):
    def get_path(self, title):
    def get_text(self, title):
    def get_available_template_files(self):
    def _escape_template_name(self, name):
    def get_menu(self):
            def get_edit_function(title):
    def make_empty_template_files(self): MAX_HITS = 10 ** 6
    class Browser(WebKit2.WebView):
    class HtmlView(Browser):
        def __init__(self):
        def load_html(self, html):
        def __init__(self):
        def set_font_size(self, size):
        def highlight(self, search_text):
        def on_load_changed(self, webview, event): class HtmlView(Gtk.DrawingArea):
    NOTEBOOK_URL = "file:///"
    def __init__(self):
    def load_html(self, html):
    def set_font_size(self, size):
    def get_handle(self):
    def on_timer(self):
    def on_realize(self, *_):
    def _embed_browser(self):
    def url_clicked_signal(self, url):
    def on_before_browse(self, browser, frame, request, **_):
    def on_address_change(self, browser, frame, url):
    def on_configure(self, *_):
    def on_size_allocate(self, _, data):
    def on_focus_in(self, *_):
    def shutdown(self, *_): class CategoriesTreeView:
    def __init__(self, tree_view, main_window):
    def _show_error_msg(self, text):
    def add_category(self, category):
    def node_on_top_level(self, iter):
    def on_editing_started(self, cell, editable, path):
    def edited_cb(self, cell, path, new_text, liststore):
    def check_category(self, category):
    def add_element(self, parent, element_content):
    def set_day_content(self, day):
    def get_day_content(self):
    def _get_element_content(self, element):
    def empty(self, category_iter=None):
    def clear(self):
    def get_iter_value(self, iter):
    def set_iter_value(self, iter, txt2tags_markup):
    def _get_category_iter(self, category_name):
    def add_entry(self, category, entry, undoing=False):
    def get_selected_node(self):
    def delete_node(self, iter, undoing=False):
    def delete_selected_node(self):
    def on_key_press_event(self, _view, event):
    def on_button_press_event(self, widget, event):
    def _get_context_menu(self):
    def _on_change_entry_clicked(self, action):
    def _on_add_entry_clicked(self, action):
    def _on_delete_entry_clicked(self, action):
    def on_size_allocate(self, treeview, allocation, column, cell): class ActionButton(Gtk.Button):
class UrlButton(ActionButton):
class CustomComboBoxEntry:
class CustomListView(Gtk.TreeView):
class Calendar(Gtk.Calendar):
class Info(Gtk.InfoBar):
class AssistantPage(Gtk.VBox):
class RadioButtonPage(AssistantPage):
class PathChooserPage(AssistantPage):
class Assistant(Gtk.Assistant):
class TemplateBar(Gtk.HBox):
class ToolbarMenuButton(Gtk.ToolButton):
    def __init__(self, text, action):
    def __init__(self, text, url):
    def __init__(self, combo_box):
    def add_entry(self, entry):
    def set_entries(self, value_list):
    def get_active_text(self):
    def set_active_text(self, text):
    def clear(self):
    def __init__(self, columns):
    def __init__(self, week_numbers=False):
    def set_date(self, date):
    def get_date(self):
    def __init__(self):
    def on_response(self, infobar, response_id):
    def show_message(self, title, msg, msg_type):
    def __init__(self, *args, **kwargs):
    def _add_header(self):
    def set_header(self, text):
    def __init__(self, *args, **kwargs):
    def add_radio_option(self, object, label, tooltip=""):
    def get_selected_object(self):
    def __init__(self, assistant, *args, **kwargs):
    def _remove_filters(self):
    def prepare(self, porter):
    def get_selected_path(self):
    def on_path_changed(self, widget):
    def __init__(self, journal, *args, **kwargs):
    def run(self):
    def _on_cancel(self, assistant):
    def _on_close(self, assistant):
    def _on_prepare(self, assistant, page):
    def __init__(self):
    def __init__(self, stock_id, menu):
    def _on_clicked(self, button):
    def set_menu(self, menu): DEFAULT_FONT = Gtk.Settings.get_default().get_property("gtk-font-name")
class Editor(GObject.GObject):
    def __init__(self, day_text_view):
    def replace_buffer(self, buffer):
    def day_text_buffer(self):
    def _connect_undo_signals(self):
    def set_text(self, text, undoing=False):
    def get_text(self, iter_start=None, iter_end=None):
    def insert(self, text, iter=None, overwrite=False, undoing=False):
    def replace_selection(self, text):
    def replace_selection_and_highlight(self, p1, p2, p3):
    def highlight(self, text):
    def iter_search_matches(self, text):
    def scroll_to_text(self, text):
    def get_selected_text(self):
    def get_text_left_of_selection(self, length):
    def get_text_right_of_selection(self, length):
    def sort_iters(*iters):
    def get_selection_bounds(self):
    def _get_markups(self, format, selection):
    def apply_format(self, format):
    def set_font(self, font_name):
    def hide(self):
    def can_spell_check(self):
    def is_spell_check_enabled(self):
    def _enable_spell_check(self):
    def _disable_spell_check(self):
    def enable_spell_check(self, enable=True):
    def on_drag_data_received(
        self, widget, drag_context, x, y, selection, info, timestamp
    ):
        def is_pic(uri):
    def _can_undo_redo_changed(self, undo_mgr=None): MENUITEMS_XML = """\
    <menuitem action="Bold"/>
    <menuitem action="Italic"/>
    <menuitem action="Monospace"/>
    <menuitem action="Underline"/>
    <menuitem action="Strikethrough"/>
    <menuitem action="Clear"/>
"""
TOOLBAR_XML = (
    """\
<ui>
<popup action="FormatMenu">
%s
</popup>
</ui>"""
    % MENUITEMS_XML
)
MENUBAR_XML = (
    """\
<menu action="FormatMenuBar">
%s
</menu>
"""
    % MENUITEMS_XML
)
    FORMAT_TO_MARKUP = {
        "bold": "**",
        "italic": "//",
        "monospace": "``",
        "underline": "__",
        "strikethrough": "--",
    }
class FormatMenu:
    def __init__(self, main_window):
    def setup(self):
        def apply_format(action):
        def shortcut(char):
    def on_clear_format(self, action): MENUITEMS_XML = """\
    <menuitem action="Picture"/>
    <menuitem action="File"/>
    <menuitem action="Link"/>
    <menuitem action="BulletList"/>
    <menu action="TitleMenu">
        <menuitem action="Title1"/>
        <menuitem action="Title2"/>
        <menuitem action="Title3"/>
        <menuitem action="Title4"/>
        <menuitem action="Title5"/>
    </menu>
    <menuitem action="Line"/>
    <menuitem action="Date"/>
    <menuitem action="LineBreak"/>
"""
TOOLBAR_XML = (
    """\
<ui>
<popup action="InsertMenu">
%s
</popup>
</ui>
"""
    % MENUITEMS_XML
)
MENUBAR_XML = (
    """\
<menu action="InsertMenuBar">
%s
</menu>
"""
    % MENUITEMS_XML
)
def get_image(name):
def insert_handler(callback_method):
    def insert_handler_wrapper(self, widget, *args, **kwargs):
    def __init__(self, main_window):
    def setup(self):
    def on_insert_pic(self, sel_text):
    def on_insert_file(self, sel_text):
    def on_insert_link(self, sel_text):
        def link_entered():
        def on_link_changed(widget):
        def respond(widget):
    def on_insert_bullet_list(self, sel_text):
    def on_insert_title(self, sel_text, level):
    def on_insert_line(self, sel_text):
    def on_insert_date_time(self, sel_text):
    def on_insert_line_break(self, sel_text):
class InsertMenu: def search(self, search_text):
    tags = []
    queries = []
    for part in search_text.split():
        if part.startswith("#"):
            tags.append(part.lstrip("#").lower())
        else:
            queries.append(part)

    search_text = " ".join(queries)

    # Highlight all occurences in the current day's text
    self.main_window.highlight_text(search_text)

    # Scroll to query.
    if search_text:
        GObject.idle_add(
            self.main_window.day_text_field.scroll_to_text, search_text
        )

    self.main_window.search_tree_view.update_data(search_text, tags)

    # Without the following, showing the search results sometimes lets the
    # search entry lose focus and search phrases are added to a day's text.
    if not self.entry.has_focus():
        self.entry.grab_focus() class CustomComboBoxEntry:
    def __init__(self, combo_box):
        self.combo_box = combo_box

        self.liststore = Gtk.ListStore(GObject.TYPE_STRING)
        self.entries = set()
        self.combo_box.set_model(self.liststore)
        self.combo_box.set_entry_text_column(0)
        self.entry = self.combo_box.get_child()

        # Autocompletion
        entry_completion = Gtk.EntryCompletion()
        entry_completion.set_model(self.liststore)
        entry_completion.set_minimum_key_length(1)
        entry_completion.set_text_column(0)
        self.entry.set_completion(entry_completion)

    def add_entry(self, entry):
        if entry not in self.entries:
            self.liststore.append([entry])
            self.entries.add(entry)

    def set_entries(self, value_list):
        self.clear()
        for entry in value_list:
            self.add_entry(entry)
        self.combo_box.set_model(self.liststore)

    def get_active_text(self):
        return self.entry.get_text()

    def set_active_text(self, text):
        return self.entry.set_text(text)

    def clear(self):
        self.combo_box.set_model(None)
        self.liststore.clear()
        self.entries.clear()
        self.set_active_text("")
        self.combo_box.set_model(self.liststore) class CustomListView(Gtk.TreeView):
    def __init__(self, columns):
        """
        *columns* must be a list of (header, type) pairs e.g. [('title', str)].
        """
        Gtk.TreeView.__init__(self)
        headers, types = list(zip(*columns))
        # create a TreeStore with columns to use as the model
        self.set_model(Gtk.ListStore(*types))

        columns = [Gtk.TreeViewColumn(header) for header in headers]

        # add tvcolumns to tree_view
        for index, column in enumerate(columns):
            self.append_column(column)

            # create a CellRendererText to render the data
            cell_renderer = Gtk.CellRendererText()

            # add the cell to the tvcolumn and allow it to expand
            column.pack_start(cell_renderer, True)

            # Get markup for column, not text
            column.set_attributes(cell_renderer, markup=index)

            # Allow sorting on the column
            column.set_sort_column_id(index)

        # make it searchable
        self.set_search_column(1) class ExportAssistant(Assistant):
    def __init__(self, *args, **kwargs):
        Assistant.__init__(self, *args, **kwargs)
        self.set_modal(True)

        self.exporters = get_exporters()

        self.set_title(_("Export Assistant"))

        def add_space():
            self.page1.pack_start(Gtk.Label(""), False, False, 0)

        self.page1 = RadioButtonPage()
        text = "\n".join(
            [
                _("Welcome to the Export Assistant."),
                _(
                    "This wizard will help you to export your journal to various formats."
                ),
                _(
                    "You can select the days you want to export and where the output will be saved."
                ),
            ]
        )
        welcome_label = Gtk.Label(text)
        self.page1.pack_start(welcome_label, False, False, 0)
        add_space()
        for exporter in self.exporters:
            name = exporter.NAME
            desc = exporter.DESCRIPTION
            self.page1.add_radio_option(exporter, name, desc)
        add_space()
        pdf_label = Gtk.Label(
            _("PDF: export to HTML, open in browser and print to PDF file")
        )
        pdf_label.set_halign(Gtk.Align.START)
        self.page1.pack_start(pdf_label, False, False, 0)
        self.append_page(self.page1)
        self.set_page_title(self.page1, _("Select Export Format") + " (1/5)")
        self.set_page_complete(self.page1, True)

        self.page2 = DatePage(self.journal)
        self.append_page(self.page2)
        self.set_page_title(self.page2, _("Select Date Range") + " (2/5)")
        self.set_page_complete(self.page2, True)

        self.page3 = ContentsPage(self.journal, self)
        self.append_page(self.page3)
        self.set_page_title(self.page3, _("Select Contents") + " (3/5)")
        self.set_page_complete(self.page3, True)
        self.page3.check_selection()

        self.page4 = PathChooserPage(self)
        self.append_page(self.page4)
        self.set_page_title(self.page4, _("Select Export Path") + " (4/5)")
        self.set_page_complete(self.page4, True)

        self.page5 = SummaryPage()
        self.append_page(self.page5)
        self.set_page_title(self.page5, _("Summary") + " (5/5)")
        self.set_page_type(self.page5, Gtk.AssistantPageType.CONFIRM)
        self.set_page_complete(self.page5, True)

        self.exporter = None
        self.path = None
        self.set_forward_page_func(self.pageforward)

    def pageforward(self, page):
        if page == 2 and self.page2.export_selected_text():
            return 4
        else:
            return page + 1

    def run(self):
        self.page2.refresh_dates()
        self.page3.refresh_categories_list()
        self.show_all()

    def _on_close(self, assistant):
        """
        Do the export
        """
        self.hide()
        self.export()

    def _on_prepare(self, assistant, page):
        """
        Called when a new page should be prepared, before it is shown
        """
        if page == self.page2:
            # Date Range
            self.exporter = self.page1.get_selected_object()
            self.page2.prepare()
        elif page == self.page3:
            # Categories
            start_date, end_date = self.page2.get_date_range()
            if not self.page2.export_all_days() and start_date == end_date:
                self.page3.filter_tags_button.set_active(False)
                self.page3.filter_tags_button.set_sensitive(False)
            else:
                self.page3.filter_tags_button.set_sensitive(True)
        elif page == self.page4:
            # Path
            self.page4.prepare(self.exporter)
        elif page == self.page5:
            # Summary
            self.path = self.page4.get_selected_path()
            self.page5.prepare()
            self.export_all_days = self.page2.export_all_days()
            self.export_selected_text = self.page2.export_selected_text()
            self.is_filtered = self.page3.is_filtered()
            self.exported_categories = self.page3.get_categories()

            self.page5.add_setting(_("Format"), self.exporter.NAME)
            self.page5.add_setting(
                _("Export all days"), self.yes_no(self.export_all_days)
            )
            if not self.export_all_days:
                start_date, end_date = self.page2.get_date_range()
                if start_date == end_date:
                    self.page5.add_setting(_("Date"), start_date)
                    self.page5.add_setting(
                        _("Export selected text only"),
                        self.yes_no(self.export_selected_text),
                    )
                else:
                    self.page5.add_setting(_("Start date"), start_date)
                    self.page5.add_setting(_("End date"), end_date)
            if self.export_selected_text:
                self.exported_categories = []
            else:
                self.page5.add_setting(
                    _("Include text"), self.yes_no(self.page3.is_text_included())
                )
                self.page5.add_setting(
                    _("Include tags"), self.yes_no(self.page3.is_tags_included())
                )
            if self.is_filtered:
                self.page5.add_setting(
                    _("Filtered by tags"), ", ".join(self.exported_categories)
                )
            self.page5.add_setting(_("Export path"), self.path)

    def yes_no(self, value):
        return _("Yes") if value else _("No")

    def get_export_string(self, target):
        if self.export_selected_text and self.page2.selected_text:
            markup_string = self.page2.selected_text
        else:
            if self.export_all_days:
                export_days = self.journal.days
            else:
                export_days = self.journal.get_days_in_date_range(
                    *self.page2.get_date_range()
                )

            selected_categories = self.exported_categories
            logging.debug("Selected Categories for Inclusion: %s" % selected_categories)

            markup_strings_for_each_day = []
            for day in export_days:
                include_day = True
                if self.is_filtered:
                    include_day = False
                    catagory_pairs = day.get_category_content_pairs()
                    for category in selected_categories:
                        if category in catagory_pairs:
                            include_day = True
                if include_day:
                    date_format = self.journal.config.read("exportDateFormat")
                    date_string = dates.format_date(date_format, day.date)
                    day_markup = markup.get_markup_for_day(
                        day,
                        target,
                        with_text=self.page3.is_text_included(),
                        with_tags=self.page3.is_tags_included(),
                        categories=selected_categories,
                        date=date_string,
                    )
                    markup_strings_for_each_day.append(day_markup)

            markup_string = "".join(markup_strings_for_each_day)

        return self.journal.convert(markup_string, target, options={"toc": 0})

    def export(self):
        format = self.exporter.FORMAT
        export_string = self.get_export_string(format)
        filesystem.write_file(self.path, export_string)
        self.journal.show_message(_("Content exported to %s") % self.path) class MainMenuBar:
    def __init__(self, main_window, *args, **kwargs):
        self.main_window = main_window
        self.uimanager = main_window.uimanager
        self.journal = self.main_window.journal
        self.setup()

    def get_actiongroup(self):
        # Create an ActionGroup
        actiongroup = Gtk.ActionGroup("MainMenuActionGroup")

        # Create actions
        actiongroup.add_actions(
            [
                ("Journal", None, _("_Journal")),
                (
                    "New",
                    Gtk.STOCK_NEW,
                    None,
                    "",
                    _("Create a new journal. The old one will be saved"),
                    self.on_new_journal_button_activate,
                ),
                (
                    "Open",
                    Gtk.STOCK_OPEN,
                    None,
                    None,
                    _("Load an existing journal. The old journal will be saved"),
                    self.on_open_journal_button_activate,
                ),
                ("Save", Gtk.STOCK_SAVE, None, None, None, self.on_save_button_clicked),
                (
                    "SaveAs",
                    Gtk.STOCK_SAVE_AS,
                    None,
                    None,
                    _(
                        "Save journal at a new location. The old journal files will also be saved"
                    ),
                    self.on_save_as_menu_item_activate,
                ),
                # Translators: Verb
                (
                    "Export",
                    Gtk.STOCK_CONVERT,
                    _("Export"),
                    "<Ctrl>e",
                    _("Open the export assistant"),
                    self.on_export_menu_item_activate,
                ),
                # Translators: Verb
                (
                    "Backup",
                    Gtk.STOCK_HARDDISK,
                    _("_Backup"),
                    None,
                    _("Save all the data in a zip archive"),
                    self.on_backup_activate,
                ),
                (
                    "Statistics",
                    None,
                    _("S_tatistics"),
                    None,
                    _("Show some statistics about the journal"),
                    self.on_statistics_menu_item_activate,
                ),
                (
                    "Quit",
                    Gtk.STOCK_QUIT,
                    None,
                    None,
                    _("Shutdown RedNotebook. It will not be sent to the tray."),
                    self.main_window.on_quit_activate,
                ),
                ("Edit", None, _("_Edit")),
                (
                    "Undo",
                    Gtk.STOCK_UNDO,
                    None,
                    "<Ctrl>z",
                    _("Undo text or tag edits"),
                    self.on_undo,
                ),
                (
                    "Redo",
                    Gtk.STOCK_REDO,
                    None,
                    "<Ctrl>y",
                    _("Redo text or tag edits"),
                    self.on_redo,
                ),
                ("Cut", Gtk.STOCK_CUT, None, "", None, self.on_cut_menu_item_activate),
                (
                    "Copy",
                    Gtk.STOCK_COPY,
                    None,
                    "",
                    None,
                    self.on_copy_menu_item_activate,
                ),
                (
                    "Paste",
                    Gtk.STOCK_PASTE,
                    None,
                    "",
                    None,
                    self.on_paste_menu_item_activate,
                ),
                (
                    "Fullscreen",
                    Gtk.STOCK_FULLSCREEN,
                    None,
                    "F11",
                    None,
                    self.on_fullscreen_menuitem_activate,
                ),
                (
                    "Find",
                    Gtk.STOCK_FIND,
                    None,
                    None,
                    None,
                    self.on_find_menuitem_activate,
                ),
            ]
        )
        actiongroup.add_toggle_actions(
            [
                (
                    "CheckSpelling",
                    Gtk.STOCK_SPELL_CHECK,
                    None,
                    "F7",
                    _("Underline misspelled words"),
                    self.on_checkspelling_menuitem_toggled,
                )
            ]
        )
        actiongroup.add_actions(
            [
                (
                    "Options",
                    Gtk.STOCK_PREFERENCES,
                    None,
                    "<Ctrl><Alt>p",
                    None,
                    self.on_options_menuitem_activate,
                ),
                ("HelpMenu", None, _("_Help")),
                (
                    "Help",
                    Gtk.STOCK_HELP,
                    _("Contents"),
                    "<Ctrl>h",
                    _("Open the RedNotebook documentation"),
                    self.on_help_menu_item_activate,
                ),
                (
                    "OnlineHelp",
                    None,
                    _("Get Help Online"),
                    None,
                    _("Browse answered questions or ask a new one"),
                    self.on_online_help,
                ),
                (
                    "Translate",
                    None,
                    _("Translate RedNotebook"),
                    None,
                    _("Connect to the Launchpad website to help translate RedNotebook"),
                    self.on_translate,
                ),
                (
                    "ReportBug",
                    None,
                    _("Report a Problem"),
                    None,
                    _("Fill out a short form about the problem"),
                    self.on_report_bug,
                ),
                (
                    "GiveFeedback",
                    None,
                    _("Give Feedback"),
                    None,
                    _("How can we improve RedNotebook?"),
                    self.on_give_feedback,
                ),
                ("Info", Gtk.STOCK_ABOUT, None, None, None, self.on_info_activate),
            ]
        )
        return actiongroup

    def setup(self):
        # Add the actiongroup to the uimanager
        self.uimanager.insert_action_group(self.get_actiongroup(), 0)

        # Add a UI description
        self.uimanager.add_ui_from_string(MENUBAR_XML)

    def get_menu_bar(self):
        return self.uimanager.get_widget("/MainMenuBar")

    def check_journal_dir(self, action, new_dir):
        if not new_dir:
            return False
        title = _("Wrong directory")
        # Check if dir exists and is no forbidden path (e.g. $HOME dir).
        if not self.journal.dirs.is_valid_journal_path(new_dir):
            self.journal.show_message(
                _("You cannot use this directory for your journal:") + " %s" % new_dir,
                title=title,
                error=True,
            )
            return False
        if action in ["new", "saveas"] and os.listdir(new_dir):
            self.journal.show_message(
                _("Please select an empty directory."), title=title, error=True
            )
            return False
        elif action in ["open"] and not list(storage.get_journal_files(new_dir)):
            self.journal.show_message(
                _("This directory contains no journal files:") + " " + new_dir,
                title=title,
                error=True,
            )
            return False
        return True

    def select_journal(self, action, title, message):
        new_dir = self.main_window.get_new_journal_dir(title, message)
        if not self.check_journal_dir(action, new_dir):
            return

        if action == "saveas":
            self.journal.dirs.data_dir = new_dir
            self.journal.save_to_disk(saveas=True)
        self.journal.open_journal(new_dir)

    def on_new_journal_button_activate(self, widget):
        msg = "{}\n{}".format(
            _("Journals are saved in a directory, not in a single file."),
            _("The directory name will be the title of the new journal."),
        )
        self.select_journal(
            "new", _("Select an empty folder for your new journal"), msg
        )

    def on_open_journal_button_activate(self, widget):
        self.select_journal(
            "open",
            _("Select an existing journal directory"),
            _("The directory should contain your journal's data files"),
        )

    def on_save_button_clicked(self, widget):
        self.journal.save_to_disk()

    def on_save_as_menu_item_activate(self, widget):
        # widget is None when we call this method after the journal could not be
        # saved. Then another try won't succeed either.
        if widget is not None:
            self.journal.save_to_disk()
        self.select_journal(
            "saveas",
            _("Select an empty folder for the new location of your journal"),
            _("The directory name will be the new title of the journal"),
        )

    def on_undo(self, widget):
        editor = self.main_window.day_text_field
        editor.day_text_buffer.undo()

    def on_redo(self, widget):
        editor = self.main_window.day_text_field
        editor.day_text_buffer.redo()

    def _get_active_editor_widget(self):
        if self.main_window.preview_mode:
            return self.main_window.html_editor
        else:
            return self.main_window.day_text_field.day_text_view

    def on_copy_menu_item_activate(self, widget):
        self._get_active_editor_widget().emit("copy_clipboard")

    def on_paste_menu_item_activate(self, widget):
        self._get_active_editor_widget().emit("paste_clipboard")

    def on_cut_menu_item_activate(self, widget):
        self._get_active_editor_widget().emit("cut_clipboard")

    def on_fullscreen_menuitem_activate(self, widget):
        self.main_window.toggle_fullscreen()

    def on_find_menuitem_activate(self, widget):
        """
        Change to search page and put the cursor into the search box
        """
        self.main_window.search_box.entry.grab_focus()

    def on_checkspelling_menuitem_toggled(self, widget):
        self.main_window.day_text_field.enable_spell_check(widget.get_active())
        enabled = self.main_window.day_text_field.is_spell_check_enabled()
        widget.set_active(enabled)
        self.journal.config["spellcheck"] = int(enabled)

    def on_options_menuitem_activate(self, widget):
        self.main_window.options_manager.on_options_dialog()

    def on_backup_activate(self, widget):
        self.journal.archiver.backup()

    def on_export_menu_item_activate(self, widget):
        self.journal.save_old_day()
        self.main_window.export_assistant.run()

    def on_statistics_menu_item_activate(self, widget):
        self.journal.stats.show_dialog(self.main_window.stats_dialog)

    def on_help_menu_item_activate(self, widget):
        temp_dir = self.journal.dirs.temp_dir
        filesystem.write_file(os.path.join(temp_dir, "source.txt"), help_text)
        html = self.journal.convert(
            help_text,
            "xhtml",
            headers=[_("RedNotebook Documentation"), info.version, ""],
            options={"toc": 1},
        )
        utils.show_html_in_browser(html, os.path.join(temp_dir, "help.html"))

    def on_online_help(self, widget):
        webbrowser.open(info.answers_url)

    def on_translate(self, widget):
        webbrowser.open(info.translation_url)

    def on_report_bug(self, widget):
        webbrowser.open(info.bug_url)

    def on_give_feedback(self, widget):
        webbrowser.open(info.discussion_url)

    def on_info_activate(self, widget):
        self.info_dialog = self.main_window.builder.get_object("about_dialog")
        self.info_dialog.set_transient_for(self.main_window.main_frame)
        self.info_dialog.set_program_name(info.program_name)
        self.info_dialog.set_version(info.version)
        self.info_dialog.set_copyright(info.copyright_)
        self.info_dialog.set_comments(info.tagline)
        self.info_dialog.set_website(info.url)
        self.info_dialog.set_website_label(info.url)
        self.info_dialog.set_artists(info.artists)
        self.info_dialog.set_authors(info.developers)
        self.info_dialog.add_credit_section(_("Contributors:"), [info.contributors_url])
        self.info_dialog.set_translator_credits(_("translator-credits"))
        img_path = os.path.join(filesystem.image_dir, "rednotebook-icon", "rn-128.png")
        self.info_dialog.set_logo(GdkPixbuf.Pixbuf.new_from_file(img_path))
        self.info_dialog.set_license_type(Gtk.License.GPL_2_0)
        self.info_dialog.run()
        self.info_dialog.hide() def get_year_and_month_from_date(date):
def get_date_from_date_string(date_string):
def isleap(year):
def get_number_of_days(year, month):
def format_date(format_string, date=None):
def same_month(date1, date2): ENCODING = sys.getfilesystemencoding() or locale.getlocale()[1] or "UTF-8"
LANGUAGE = locale.getdefaultlocale()[0]
REMOTE_PROTOCOLS = ["http", "ftp", "irc"]
IS_WIN = sys.platform.startswith("win")
IS_MAC = sys.platform == "darwin"
LOCAL_FILE_PEFIX = "file:///" if IS_WIN else "file://"
def has_system_tray():
def main_is_frozen():
    def __init__(self, config):
    def get_user_dir(self, config):
    def is_valid_journal_path(self, path):
    def __getattribute__(self, attr):
def read_file(filename):
def write_file(filename, content):
def make_directory(dir):
def make_directories(dirs):
def make_file(file, content=""):
def make_files(file_content_pairs):
def make_file_with_dir(file, content):
def get_relative_path(from_dir, to_dir):
def get_journal_title(dir):
def get_platform_info():
def system_call(args):
def get_peak_memory_in_kb():
class Filenames(dict): REGEX_LINEBREAK = r"\\\\[\s]*$"
REGEX_HTML_LINK = r"<a.*?>(.*?)</a>"
PIC_NAME = r"\S.*?\S|\S"
PIC_EXT = r"(?:png|jpe?g|gif|eps|bmp|svg)"
REGEX_PIC = re.compile(
    r'(\["")({})("")(\.{})(\?\d+)?(\])'.format(PIC_NAME, PIC_EXT), flags=re.I
)
REGEX_NAMED_LINK = re.compile(r'(\[)(.*?)(\s"")(\S.*?\S)(""\])', flags=re.I)
ESCAPE_COLOR = r"XBEGINCOLORX\1XSEPARATORX\2XENDCOLORX"
COLOR_ESCAPED = r"XBEGINCOLORX(.*?)XSEPARATORX(.*?)XENDCOLORX"
CHARSET_UTF8 = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'
CSS = """\
<style type="text/css">
    :root {
        color-scheme: light dark;
        --fgcolor: %(fgcolor)s;
        --bgcolor: %(bgcolor)s;
        --dark-link-color: rgb(0, 188, 212);
        --light-link-color: rgb(0, 0, 238);
    }
    a { color: var(--light-link-color); }
    body {
        font-family: %(font)s;
        background: var(--bgcolor);
        color: var(--fgcolor);
    }
    @media (prefers-color-scheme: dark) {
        a { color: var(--dark-link-color); }
    }
    <!-- Don't split last line between pages.
         This fix is only supported by Opera -->
    p {
        page-break-inside: avoid;
    }
    blockquote {
        margin: 1em 2em;
        border-left: 2px solid #999;
        font-style: oblique;
        padding-left: 1em;
    }
    blockquote:first-letter {
        margin: .2em .1em .1em 0;
        font-size: 160%%;
        font-weight: bold;
    }
    blockquote:first-line {
        font-weight: bold;
    }
    table {
        border-collapse: collapse;
    }
    td, th {
        <!--border: 1px solid #888;--> <!--Allow tables without borders-->
        padding: 3px 7px 2px 7px;
    }
    th {
        text-align: left;
        padding-top: 5px;
        padding-bottom: 4px;
        background-color: #aaa;
        color: #ffffff;
    }
    hr.heavy {
        height: 2px;
        background-color: black;
    }
</style>
"""
FORMULAS_SUPPORTED = True
MATHJAX_FILE = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"
MATHJAX_DELIMITERS = ["$$", "\\(", "\\)", r"\\[", "\\]"]
MATHJAX = """\
<!--MathJax included-->
<script type="text/javascript" src="{MATHJAX_FILE}"></script>
""".format(
    **locals()
)
def convert_categories_to_markup(categories, with_category_title=True):
def get_markup_for_day(
    day, target, with_text=True, with_tags=True, categories=None, date=None
):
def _get_config(target, options):
def _convert_paths(txt, data_dir):
    def _convert_uri(uri):
    def _convert_pic_path(match):
    def _convert_file_path(match):
def convert(txt, target, data_dir, headers=None, options=None):
def convert_to_pango(txt, headers=None, options=None):
    def replace_links(match):
def convert_from_pango(pango_markup): ENTRY_REFERENCE_URI_PATTERN = re.compile(r"^file:///#(?P<date>\d{4}-\d{2}-\d{2})$")
def get_local_url(url):
def open_url_in_browser(url):
def unquote_url(url):
def _open_url_with_call(url, prog):
def open_url(url):
def is_entry_reference_uri(uri): def sort_asc(string):
def setup_signal_handlers(journal):
    def signal_handler(signum, frame):
def get_gtk_colors(widget):
def get_new_version_number():
def _show_update_dialog(journal, current_version, new_version, startup):
def _check_new_version(journal, current_version, startup):
def check_new_version(journal, current_version, startup):
def show_html_in_browser(html, filename):
    def __init__(self, streams):
    def write(self, buf):
    def flush(self):
    def close(self):
class StreamDuplicator:
from collections import OrderedDict
from unittest import mock
from gi.repository import Gdk, GdkPixbuf, GObject, Gtk, GtkSource, Pango
from rednotebook.gui.options import OptionsManager
from rednotebook import info, templates
from rednotebook.gui import (
    browser,
    browser_cef,
    categories,
    customwidgets,
    editor,
    format_menu,
    insert_menu,
    search,
)
from rednotebook.gui.customwidgets import CustomComboBoxEntry, CustomListView
from rednotebook.gui.exports import ExportAssistant
from rednotebook.gui.menu import MainMenuBar
from rednotebook.util import dates, filesystem, markup, urls, utils
            from xml.etree import ElementTree as ET
            from rednotebook.gui import clouds
import datetime
import logging
import os
import urllib.parse
# -----------------------------------------------------------------------
# Copyright (c) 2009  Jendrik Seipp
#
# RedNotebook is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# RedNotebook is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with RedNotebook; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# -----------------------------------------------------------------------





class MainWindow:
    """
    Class that holds the reference to the main glade file and handles
    all actions
    """

    def __init__(self, journal):

        self.journal = journal

        # Load Glade file.
        # TODO: Remove workaround for Windows once it is no longer needed.
        self.gladefile = os.path.join(filesystem.files_dir, "main_window.glade")
        self.builder = Gtk.Builder()
        # Register GtkSourceView so builder can use it when loading the file
        # https://stackoverflow.com/q/10524196/434217
        GObject.type_register(GtkSource.View)
        if filesystem.IS_WIN:

            tree = ET.parse(self.gladefile)
            for node in tree.iter():
                if "translatable" in node.attrib:
                    node.text = _(node.text)
            xml_text = ET.tostring(tree.getroot(), encoding="unicode")
            self.builder = Gtk.Builder.new_from_string(xml_text, len(xml_text))
        else:
            self.builder.set_translation_domain("rednotebook")
            self.builder.add_from_file(self.gladefile)

        # Get the main window and set the icon
        self.main_frame = self.builder.get_object("main_frame")
        self.main_frame.set_title("RedNotebook")
        icon = GdkPixbuf.Pixbuf.new_from_file(
            os.path.join(filesystem.frame_icon_dir, "rn-128.png")
        )
        self.main_frame.set_icon(icon)

        self.is_fullscreen = False

        self.uimanager = Gtk.UIManager()

        # Before fetching the menubar, add all menus and actiongroups.
        # Setup the toolbar items first to avoid warnings for missing actions.

        insert_menu.InsertMenu(self)