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 != ¬e) { 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