1 /*
2  * gnote
3  *
4  * Copyright (C) 2011-2021 Aurimas Cernius
5  * Copyright (C) 2009 Hubert Figuiere
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <glibmm/i18n.h>
26 #include <gtkmm/grid.h>
27 #include <gtkmm/image.h>
28 #include <gtkmm/separator.h>
29 #include <gtkmm/separatortoolitem.h>
30 #include <gtkmm/separatormenuitem.h>
31 
32 #include "debug.hpp"
33 #include "addinmanager.hpp"
34 #include "iconmanager.hpp"
35 #include "ignote.hpp"
36 #include "mainwindow.hpp"
37 #include "note.hpp"
38 #include "notewindow.hpp"
39 #include "notemanager.hpp"
40 #include "noteeditor.hpp"
41 #include "preferences.hpp"
42 #include "utils.hpp"
43 #include "undo.hpp"
44 #include "search.hpp"
45 #include "notebooks/notebookmanager.hpp"
46 #include "sharp/exception.hpp"
47 #include "mainwindowaction.hpp"
48 
49 
50 namespace gnote {
51 
get_icon_pin_active(IconManager & icon_manager)52   Glib::RefPtr<Gio::Icon> NoteWindow::get_icon_pin_active(IconManager & icon_manager)
53   {
54     return icon_manager.get_icon(IconManager::PIN_ACTIVE, 22);
55   }
56 
get_icon_pin_down(IconManager & icon_manager)57   Glib::RefPtr<Gio::Icon> NoteWindow::get_icon_pin_down(IconManager & icon_manager)
58   {
59     return icon_manager.get_icon(IconManager::PIN_DOWN, 22);
60   }
61 
62 
63 
NoteWindow(Note & note,IGnote & g)64   NoteWindow::NoteWindow(Note & note, IGnote & g)
65     : m_note(note)
66     , m_gnote(g)
67     , m_name(note.get_title())
68     , m_height(450)
69     , m_width(600)
70     , m_find_handler(note)
71     , m_global_keys(NULL)
72     , m_enabled(true)
73   {
74     ITagManager & tag_manager = note.manager().tag_manager();
75     m_template_tag = tag_manager.get_or_create_system_tag(ITagManager::TEMPLATE_NOTE_SYSTEM_TAG);
76     m_template_save_size_tag = tag_manager.get_or_create_system_tag(ITagManager::TEMPLATE_NOTE_SAVE_SIZE_SYSTEM_TAG);
77     m_template_save_selection_tag = tag_manager.get_or_create_system_tag(ITagManager::TEMPLATE_NOTE_SAVE_SELECTION_SYSTEM_TAG);
78     m_template_save_title_tag = tag_manager.get_or_create_system_tag(ITagManager::TEMPLATE_NOTE_SAVE_TITLE_SYSTEM_TAG);
79 
80     set_hexpand(true);
81     set_vexpand(true);
82 
83     m_text_menu = Gtk::manage(new NoteTextMenu(*this, note.get_buffer(), note.get_buffer()->undoer()));
84 
85     m_embeddable_toolbar = manage(make_toolbar());
86 
87     m_template_widget = make_template_bar();
88 
89     // The main editor widget
90     m_editor = manage(new NoteEditor(note.get_buffer(), g.preferences()));
91     m_editor->signal_populate_popup().connect(sigc::mem_fun(*this, &NoteWindow::on_populate_popup));
92     m_editor->show();
93 
94     note.get_buffer()->signal_mark_set().connect(
95       sigc::mem_fun(*this, &NoteWindow::on_selection_mark_set));
96 
97     // FIXME: I think it would be really nice to let the
98     //        window get bigger up till it grows more than
99     //        60% of the screen, and then show scrollbars.
100     m_editor_window = manage(new Gtk::ScrolledWindow());
101     m_editor_window->property_hscrollbar_policy().set_value(Gtk::POLICY_AUTOMATIC);
102     m_editor_window->property_vscrollbar_policy().set_value(Gtk::POLICY_AUTOMATIC);
103     m_editor_window->add(*m_editor);
104     m_editor_window->set_hexpand(true);
105     m_editor_window->set_vexpand(true);
106     m_editor_window->show();
107 
108     set_focus_child(*m_editor);
109 
110     attach(*m_template_widget, 0, 0, 1, 1);
111     attach(*m_editor_window, 0, 1, 1, 1);
112   }
113 
114 
~NoteWindow()115   NoteWindow::~NoteWindow()
116   {
117     delete m_global_keys;
118     m_global_keys = NULL;
119     // make sure editor is NULL. See bug 586084
120     m_editor = NULL;
121   }
122 
123 
get_name() const124   Glib::ustring NoteWindow::get_name() const
125   {
126     return m_name;
127   }
128 
set_name(const Glib::ustring & name)129   void NoteWindow::set_name(const Glib::ustring & name)
130   {
131     m_name = name;
132     signal_name_changed(m_name);
133   }
134 
foreground()135   void NoteWindow::foreground()
136   {
137     //addins may add accelarators, so accel group must be there
138     EmbeddableWidgetHost *current_host = host();
139     Gtk::Window *parent = dynamic_cast<Gtk::Window*>(current_host);
140     if(parent) {
141       add_accel_group(*parent);
142     }
143 
144     EmbeddableWidget::foreground();
145     if(parent) {
146       parent->set_focus(*m_editor);
147     }
148 
149     // Don't allow deleting the "Start Here" note...
150     if(!m_note.is_special()) {
151       m_delete_note_slot = current_host->find_action("delete-note")->signal_activate()
152         .connect(sigc::mem_fun(*this, &NoteWindow::on_delete_button_clicked));
153     }
154 
155     MainWindowAction::Ptr important_action = current_host->find_action("important-note");
156     important_action->set_state(Glib::Variant<bool>::create(m_note.is_pinned()));
157     m_important_note_slot = important_action->signal_change_state()
158       .connect(sigc::mem_fun(*this, &NoteWindow::on_pin_button_clicked));
159     m_gnote.notebook_manager().signal_note_pin_status_changed
160       .connect(sigc::mem_fun(*this, &NoteWindow::on_pin_status_changed));
161 
162   }
163 
background()164   void NoteWindow::background()
165   {
166     EmbeddableWidget::background();
167     Gtk::Window *parent = dynamic_cast<Gtk::Window*>(host());
168     if(!parent) {
169       return;
170     }
171     remove_accel_group(*parent);
172     if(parent->get_window()
173        && (parent->get_window()->get_state() & Gdk::WINDOW_STATE_MAXIMIZED) == 0) {
174       int cur_width, cur_height;
175       parent->get_size(cur_width, cur_height);
176 
177       if(!(m_note.data().width() == cur_width && m_note.data().height() == cur_height)) {
178         m_note.data().set_extent(cur_width, cur_height);
179         m_width = cur_width;
180         m_height = cur_height;
181 
182         DBG_OUT("WindowConfigureEvent queueing save");
183         m_note.queue_save(NO_CHANGE);
184       }
185     }
186 
187     m_note.save();  // to update not title immediately in notes list
188     m_delete_note_slot.disconnect();
189     m_important_note_slot.disconnect();
190   }
191 
hint_size(int & width,int & height)192   void NoteWindow::hint_size(int & width, int & height)
193   {
194     if(m_gnote.preferences().autosize_note_window()) {
195       width = m_width;
196       height = m_height;
197     }
198   }
199 
size_internals()200   void NoteWindow::size_internals()
201   {
202     m_editor->scroll_to(m_editor->get_buffer()->get_insert());
203   }
204 
add_accel_group(Gtk::Window & window)205   void NoteWindow::add_accel_group(Gtk::Window & window)
206   {
207     if(!m_accel_group) {
208       m_accel_group = Gtk::AccelGroup::create();
209       window.add_accel_group(m_accel_group);
210 
211       if(!m_global_keys) {
212         // NOTE: Since some of our keybindings are only
213         // available in the context menu, and the context menu
214         // is created on demand, register them with the
215         // global keybinder
216         m_global_keys = new utils::GlobalKeybinder(m_accel_group);
217 
218         // Open Help (F1)
219         m_global_keys->add_accelerator(sigc::mem_fun(*this, &NoteWindow::open_help_activate),
220                                        GDK_KEY_F1, (Gdk::ModifierType)0, (Gtk::AccelFlags)0);
221 
222         // Increase Indent
223         m_global_keys->add_accelerator(sigc::mem_fun(*this, &NoteWindow::change_depth_right_handler),
224                                        GDK_KEY_Right, Gdk::MOD1_MASK,
225                                        Gtk::ACCEL_VISIBLE);
226 
227         // Decrease Indent
228         m_global_keys->add_accelerator(sigc::mem_fun(*this, &NoteWindow::change_depth_left_handler),
229                                       GDK_KEY_Left, Gdk::MOD1_MASK,
230                                       Gtk::ACCEL_VISIBLE);
231         m_global_keys->enabled(m_enabled);
232       }
233 
234       m_text_menu->set_accels(*m_global_keys);
235     }
236     else {
237       window.add_accel_group(m_accel_group);
238     }
239   }
240 
remove_accel_group(Gtk::Window & window)241   void NoteWindow::remove_accel_group(Gtk::Window & window)
242   {
243     if(m_accel_group) {
244       window.remove_accel_group(m_accel_group);
245     }
246   }
247 
perform_search(const Glib::ustring & text)248   void NoteWindow::perform_search(const Glib::ustring & text)
249   {
250     get_find_handler().perform_search(text);
251   }
252 
supports_goto_result()253   bool NoteWindow::supports_goto_result()
254   {
255     return true;
256   }
257 
goto_next_result()258   bool NoteWindow::goto_next_result()
259   {
260     return get_find_handler().goto_next_result();
261   }
262 
goto_previous_result()263   bool NoteWindow::goto_previous_result()
264   {
265     return get_find_handler().goto_previous_result();
266   }
267 
embeddable_toolbar()268   Gtk::Grid *NoteWindow::embeddable_toolbar()
269   {
270     return m_embeddable_toolbar;
271   }
272 
get_popover_widgets()273   std::vector<PopoverWidget> NoteWindow::get_popover_widgets()
274   {
275     std::vector<PopoverWidget> popover_widgets;
276     popover_widgets.reserve(20);
277 
278     Gtk::Widget *new_note = utils::create_popover_button("app.new-note", _("_New Note"));
279     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_NEW, 1, new_note));
280     Gtk::Widget *new_window = utils::create_popover_button("app.new-window", _("New _Window"));
281     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_NEW, 2, new_window));
282     Gtk::Widget *undo = utils::create_popover_button("win.undo", _("_Undo"));
283     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_UNDO, 1, undo));
284     Gtk::Widget *redo = utils::create_popover_button("win.redo", _("_Redo"));
285     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_UNDO, 2, redo));
286     Gtk::Widget *link = utils::create_popover_button("win.link", _("_Link to New Note"));
287     popover_widgets.push_back(PopoverWidget::create_for_note(LINK_ORDER, link));
288     Gtk::Widget *important = utils::create_popover_button("win.important-note", C_("NoteActions", "_Important"));
289     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_FLAGS, IMPORTANT_ORDER, important));
290 
291     NoteManager & manager = static_cast<NoteManager&>(m_note.manager());
292     Note::Ptr note = std::dynamic_pointer_cast<Note>(m_note.shared_from_this());
293     for(NoteAddin *addin : manager.get_addin_manager().get_note_addins(note)) {
294       auto addin_widgets = addin->get_actions_popover_widgets();
295       popover_widgets.insert(popover_widgets.end(), addin_widgets.begin(), addin_widgets.end());
296     }
297 
298     auto delete_button = utils::create_popover_button("win.delete-note", _("_Delete…"));
299     popover_widgets.push_back(PopoverWidget(NOTE_SECTION_ACTIONS, 1000, delete_button));
300 
301     return popover_widgets;
302   }
303 
304     // Delete this Note.
305     //
306 
on_delete_button_clicked(const Glib::VariantBase &)307   void NoteWindow::on_delete_button_clicked(const Glib::VariantBase&)
308   {
309     // Prompt for note deletion
310     Note::List single_note_list;
311     single_note_list.push_back(std::static_pointer_cast<Note>(m_note.shared_from_this()));
312     noteutils::show_deletion_dialog(single_note_list, dynamic_cast<Gtk::Window*>(host()));
313   }
314 
on_selection_mark_set(const Gtk::TextIter &,const Glib::RefPtr<Gtk::TextMark> & mark)315   void NoteWindow::on_selection_mark_set(const Gtk::TextIter&, const Glib::RefPtr<Gtk::TextMark> & mark)
316   {
317     auto mark_name = mark->get_name();
318     if(mark_name == "insert" || mark_name == "selection_bound") {
319       m_text_menu->refresh_state();
320     }
321   }
322 
on_populate_popup(Gtk::Menu * menu)323   void NoteWindow::on_populate_popup(Gtk::Menu* menu)
324   {
325     menu->set_accel_group(m_accel_group);
326 
327     DBG_OUT("Populating context menu...");
328 
329     // Remove the lame-o gigantic Insert Unicode Control
330     // Characters menu item.
331     Gtk::Widget *lame_unicode;
332     std::vector<Gtk::Widget*> children(menu->get_children());
333 
334     lame_unicode = *children.rbegin();
335     menu->remove(*lame_unicode);
336 
337     Gtk::MenuItem *spacer1 = manage(new Gtk::SeparatorMenuItem());
338     spacer1->show ();
339 
340     auto link = manage(new Gtk::MenuItem(_("_Link to New Note"), true));
341     link->set_sensitive(!m_note.get_buffer()->get_selection().empty());
342     link->signal_activate().connect(sigc::mem_fun(*this, &NoteWindow::link_button_clicked));
343     link->add_accelerator("activate", m_accel_group, GDK_KEY_L,
344                           Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
345     link->show();
346 
347     Gtk::MenuItem *spacer2 = manage(new Gtk::SeparatorMenuItem());
348     spacer2->show();
349 
350     menu->prepend(*spacer1);
351     menu->prepend(*link);
352   }
353 
354   //
355   // Toolbar
356   //
357   // Add Link button, Font menu, Delete button to the window's
358   // toolbar.
359   //
make_toolbar()360   Gtk::Grid *NoteWindow::make_toolbar()
361   {
362     Gtk::Grid *grid = manage(new Gtk::Grid);
363     int grid_col = 0;
364 
365     Gtk::Button *text_button = manage(new Gtk::Button);
366     Gtk::Image *image = manage(new Gtk::Image);
367     image->property_icon_name() = "insert-text-symbolic";
368     image->property_icon_size() = GTK_ICON_SIZE_MENU;
369     text_button->set_image(*image);
370     text_button->signal_clicked()
371       .connect(sigc::mem_fun(*this, &NoteWindow::on_text_button_clicked));
372     text_button->property_margin_start() = 12;
373     text_button->show_all();
374     grid->attach(*text_button, grid_col++, 0, 1, 1);
375     text_button->set_tooltip_text(_("Set properties of text"));
376     m_text_menu->set_relative_to(*text_button);
377 
378     grid->property_margin_start() = 12;
379     grid->show_all();
380     return grid;
381   }
382 
383 
make_template_bar()384   Gtk::Grid * NoteWindow::make_template_bar()
385   {
386     Gtk::Grid * bar = manage(new Gtk::Grid);
387 
388     Gtk::Label * infoLabel = manage(new Gtk::Label(
389       _("This note is a template note. It determines the default content of regular notes, and will not show up in the note menu or search window.")));
390     infoLabel->set_line_wrap(true);
391 
392     Gtk::Button * untemplateButton = manage(new Gtk::Button(_("Convert to regular note")));
393     untemplateButton->signal_clicked().connect(sigc::mem_fun(*this, &NoteWindow::on_untemplate_button_click));
394 
395     m_save_size_check_button = manage(new Gtk::CheckButton(_("Save Si_ze"), true));
396     m_save_size_check_button->set_active(m_note.contains_tag(m_template_save_size_tag));
397     m_save_size_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_size_check_button_toggled));
398 
399     m_save_selection_check_button = manage(new Gtk::CheckButton(_("Save Se_lection"), true));
400     m_save_selection_check_button->set_active(m_note.contains_tag(m_template_save_selection_tag));
401     m_save_selection_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_selection_check_button_toggled));
402 
403     m_save_title_check_button = manage(new Gtk::CheckButton(_("Save _Title"), true));
404     m_save_title_check_button->set_active(m_note.contains_tag(m_template_save_title_tag));
405     m_save_title_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_title_check_button_toggled));
406 
407     bar->attach(*infoLabel, 0, 0, 1, 1);
408     bar->attach(*untemplateButton, 0, 1, 1, 1);
409     bar->attach(*m_save_size_check_button, 0, 2, 1, 1);
410     bar->attach(*m_save_selection_check_button, 0, 3, 1, 1);
411     bar->attach(*m_save_title_check_button, 0, 4, 1, 1);
412 
413     if(m_note.contains_tag(m_template_tag)) {
414       bar->show_all();
415     }
416 
417     m_note.signal_tag_added.connect(sigc::mem_fun(*this, &NoteWindow::on_note_tag_added));
418     m_note.signal_tag_removed.connect(sigc::mem_fun(*this, &NoteWindow::on_note_tag_removed));
419 
420     return bar;
421   }
422 
423 
on_untemplate_button_click()424   void NoteWindow::on_untemplate_button_click()
425   {
426     m_note.remove_tag(m_template_tag);
427   }
428 
429 
on_save_size_check_button_toggled()430   void NoteWindow::on_save_size_check_button_toggled()
431   {
432     if(m_save_size_check_button->get_active()) {
433       m_note.add_tag(m_template_save_size_tag);
434     }
435     else {
436       m_note.remove_tag(m_template_save_size_tag);
437     }
438   }
439 
440 
on_save_selection_check_button_toggled()441   void NoteWindow::on_save_selection_check_button_toggled()
442   {
443     if(m_save_selection_check_button->get_active()) {
444       m_note.add_tag(m_template_save_selection_tag);
445     }
446     else {
447       m_note.remove_tag(m_template_save_selection_tag);
448     }
449   }
450 
451 
on_save_title_check_button_toggled()452   void NoteWindow::on_save_title_check_button_toggled()
453   {
454     if(m_save_title_check_button->get_active()) {
455       m_note.add_tag(m_template_save_title_tag);
456     }
457     else {
458       m_note.remove_tag(m_template_save_title_tag);
459     }
460   }
461 
462 
on_note_tag_added(const NoteBase &,const Tag::Ptr & tag)463   void NoteWindow::on_note_tag_added(const NoteBase&, const Tag::Ptr & tag)
464   {
465     if(tag == m_template_tag) {
466       m_template_widget->show_all();
467     }
468   }
469 
470 
on_note_tag_removed(const NoteBase::Ptr &,const Glib::ustring & tag)471   void NoteWindow::on_note_tag_removed(const NoteBase::Ptr&, const Glib::ustring & tag)
472   {
473     if(tag == m_template_tag->normalized_name()) {
474       m_template_widget->hide();
475     }
476   }
477 
478 
479   //
480   // Link menu item activate
481   //
482   // Create a new note, names according to the buffer's selected
483   // text.  Does nothing if there is no active selection.
484   //
link_button_clicked()485   void NoteWindow::link_button_clicked()
486   {
487     Glib::ustring select = m_note.get_buffer()->get_selection();
488     if (select.empty())
489       return;
490 
491     Glib::ustring body_unused;
492     Glib::ustring title = NoteManagerBase::split_title_from_content(select, body_unused);
493     if (title.empty())
494       return;
495 
496     NoteBase::Ptr match = m_note.manager().find(title);
497     if (!match) {
498       try {
499         match = m_note.manager().create(select);
500       }
501       catch (const sharp::Exception & e) {
502         utils::HIGMessageDialog dialog(dynamic_cast<Gtk::Window*>(host()),
503           GTK_DIALOG_DESTROY_WITH_PARENT,
504           Gtk::MESSAGE_ERROR,  Gtk::BUTTONS_OK,
505           _("Cannot create note"), e.what());
506         dialog.run ();
507         return;
508       }
509     }
510     else {
511       Gtk::TextIter start, end;
512       m_note.get_buffer()->get_selection_bounds(start, end);
513       m_note.get_buffer()->remove_tag(m_note.get_tag_table()->get_broken_link_tag(), start, end);
514       m_note.get_buffer()->apply_tag(m_note.get_tag_table()->get_link_tag(), start, end);
515     }
516 
517     MainWindow::present_in(*dynamic_cast<MainWindow*>(host()), std::static_pointer_cast<Note>(match));
518   }
519 
open_help_activate()520   void NoteWindow::open_help_activate()
521   {
522     utils::show_help("gnote", "editing-notes", *dynamic_cast<Gtk::Window*>(host()));
523   }
524 
change_depth_right_handler()525   void NoteWindow::change_depth_right_handler()
526   {
527     Glib::RefPtr<NoteBuffer>::cast_static(m_editor->get_buffer())->change_cursor_depth_directional(true);
528   }
529 
change_depth_left_handler()530   void NoteWindow::change_depth_left_handler()
531   {
532     Glib::RefPtr<NoteBuffer>::cast_static(m_editor->get_buffer())->change_cursor_depth_directional(false);
533   }
534 
on_pin_status_changed(const Note & note,bool pinned)535   void NoteWindow::on_pin_status_changed(const Note & note, bool pinned)
536   {
537     if(&m_note != &note) {
538       return;
539     }
540     EmbeddableWidgetHost *h = host();
541     if(h != NULL) {
542       h->find_action("important-note")->change_state(Glib::Variant<bool>::create(pinned));
543     }
544   }
545 
on_pin_button_clicked(const Glib::VariantBase & state)546   void NoteWindow::on_pin_button_clicked(const Glib::VariantBase & state)
547   {
548     EmbeddableWidgetHost *h = host();
549     if(h != NULL) {
550       Glib::Variant<bool> new_state = Glib::VariantBase::cast_dynamic<Glib::Variant<bool> >(state);
551       m_note.set_pinned(new_state.get());
552       h->find_action("important-note")->set_state(state);
553     }
554   }
555 
on_text_button_clicked()556   void NoteWindow::on_text_button_clicked()
557   {
558     m_text_menu->show_all();
559   }
560 
enabled(bool enable)561   void NoteWindow::enabled(bool enable)
562   {
563     m_enabled = enable;
564     m_editor->set_editable(m_enabled);
565     embeddable_toolbar()->set_sensitive(m_enabled);
566     if(m_global_keys)
567       m_global_keys->enabled(m_enabled);
568   }
569 
570 
NoteFindHandler(Note & note)571   NoteFindHandler::NoteFindHandler(Note & note)
572     : m_note(note)
573   {
574   }
575 
goto_previous_result()576   bool NoteFindHandler::goto_previous_result()
577   {
578     if (m_current_matches.empty() || m_current_matches.size() == 0)
579       return false;
580 
581     Match *previous_match = nullptr;
582     for (auto & match : m_current_matches) {
583       Glib::RefPtr<NoteBuffer> buffer = match.buffer;
584       Gtk::TextIter selection_start, selection_end;
585       buffer->get_selection_bounds(selection_start, selection_end);
586       Gtk::TextIter end = buffer->get_iter_at_mark(match.start_mark);
587 
588       if (end.get_offset() < selection_start.get_offset()) {
589         previous_match = &match;
590       }
591       else {
592         break;
593       }
594     }
595     if(previous_match) {
596       jump_to_match(*previous_match);
597       return true;
598     }
599 
600     return false;
601   }
602 
goto_next_result()603   bool NoteFindHandler::goto_next_result()
604   {
605     if (m_current_matches.empty() || m_current_matches.size() == 0)
606       return false;
607 
608     for (auto & match : m_current_matches) {
609       Glib::RefPtr<NoteBuffer> buffer = match.buffer;
610       Gtk::TextIter selection_start, selection_end;
611       buffer->get_selection_bounds(selection_start, selection_end);
612       Gtk::TextIter start = buffer->get_iter_at_mark(match.start_mark);
613 
614       if (start.get_offset() >= selection_end.get_offset()) {
615         jump_to_match(match);
616         return true;
617       }
618     }
619 
620     return false;
621   }
622 
jump_to_match(const Match & match)623   void NoteFindHandler::jump_to_match(const Match & match)
624   {
625     Glib::RefPtr<NoteBuffer> buffer(match.buffer);
626 
627     Gtk::TextIter start = buffer->get_iter_at_mark(match.start_mark);
628     Gtk::TextIter end = buffer->get_iter_at_mark(match.end_mark);
629 
630     // Move cursor to end of match, and select match text
631     buffer->place_cursor(end);
632     buffer->move_mark(buffer->get_selection_bound(), start);
633 
634     Gtk::TextView *editor = m_note.get_window()->editor();
635     editor->scroll_to(buffer->get_insert());
636   }
637 
638 
perform_search(const Glib::ustring & txt)639   void NoteFindHandler::perform_search(const Glib::ustring & txt)
640   {
641     cleanup_matches();
642     if(txt.empty()) {
643       return;
644     }
645 
646     Glib::ustring text(txt);
647     text = text.lowercase();
648 
649     std::vector<Glib::ustring> words;
650     Search::split_watching_quotes(words, text);
651 
652     find_matches_in_buffer(m_note.get_buffer(), words, m_current_matches);
653 
654     if(!m_current_matches.empty()) {
655       highlight_matches(true);
656       jump_to_match(m_current_matches.front());
657     }
658   }
659 
highlight_matches(bool highlight)660   void NoteFindHandler::highlight_matches(bool highlight)
661   {
662     if(m_current_matches.empty()) {
663       return;
664     }
665 
666     for(auto & match : m_current_matches) {
667       Glib::RefPtr<NoteBuffer> buffer = match.buffer;
668 
669       if (match.highlighting != highlight) {
670         Gtk::TextIter start = buffer->get_iter_at_mark(match.start_mark);
671         Gtk::TextIter end = buffer->get_iter_at_mark(match.end_mark);
672 
673         match.highlighting = highlight;
674 
675         if (match.highlighting) {
676           buffer->apply_tag_by_name("find-match", start, end);
677         }
678         else {
679           buffer->remove_tag_by_name("find-match", start, end);
680         }
681       }
682     }
683   }
684 
685 
cleanup_matches()686   void NoteFindHandler::cleanup_matches()
687   {
688     if (!m_current_matches.empty()) {
689       highlight_matches (false /* unhighlight */);
690 
691       for(auto & match : m_current_matches) {
692         match.buffer->delete_mark(match.start_mark);
693         match.buffer->delete_mark(match.end_mark);
694       }
695 
696       m_current_matches.clear();
697     }
698   }
699 
700 
701 
find_matches_in_buffer(const Glib::RefPtr<NoteBuffer> & buffer,const std::vector<Glib::ustring> & words,std::vector<NoteFindHandler::Match> & matches)702   void NoteFindHandler::find_matches_in_buffer(const Glib::RefPtr<NoteBuffer> & buffer,
703                                                const std::vector<Glib::ustring> & words,
704                                                std::vector<NoteFindHandler::Match> & matches)
705   {
706     matches.clear();
707     Glib::ustring note_text = buffer->get_slice (buffer->begin(),
708                                                buffer->end(),
709                                                false /* hidden_chars */);
710     note_text = note_text.lowercase();
711 
712     for(std::vector<Glib::ustring>::const_iterator iter = words.begin();
713         iter != words.end(); ++iter) {
714       const Glib::ustring & word(*iter);
715       Glib::ustring::size_type idx = 0;
716       bool this_word_found = false;
717 
718       if (word.empty())
719         continue;
720 
721       while(true) {
722         idx = note_text.find(word, idx);
723         if (idx == Glib::ustring::npos) {
724           if (this_word_found) {
725             break;
726           }
727           else {
728             matches.clear();
729             return;
730           }
731         }
732 
733         this_word_found = true;
734 
735         Gtk::TextIter start = buffer->get_iter_at_offset(idx);
736         Gtk::TextIter end = start;
737         end.forward_chars(word.length());
738 
739         Match match;
740         match.buffer = buffer;
741         match.start_mark = buffer->create_mark(start, false);
742         match.end_mark = buffer->create_mark(end, true);
743         match.highlighting = false;
744 
745         matches.push_back(match);
746 
747         idx += word.length();
748       }
749     }
750   }
751 
752 
753   // FIXME: Tags applied to a word should hold over the space
754   // between the next word, as thats where you'll start typeing.
755   // Tags are only active -after- a character with that tag.  This
756   // is different from the way gtk-textbuffer applies tags.
757 
758   //
759   // Text menu
760   //
761   // Menu for font style and size, and set the active radio
762   // menuitem depending on the cursor poition.
763   //
NoteTextMenu(EmbeddableWidget & widget,const Glib::RefPtr<NoteBuffer> & buffer,UndoManager & undo_manager)764   NoteTextMenu::NoteTextMenu(EmbeddableWidget & widget, const Glib::RefPtr<NoteBuffer> & buffer, UndoManager & undo_manager)
765     : Gtk::PopoverMenu()
766     , m_widget(widget)
767     , m_buffer(buffer)
768     , m_undo_manager(undo_manager)
769     {
770       m_widget.signal_foregrounded.connect(sigc::mem_fun(*this, &NoteTextMenu::on_widget_foregrounded));
771       m_widget.signal_backgrounded.connect(sigc::mem_fun(*this, &NoteTextMenu::on_widget_backgrounded));
772 
773       set_position(Gtk::POS_BOTTOM);
774       Gtk::Box *menu_box = manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
775 
776       // Listen to events so we can sensitize and
777       // enable keybinding
778       undo_manager.signal_undo_changed().connect(sigc::mem_fun(*this, &NoteTextMenu::undo_changed));
779 
780       Glib::Quark tag_quark("Tag");
781       Gtk::Widget *bold = create_font_item("win.change-font-bold", _("_Bold"), "b");
782       Gtk::Widget *italic = create_font_item("win.change-font-italic", _("_Italic"), "i");
783       Gtk::Widget *strikeout = create_font_item("win.change-font-strikeout", _("_Strikeout"), "s");
784 
785       Gtk::Widget *highlight = manage(utils::create_popover_button("win.change-font-highlight", ""));
786       auto label = static_cast<Gtk::Label*>(static_cast<Gtk::Bin*>(highlight)->get_child());
787       Glib::ustring markup = Glib::ustring::compose("<span background=\"yellow\">%1</span>", _("_Highlight"));
788       label->set_markup_with_mnemonic(markup);
789 
790       Gtk::Widget *normal = create_font_size_item(_("_Normal"), NULL, "");
791       Gtk::Widget *small = create_font_size_item(_("S_mall"), "small", "size:small");
792       Gtk::Widget *large = create_font_size_item(_("_Large"), "large", "size:large");
793       Gtk::Widget *huge = create_font_size_item(_("Hu_ge"), "x-large", "size:huge");
794 
795       auto box = manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
796       utils::set_common_popover_widget_props(*box);
797       box->set_name("formatting");
798       box->add(*bold);
799       box->add(*italic);
800       box->add(*strikeout);
801       box->add(*highlight);
802       menu_box->add(*box);
803       menu_box->add(*manage(new Gtk::Separator));
804 
805       box = manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
806       utils::set_common_popover_widget_props(*box);
807       box->set_name("font-size");
808       box->add(*small);
809       box->add(*normal);
810       box->add(*large);
811       box->add(*huge);
812       menu_box->add(*box);
813       menu_box->add(*manage(new Gtk::Separator));
814 
815       Gtk::Widget *bullets = manage(utils::create_popover_button("win.enable-bullets", _("⦁ Bullets")));
816       menu_box->add(*bullets);
817       Gtk::Widget *increase_indent = manage(utils::create_popover_button("win.increase-indent", _("→ Increase indent")));
818       menu_box->add(*increase_indent);
819       Gtk::Widget *decrease_indent = manage(utils::create_popover_button("win.decrease-indent", _("← Decrease indent")));
820       menu_box->add(*decrease_indent);
821 
822       add(*menu_box);
823 
824       refresh_state();
825     }
826 
create_font_item(const char * action,const char * label,const char * markup)827   Gtk::Widget *NoteTextMenu::create_font_item(const char *action, const char *label, const char *markup)
828   {
829     Gtk::Widget *widget = manage(utils::create_popover_button(action, ""));
830     auto lbl = static_cast<Gtk::Label*>(static_cast<Gtk::Bin*>(widget)->get_child());
831     Glib::ustring m = Glib::ustring::compose("<%1>%2</%1>", markup, label);
832     lbl->set_markup_with_mnemonic(m);
833     return widget;
834   }
835 
create_font_size_item(const char * label,const char * markup,const char * size)836   Gtk::Widget *NoteTextMenu::create_font_size_item(const char *label, const char *markup, const char *size)
837   {
838     Gtk::Widget *item = manage(utils::create_popover_button("win.change-font-size", ""));
839     auto lbl = static_cast<Gtk::Label*>(static_cast<Gtk::Bin*>(item)->get_child());
840     Glib::ustring mrkp;
841     if(markup != NULL) {
842       mrkp = Glib::ustring::compose("<span size=\"%1\">%2</span>", markup, label);
843     }
844     else {
845       mrkp = label;
846     }
847     lbl->set_markup_with_mnemonic(mrkp);
848     gtk_actionable_set_action_target_value(GTK_ACTIONABLE(item->gobj()), g_variant_new_string(size));
849     return item;
850   }
851 
set_accels(utils::GlobalKeybinder & keybinder)852   void NoteTextMenu::set_accels(utils::GlobalKeybinder & keybinder)
853   {
854     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::undo_clicked),
855                               GDK_KEY_Z, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
856     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::redo_clicked),
857                               GDK_KEY_Z, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
858     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::link_clicked),
859                               GDK_KEY_L, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
860     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::bold_pressed),
861                               GDK_KEY_B, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
862     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::italic_pressed),
863                               GDK_KEY_I, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
864     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::strikeout_pressed),
865                               GDK_KEY_S, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
866     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::highlight_pressed),
867                               GDK_KEY_H, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
868     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::increase_font_clicked),
869                               GDK_KEY_plus, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
870     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::decrease_font_clicked),
871                               GDK_KEY_minus, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
872     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::increase_indent_pressed),
873                               GDK_KEY_Right, Gdk::MOD1_MASK, Gtk::ACCEL_VISIBLE);
874     keybinder.add_accelerator(sigc::mem_fun(*this, &NoteTextMenu::decrease_indent_pressed),
875                               GDK_KEY_Left, Gdk::MOD1_MASK, Gtk::ACCEL_VISIBLE);
876 
877     signal_set_accels(keybinder);
878   }
879 
on_show()880   void NoteTextMenu::on_show()
881   {
882     refresh_state();
883     Gtk::PopoverMenu::on_show();
884   }
885 
refresh_sizing_state()886   void NoteTextMenu::refresh_sizing_state()
887   {
888     EmbeddableWidgetHost *host = m_widget.host();
889     if(host == NULL) {
890       return;
891     }
892     auto action = host->find_action("change-font-size");
893     Gtk::TextIter cursor = m_buffer->get_iter_at_mark(m_buffer->get_insert());
894     Gtk::TextIter selection = m_buffer->get_iter_at_mark(m_buffer->get_selection_bound());
895 
896     // When on title line, activate the hidden menu item
897     if ((cursor.get_line() == 0) || (selection.get_line() == 0)) {
898       action->set_enabled(false);
899       return;
900     }
901 
902     action->set_enabled(true);
903     if(m_buffer->is_active_tag ("size:huge")) {
904       action->set_state(Glib::Variant<Glib::ustring>::create("size:huge"));
905     }
906     else if(m_buffer->is_active_tag ("size:large")) {
907       action->set_state(Glib::Variant<Glib::ustring>::create("size:large"));
908     }
909     else if(m_buffer->is_active_tag ("size:small")) {
910       action->set_state(Glib::Variant<Glib::ustring>::create("size:small"));
911     }
912     else {
913       action->set_state(Glib::Variant<Glib::ustring>::create(""));
914     }
915   }
916 
refresh_state()917   void NoteTextMenu::refresh_state ()
918   {
919     EmbeddableWidgetHost *host = m_widget.host();
920     if(host == NULL) {
921       return;
922     }
923 
924     m_event_freeze = true;
925 
926     Gtk::TextIter start, end;
927     host->find_action("link")->property_enabled() = m_buffer->get_selection_bounds(start, end);
928     host->find_action("change-font-bold")->set_state(Glib::Variant<bool>::create(m_buffer->is_active_tag("bold")));
929     host->find_action("change-font-italic")->set_state(Glib::Variant<bool>::create(m_buffer->is_active_tag("italic")));
930     host->find_action("change-font-strikeout")->set_state(Glib::Variant<bool>::create(m_buffer->is_active_tag("strikethrough")));
931     host->find_action("change-font-highlight")->set_state(Glib::Variant<bool>::create(m_buffer->is_active_tag("highlight")));
932 
933     bool inside_bullets = m_buffer->is_bulleted_list_active();
934     bool can_make_bulleted_list = m_buffer->can_make_bulleted_list();
935     auto enable_bullets = host->find_action("enable-bullets");
936     enable_bullets->set_state(Glib::Variant<bool>::create(inside_bullets));
937     enable_bullets->property_enabled() = can_make_bulleted_list;
938     host->find_action("increase-indent")->property_enabled() = inside_bullets;
939     host->find_action("decrease-indent")->property_enabled() = inside_bullets;
940 
941     refresh_sizing_state ();
942 
943     undo_changed();
944 
945     m_event_freeze = false;
946   }
947 
on_widget_foregrounded()948   void NoteTextMenu::on_widget_foregrounded()
949   {
950     auto host = m_widget.host();
951 
952     m_signal_cids.push_back(host->find_action("undo")->signal_activate()
953       .connect([this](const Glib::VariantBase&) { undo_clicked(); } ));
954     m_signal_cids.push_back(host->find_action("redo")->signal_activate()
955       .connect([this](const Glib::VariantBase&) { redo_clicked(); } ));
956     m_signal_cids.push_back(host->find_action("link")->signal_activate()
957       .connect([this](const Glib::VariantBase&) { link_clicked(); } ));
958     m_signal_cids.push_back(host->find_action("change-font-bold")->signal_change_state()
959       .connect(sigc::mem_fun(*this, &NoteTextMenu::bold_clicked)));
960     m_signal_cids.push_back(host->find_action("change-font-italic")->signal_change_state()
961       .connect(sigc::mem_fun(*this, &NoteTextMenu::italic_clicked)));
962     m_signal_cids.push_back(host->find_action("change-font-strikeout")->signal_change_state()
963       .connect(sigc::mem_fun(*this, &NoteTextMenu::strikeout_clicked)));
964     m_signal_cids.push_back(host->find_action("change-font-highlight")->signal_change_state()
965       .connect(sigc::mem_fun(*this, &NoteTextMenu::highlight_clicked)));
966     m_signal_cids.push_back(host->find_action("change-font-size")->signal_change_state()
967       .connect(sigc::mem_fun(*this, &NoteTextMenu::font_size_activated)));
968     m_signal_cids.push_back(host->find_action("enable-bullets")->signal_change_state()
969       .connect(sigc::mem_fun(*this, &NoteTextMenu::toggle_bullets_clicked)));
970     m_signal_cids.push_back(host->find_action("increase-indent")->signal_activate()
971       .connect(sigc::mem_fun(*this, &NoteTextMenu::increase_indent_clicked)));
972     m_signal_cids.push_back(host->find_action("decrease-indent")->signal_activate()
973       .connect(sigc::mem_fun(*this, &NoteTextMenu::decrease_indent_clicked)));
974   }
975 
on_widget_backgrounded()976   void NoteTextMenu::on_widget_backgrounded()
977   {
978     for(auto & cid : m_signal_cids) {
979       cid.disconnect();
980     }
981     m_signal_cids.clear();
982   }
983 
link_clicked()984   void NoteTextMenu::link_clicked()
985   {
986     if(m_event_freeze) {
987       return;
988     }
989 
990     Glib::ustring select = m_buffer->get_selection();
991     if(select.empty()) {
992       return;
993     }
994 
995     Glib::ustring body_unused;
996     Glib::ustring title = NoteManagerBase::split_title_from_content(select, body_unused);
997     if(title.empty()) {
998       return;
999     }
1000 
1001     NoteManagerBase & manager(m_buffer->note().manager());
1002     NoteBase::Ptr match = manager.find(title);
1003     if(!match) {
1004       try {
1005         match = manager.create(select);
1006       }
1007       catch(const sharp::Exception & e) {
1008         utils::HIGMessageDialog dialog(dynamic_cast<Gtk::Window*>(m_buffer->note().get_window()->host()),
1009           GTK_DIALOG_DESTROY_WITH_PARENT,
1010           Gtk::MESSAGE_ERROR,  Gtk::BUTTONS_OK,
1011           _("Cannot create note"), e.what());
1012         dialog.run();
1013         return;
1014       }
1015     }
1016     else {
1017       Gtk::TextIter start, end;
1018       m_buffer->get_selection_bounds(start, end);
1019       m_buffer->remove_tag(m_buffer->note().get_tag_table()->get_broken_link_tag(), start, end);
1020       m_buffer->apply_tag(m_buffer->note().get_tag_table()->get_link_tag(), start, end);
1021     }
1022 
1023     MainWindow::present_in(*dynamic_cast<MainWindow*>(m_buffer->note().get_window()->host()),
1024                            std::static_pointer_cast<Note>(match));
1025   }
1026 
1027   //
1028   // Font-style menu item activate
1029   //
1030   // Toggle the style tag for the current text.  Style tags are
1031   // stored in a "Tag" member of the menuitem's Data.
1032   //
font_style_clicked(const char * tag)1033   void NoteTextMenu::font_style_clicked(const char * tag)
1034   {
1035     if (m_event_freeze)
1036       return;
1037 
1038     if(tag) {
1039       m_buffer->toggle_active_tag(tag);
1040     }
1041   }
1042 
font_clicked(const char * action,const Glib::VariantBase & state,void (NoteTextMenu::* func)())1043   void NoteTextMenu::font_clicked(const char *action, const Glib::VariantBase & state, void (NoteTextMenu::*func)())
1044   {
1045     auto host = m_widget.host();
1046     if(host == NULL) {
1047       return;
1048     }
1049     host->find_action(action)->set_state(state);
1050     (this->*func)();
1051   }
1052 
bold_clicked(const Glib::VariantBase & state)1053   void NoteTextMenu::bold_clicked(const Glib::VariantBase & state)
1054   {
1055     font_clicked("change-font-bold", state, &NoteTextMenu::bold_pressed);
1056   }
1057 
bold_pressed()1058   void NoteTextMenu::bold_pressed()
1059   {
1060     font_style_clicked("bold");
1061   }
1062 
italic_clicked(const Glib::VariantBase & state)1063   void NoteTextMenu::italic_clicked(const Glib::VariantBase & state)
1064   {
1065     font_clicked("change-font-italic", state, &NoteTextMenu::italic_pressed);
1066   }
1067 
italic_pressed()1068   void NoteTextMenu::italic_pressed()
1069   {
1070     font_style_clicked("italic");
1071   }
1072 
strikeout_clicked(const Glib::VariantBase & state)1073   void NoteTextMenu::strikeout_clicked(const Glib::VariantBase & state)
1074   {
1075     font_clicked("change-font-strikeout", state, &NoteTextMenu::strikeout_pressed);
1076   }
1077 
strikeout_pressed()1078   void NoteTextMenu::strikeout_pressed()
1079   {
1080     font_style_clicked("strikethrough");
1081   }
1082 
highlight_clicked(const Glib::VariantBase & state)1083   void NoteTextMenu::highlight_clicked(const Glib::VariantBase & state)
1084   {
1085     font_clicked("change-font-highlight", state, &NoteTextMenu::highlight_pressed);
1086   }
1087 
highlight_pressed()1088   void NoteTextMenu::highlight_pressed()
1089   {
1090     font_style_clicked("highlight");
1091   }
1092 
1093   // Font-style menu item activate
font_size_activated(const Glib::VariantBase & state)1094   void NoteTextMenu::font_size_activated(const Glib::VariantBase & state)
1095   {
1096     if (m_event_freeze)
1097       return;
1098 
1099     auto host = m_widget.host();
1100     if(host == NULL) {
1101       return;
1102     }
1103     host->find_action("change-font-size")->set_state(state);
1104 
1105     m_buffer->remove_active_tag ("size:huge");
1106     m_buffer->remove_active_tag ("size:large");
1107     m_buffer->remove_active_tag ("size:small");
1108 
1109     auto tag = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(state).get();
1110     if(!tag.empty())
1111       m_buffer->set_active_tag(tag);
1112   }
1113 
increase_font_clicked()1114   void NoteTextMenu::increase_font_clicked ()
1115   {
1116     if (m_event_freeze)
1117       return;
1118 
1119     if (m_buffer->is_active_tag ("size:small")) {
1120       m_buffer->remove_active_tag ("size:small");
1121     }
1122     else if (m_buffer->is_active_tag ("size:large")) {
1123       m_buffer->remove_active_tag ("size:large");
1124       m_buffer->set_active_tag ("size:huge");
1125     }
1126     else if (m_buffer->is_active_tag ("size:huge")) {
1127       // Maximum font size, do nothing
1128     }
1129     else {
1130       // Current font size is normal
1131       m_buffer->set_active_tag ("size:large");
1132     }
1133  }
1134 
decrease_font_clicked()1135   void NoteTextMenu::decrease_font_clicked ()
1136   {
1137     if (m_event_freeze)
1138       return;
1139 
1140     if (m_buffer->is_active_tag ("size:small")) {
1141 // Minimum font size, do nothing
1142     }
1143     else if (m_buffer->is_active_tag ("size:large")) {
1144       m_buffer->remove_active_tag ("size:large");
1145     }
1146     else if (m_buffer->is_active_tag ("size:huge")) {
1147       m_buffer->remove_active_tag ("size:huge");
1148       m_buffer->set_active_tag ("size:large");
1149     }
1150     else {
1151 // Current font size is normal
1152       m_buffer->set_active_tag ("size:small");
1153     }
1154   }
1155 
undo_clicked()1156   void NoteTextMenu::undo_clicked()
1157   {
1158     if (m_undo_manager.get_can_undo()) {
1159       DBG_OUT("Running undo...");
1160       m_undo_manager.undo();
1161     }
1162   }
1163 
redo_clicked()1164   void NoteTextMenu::redo_clicked()
1165   {
1166     if (m_undo_manager.get_can_redo()) {
1167       DBG_OUT("Running redo...");
1168       m_undo_manager.redo();
1169     }
1170   }
1171 
undo_changed()1172   void NoteTextMenu::undo_changed ()
1173   {
1174     EmbeddableWidgetHost *host = m_widget.host();
1175     if(host == NULL) {
1176       return;
1177     }
1178     host->find_action("undo")->property_enabled() = m_undo_manager.get_can_undo();
1179     host->find_action("redo")->property_enabled() = m_undo_manager.get_can_redo();
1180   }
1181 
1182 
1183     //
1184     // Bulleted list handlers
1185     //
toggle_bullets_clicked(const Glib::VariantBase &)1186   void NoteTextMenu::toggle_bullets_clicked(const Glib::VariantBase&)
1187   {
1188     m_buffer->toggle_selection_bullets();
1189   }
1190 
increase_indent_clicked(const Glib::VariantBase &)1191   void NoteTextMenu::increase_indent_clicked(const Glib::VariantBase&)
1192   {
1193     m_buffer->increase_cursor_depth();
1194   }
1195 
increase_indent_pressed()1196   void NoteTextMenu::increase_indent_pressed()
1197   {
1198     m_buffer->toggle_selection_bullets();
1199   }
1200 
decrease_indent_clicked(const Glib::VariantBase &)1201   void NoteTextMenu::decrease_indent_clicked(const Glib::VariantBase&)
1202   {
1203     m_buffer->decrease_cursor_depth();
1204   }
1205 
decrease_indent_pressed()1206   void NoteTextMenu::decrease_indent_pressed()
1207   {
1208     m_buffer->decrease_cursor_depth();
1209   }
1210 
1211 }
1212