1 /* 2 * gnote 3 * 4 * Copyright (C) 2010-2021 Aurimas Cernius 5 * Copyright (C) 2010 Debarshi Ray 6 * Copyright (C) 2009 Hubert Figuiere 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #ifdef HAVE_CONFIG_H 23 #include <config.h> 24 #endif 25 26 #include <glibmm/i18n.h> 27 #include <gtkmm/alignment.h> 28 #include <gtkmm/headerbar.h> 29 #include <gtkmm/image.h> 30 #include <gtkmm/separator.h> 31 #include <gtkmm/separatormenuitem.h> 32 #include <gtkmm/stock.h> 33 34 #include "debug.hpp" 35 #include "iactionmanager.hpp" 36 #include "iconmanager.hpp" 37 #include "ignote.hpp" 38 #include "note.hpp" 39 #include "notemanager.hpp" 40 #include "notewindow.hpp" 41 #include "recentchanges.hpp" 42 #include "sharp/string.hpp" 43 44 namespace gnote { 45 46 namespace { 47 const char *MAIN_MENU_PRIMARY_ICON = "open-menu-symbolic"; 48 const char *MAIN_MENU_SECONDARY_ICON = "view-more-symbolic"; 49 } 50 NoteRecentChanges(IGnote & g,NoteManagerBase & m)51 NoteRecentChanges::NoteRecentChanges(IGnote & g, NoteManagerBase & m) 52 : MainWindow(_("Gnote")) 53 , m_gnote(g) 54 , m_note_manager(m) 55 , m_preferences(g.preferences()) 56 , m_search_box(nullptr) 57 , m_find_next_prev_box(nullptr) 58 , m_search_entry(nullptr) 59 , m_embedded_widget(nullptr) 60 , m_mapped(false) 61 , m_entry_changed_timeout(NULL) 62 , m_window_menu_embedded(NULL) 63 , m_window_menu_default(NULL) 64 , m_accel_group(Gtk::AccelGroup::create()) 65 , m_keybinder(m_accel_group) 66 { 67 add_accel_group(m_accel_group); 68 set_default_size(450,400); 69 set_resizable(true); 70 if(g.preferences().main_window_maximized()) { 71 maximize(); 72 } 73 74 set_icon_name(IconManager::GNOTE); 75 76 m_search_notes_widget = new SearchNotesWidget(g, m); 77 m_search_notes_widget->signal_open_note 78 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_note)); 79 m_search_notes_widget->signal_open_note_new_window 80 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_open_note_new_window)); 81 m_search_notes_widget->notes_widget().signal_key_press_event() 82 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_notes_widget_key_press)); 83 84 make_header_bar(); 85 auto content = manage(new Gtk::Grid); 86 content->set_orientation(Gtk::ORIENTATION_VERTICAL); 87 int content_y_attach = 0; 88 if(use_client_side_decorations(m_preferences)) { 89 set_titlebar(*static_cast<Gtk::HeaderBar*>(m_header_bar)); 90 } 91 else { 92 content->attach(*m_header_bar, 0, content_y_attach++, 1, 1); 93 } 94 content->attach(m_embed_box, 0, content_y_attach++, 1, 1); 95 m_embed_box.set_hexpand(true); 96 m_embed_box.set_vexpand(true); 97 m_embed_box.show(); 98 content->show(); 99 100 add(*content); 101 signal_delete_event().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_delete)); 102 signal_key_press_event() 103 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_key_pressed)); 104 g.signal_quit 105 .connect(sigc::mem_fun(*this, &NoteRecentChanges::close_window));// to save size/pos 106 m_keybinder.add_accelerator(sigc::mem_fun(*this, &NoteRecentChanges::close_window), 107 GDK_KEY_W, Gdk::CONTROL_MASK, (Gtk::AccelFlags)0); 108 m_keybinder.add_accelerator(sigc::mem_fun(*this, &NoteRecentChanges::close_window), 109 GDK_KEY_Q, Gdk::CONTROL_MASK, (Gtk::AccelFlags)0); 110 111 std::map<Glib::ustring, const Glib::VariantType*> actions = g.action_manager().get_main_window_actions(); 112 for(std::map<Glib::ustring, const Glib::VariantType*>::iterator iter = actions.begin(); 113 iter != actions.end(); ++iter) { 114 MainWindowAction::Ptr action; 115 if(iter->second == NULL) { 116 add_action(action = MainWindowAction::create(iter->first)); 117 } 118 else if(iter->second == &Glib::Variant<bool>::variant_type()) { 119 add_action(action = MainWindowAction::create(iter->first, false)); 120 } 121 else if(iter->second == &Glib::Variant<gint32>::variant_type()) { 122 add_action(action = MainWindowAction::create(iter->first, 0)); 123 } 124 else if(iter->second == &Glib::Variant<Glib::ustring>::variant_type()) { 125 add_action(action = MainWindowAction::create(iter->first, Glib::ustring(""))); 126 } 127 if(action) { 128 action->is_modifying(g.action_manager().is_modifying_main_window_action(iter->first)); 129 } 130 } 131 find_action("close-window")->signal_activate() 132 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_close_window)); 133 134 embed_widget(*m_search_notes_widget); 135 } 136 137 ~NoteRecentChanges()138 NoteRecentChanges::~NoteRecentChanges() 139 { 140 if(m_entry_changed_timeout) { 141 delete m_entry_changed_timeout; 142 } 143 if(!m_search_box && m_search_text) { 144 delete m_search_text; 145 } 146 delete m_search_notes_widget; 147 } 148 make_header_bar()149 void NoteRecentChanges::make_header_bar() 150 { 151 Gtk::Grid *left_box = manage(new Gtk::Grid); 152 left_box->get_style_context()->add_class(GTK_STYLE_CLASS_RAISED); 153 left_box->set_orientation(Gtk::ORIENTATION_HORIZONTAL); 154 left_box->set_valign(Gtk::ALIGN_CENTER); 155 m_all_notes_button = manage(new Gtk::Button); 156 Gtk::Image *image = manage(new Gtk::Image); 157 image->property_icon_name() = "go-previous-symbolic"; 158 image->property_icon_size() = GTK_ICON_SIZE_MENU; 159 m_all_notes_button->set_image(*image); 160 m_all_notes_button->set_tooltip_text(_("All Notes")); 161 m_all_notes_button->signal_clicked().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_all_notes_button_clicked)); 162 m_all_notes_button->add_accelerator("activate", m_accel_group, GDK_KEY_comma, Gdk::CONTROL_MASK, (Gtk::AccelFlags) 0); 163 m_all_notes_button->show_all(); 164 left_box->attach(*m_all_notes_button, 0, 0, 1, 1); 165 166 m_new_note_button = manage(new Gtk::Button); 167 image = manage(new Gtk::Image); 168 image->property_icon_name() = "list-add-symbolic"; 169 image->property_icon_size() = GTK_ICON_SIZE_MENU; 170 m_new_note_button->set_image(*image); 171 m_new_note_button->set_tooltip_text(_("Create New Note")); 172 m_new_note_button->add_accelerator("activate", m_accel_group, GDK_KEY_N, Gdk::CONTROL_MASK, (Gtk::AccelFlags) 0); 173 m_new_note_button->signal_clicked().connect(sigc::mem_fun(*m_search_notes_widget, &SearchNotesWidget::new_note)); 174 m_new_note_button->show_all(); 175 left_box->attach(*m_new_note_button, 1, 0, 1, 1); 176 left_box->show(); 177 178 m_embedded_toolbar.set_margin_start(6); 179 m_embedded_toolbar.set_halign(Gtk::ALIGN_START); 180 m_embedded_toolbar.set_valign(Gtk::ALIGN_CENTER); 181 m_embedded_toolbar.show(); 182 183 Gtk::Grid *right_box = manage(new Gtk::Grid); 184 right_box->set_orientation(Gtk::ORIENTATION_HORIZONTAL); 185 right_box->set_column_spacing(5); 186 right_box->set_valign(Gtk::ALIGN_CENTER); 187 image = manage(new Gtk::Image); 188 image->property_icon_name() = "edit-find-symbolic"; 189 image->property_icon_size() = GTK_ICON_SIZE_MENU; 190 m_search_button.set_image(*image); 191 m_search_button.signal_toggled().connect(sigc::mem_fun(*this, &NoteRecentChanges::on_search_button_toggled)); 192 m_search_button.add_accelerator("activate", m_accel_group, GDK_KEY_F, Gdk::CONTROL_MASK, (Gtk::AccelFlags) 0); 193 m_search_button.set_tooltip_text(_("Search")); 194 m_search_button.show_all(); 195 right_box->attach(m_search_button, 0, 0, 1, 1); 196 197 m_window_actions_button = manage(new Gtk::Button); 198 image = manage(new Gtk::Image); 199 image->property_icon_name() = MAIN_MENU_PRIMARY_ICON; 200 image->property_icon_size() = GTK_ICON_SIZE_MENU; 201 m_window_actions_button->set_image(*image); 202 m_window_actions_button->signal_clicked().connect( 203 sigc::mem_fun(*this, &NoteRecentChanges::on_show_window_menu)); 204 m_window_actions_button->add_accelerator( 205 "activate", m_accel_group, GDK_KEY_F10, (Gdk::ModifierType) 0, (Gtk::AccelFlags) 0); 206 m_window_actions_button->show_all(); 207 right_box->attach(*m_window_actions_button, 1, 0, 1, 1); 208 right_box->show(); 209 210 if(use_client_side_decorations(m_preferences)) { 211 Gtk::HeaderBar *header_bar = manage(new Gtk::HeaderBar); 212 header_bar->set_show_close_button(true); 213 header_bar->pack_start(*left_box); 214 header_bar->pack_end(*right_box); 215 header_bar->pack_end(m_embedded_toolbar); 216 m_header_bar = header_bar; 217 } 218 else { 219 Gtk::Grid *header_bar = manage(new Gtk::Grid); 220 header_bar->set_margin_start(5); 221 header_bar->set_margin_end(5); 222 header_bar->set_margin_top(5); 223 header_bar->set_margin_bottom(5); 224 header_bar->attach(*left_box, 0, 0, 1, 1); 225 left_box->set_hexpand(true); 226 header_bar->attach(m_embedded_toolbar, 2, 0, 1, 1); 227 header_bar->attach(*right_box, 3, 0, 1, 1); 228 m_header_bar = header_bar; 229 } 230 231 m_header_bar->show(); 232 } 233 make_search_box()234 void NoteRecentChanges::make_search_box() 235 { 236 if(m_search_box) { 237 return; 238 } 239 240 Glib::ustring search_text; 241 if(m_search_text) { 242 search_text = *m_search_text; 243 delete m_search_text; 244 } 245 m_search_entry = manage(new Gtk::SearchEntry); 246 m_search_entry->set_text(search_text); 247 m_search_entry->set_activates_default(false); 248 m_search_entry->set_size_request(300); 249 m_search_entry->signal_key_press_event() 250 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_key_pressed), false); 251 m_search_entry->signal_changed() 252 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_changed)); 253 m_search_entry->signal_activate() 254 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_entry_activated)); 255 m_search_entry->show(); 256 257 m_search_box = manage(new Gtk::Grid); 258 m_search_box->set_hexpand(false); 259 m_search_box->attach(*m_search_entry, 0, 0, 1, 1); 260 m_search_box->set_halign(Gtk::ALIGN_CENTER); 261 262 auto content = dynamic_cast<Gtk::Grid*>(m_embed_box.get_parent()); 263 if(content) { 264 content->attach_next_to(*m_search_box, m_embed_box, Gtk::POS_TOP); 265 } 266 else { 267 ERR_OUT(_("Parent of embed box is not a Gtk::Grid, please report a bug!")); 268 } 269 } 270 make_find_next_prev()271 void NoteRecentChanges::make_find_next_prev() 272 { 273 if(m_find_next_prev_box) { 274 return; 275 } 276 277 m_find_next_prev_box = manage(new Gtk::Grid); 278 m_find_next_prev_box->set_margin_start(5); 279 280 Gtk::Button *find_next_button = manage(new Gtk::Button); 281 Gtk::Image *image = manage(new Gtk::Image); 282 image->property_icon_name() = "go-down-symbolic"; 283 image->property_icon_size() = GTK_ICON_SIZE_MENU; 284 find_next_button->set_image(*image); 285 find_next_button->set_always_show_image(true); 286 find_next_button->signal_clicked() 287 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_find_next_button_clicked)); 288 find_next_button->add_accelerator("activate", m_accel_group, GDK_KEY_G, Gdk::CONTROL_MASK, (Gtk::AccelFlags) 0); 289 find_next_button->show(); 290 m_find_next_prev_box->attach(*find_next_button, 0, 0, 1, 1); 291 292 Gtk::Button *find_prev_button = manage(new Gtk::Button); 293 image = manage(new Gtk::Image); 294 image->property_icon_name() = "go-up-symbolic"; 295 image->property_icon_size() = GTK_ICON_SIZE_MENU; 296 find_prev_button->set_image(*image); 297 find_prev_button->set_always_show_image(true); 298 find_prev_button->signal_clicked() 299 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_find_prev_button_clicked)); 300 find_prev_button->add_accelerator("activate", m_accel_group, GDK_KEY_G, Gdk::CONTROL_MASK|Gdk::SHIFT_MASK, (Gtk::AccelFlags) 0); 301 find_prev_button->show(); 302 m_find_next_prev_box->attach(*find_prev_button, 1, 0, 1, 1); 303 304 auto grid = dynamic_cast<Gtk::Grid*>(m_search_entry->get_parent()); 305 if(grid) { 306 grid->attach(*m_find_next_prev_box, 1, 0, 1, 1); 307 } 308 else { 309 ERR_OUT(_("Parent of search entry is not Gtk::Grid, please report a bug!")); 310 } 311 } 312 on_search_button_toggled()313 void NoteRecentChanges::on_search_button_toggled() 314 { 315 if(m_search_button.get_active()) { 316 show_search_bar(); 317 } 318 else { 319 m_search_box->hide(); 320 SearchableItem *searchable_widget = dynamic_cast<SearchableItem*>(currently_foreground()); 321 if(searchable_widget) { 322 searchable_widget->perform_search(""); 323 } 324 } 325 } 326 on_find_next_button_clicked()327 void NoteRecentChanges::on_find_next_button_clicked() 328 { 329 SearchableItem *searchable_widget = dynamic_cast<SearchableItem*>(currently_foreground()); 330 if(searchable_widget) { 331 searchable_widget->goto_next_result(); 332 } 333 } 334 on_find_prev_button_clicked()335 void NoteRecentChanges::on_find_prev_button_clicked() 336 { 337 SearchableItem *searchable_widget = dynamic_cast<SearchableItem*>(currently_foreground()); 338 if(searchable_widget) { 339 searchable_widget->goto_previous_result(); 340 } 341 } 342 show_search_bar(bool focus)343 void NoteRecentChanges::show_search_bar(bool focus) 344 { 345 make_search_box(); 346 if(m_search_box->get_visible()) { 347 focus = false; 348 } 349 m_search_box->show(); 350 if(focus) { 351 m_search_entry->grab_focus(); 352 } 353 Glib::ustring text = m_search_entry->get_text(); 354 update_search_bar(*currently_foreground(), text != ""); 355 } 356 update_search_bar(EmbeddableWidget & widget,bool perform_search)357 void NoteRecentChanges::update_search_bar(EmbeddableWidget & widget, bool perform_search) 358 { 359 SearchableItem *searchable_item = dynamic_cast<SearchableItem*>(&widget); 360 if(searchable_item) { 361 m_search_button.show(); 362 if(searchable_item->supports_goto_result()) { 363 if(m_search_box && m_search_box->get_visible()) { 364 make_find_next_prev(); 365 m_find_next_prev_box->show(); 366 } 367 } 368 else { 369 if(m_find_next_prev_box) { 370 m_find_next_prev_box->hide(); 371 } 372 } 373 if(perform_search) { 374 searchable_item->perform_search(m_search_button.get_active() ? m_search_entry->get_text() : ""); 375 } 376 } 377 else { 378 m_search_button.set_active(false); 379 m_search_button.hide(); 380 } 381 } 382 present_search()383 void NoteRecentChanges::present_search() 384 { 385 EmbeddableWidget *current = currently_foreground(); 386 if(m_search_notes_widget == dynamic_cast<SearchNotesWidget*>(current)) { 387 return; 388 } 389 if(current) { 390 background_embedded(*current); 391 } 392 foreground_embedded(*m_search_notes_widget); 393 } 394 present_note(const Note::Ptr & note)395 void NoteRecentChanges::present_note(const Note::Ptr & note) 396 { 397 embed_widget(*note->create_window()); 398 } 399 400 new_note()401 void NoteRecentChanges::new_note() 402 { 403 std::vector<Gtk::Widget*> current = m_embed_box.get_children(); 404 SearchNotesWidget *search_wgt = dynamic_cast<SearchNotesWidget*>(current.size() > 0 ? current[0] : NULL); 405 if(search_wgt) { 406 search_wgt->new_note(); 407 } 408 else { 409 present_note(std::static_pointer_cast<Note>(m_note_manager.create())); 410 } 411 } 412 413 on_open_note(const Note::Ptr & note)414 void NoteRecentChanges::on_open_note(const Note::Ptr & note) 415 { 416 if(m_preferences.open_notes_in_new_window()) { 417 on_open_note_new_window(note); 418 } 419 else { 420 if(!present_active(note)) { 421 present_note(note); 422 } 423 } 424 } 425 on_open_note_new_window(const Note::Ptr & note)426 void NoteRecentChanges::on_open_note_new_window(const Note::Ptr & note) 427 { 428 present_in_new_window(m_gnote, note, m_preferences.enable_close_note_on_escape()); 429 } 430 on_delete_note()431 void NoteRecentChanges::on_delete_note() 432 { 433 m_search_notes_widget->delete_selected_notes(); 434 } 435 436 437 close_window()438 void NoteRecentChanges::close_window() 439 { 440 Glib::RefPtr<Gdk::Window> win = get_window(); 441 // background window (for tray to work) might not have GDK window 442 if(win) { 443 m_preferences.main_window_maximized(win->get_state() & Gdk::WINDOW_STATE_MAXIMIZED); 444 } 445 std::vector<Gtk::Widget*> current = m_embed_box.get_children(); 446 for(std::vector<Gtk::Widget*>::iterator iter = current.begin(); 447 iter != current.end(); ++iter) { 448 EmbeddableWidget *widget = dynamic_cast<EmbeddableWidget*>(*iter); 449 if(widget) { 450 background_embedded(*widget); 451 } 452 } 453 if(m_embedded_widget) { 454 m_embedded_widget->unembed(); 455 m_embedded_widget = nullptr; 456 } 457 458 hide(); 459 } 460 461 is_search()462 bool NoteRecentChanges::is_search() 463 { 464 return m_search_notes_widget == currently_foreground(); 465 } 466 467 on_close_window(const Glib::VariantBase &)468 void NoteRecentChanges::on_close_window(const Glib::VariantBase&) 469 { 470 close_window(); 471 } 472 473 on_delete(GdkEventAny *)474 bool NoteRecentChanges::on_delete(GdkEventAny *) 475 { 476 close_window(); 477 return true; 478 } 479 on_key_pressed(GdkEventKey * ev)480 bool NoteRecentChanges::on_key_pressed(GdkEventKey *ev) 481 { 482 guint keyval; 483 gdk_event_get_keyval((GdkEvent*)ev, &keyval); 484 switch(keyval) { 485 case GDK_KEY_Escape: 486 if(m_search_button.get_active()) { 487 m_search_entry->set_text(""); 488 m_search_button.set_active(false); 489 } 490 // Allow Escape to close the window 491 else if(close_on_escape()) { 492 close_window(); 493 } 494 else if(m_preferences.enable_close_note_on_escape()) { 495 EmbeddableWidget *current_item = currently_foreground(); 496 if(current_item) { 497 background_embedded(*current_item); 498 } 499 foreground_embedded(*m_search_notes_widget); 500 } 501 break; 502 case GDK_KEY_F1: 503 utils::show_help("gnote", "", *this); 504 break; 505 default: 506 break; 507 } 508 return true; 509 } 510 511 on_show()512 void NoteRecentChanges::on_show() 513 { 514 // Select "All Notes" in the notebooks list 515 m_search_notes_widget->select_all_notes_notebook(); 516 517 EmbeddableWidget *widget = m_embedded_widget ? m_embedded_widget : m_search_notes_widget; 518 foreground_embedded(*widget); 519 520 MainWindow::on_show(); 521 522 if(widget) { 523 int x = 0, y = 0; 524 widget->hint_position(x, y); 525 if(x && y) { 526 move(x, y); 527 } 528 } 529 } 530 set_search_text(const Glib::ustring & value)531 void NoteRecentChanges::set_search_text(const Glib::ustring & value) 532 { 533 if(m_search_box) { 534 m_search_entry->set_text(value); 535 } 536 else { 537 if(!m_search_text) { 538 m_search_text = new Glib::ustring(value); 539 } 540 else { 541 *m_search_text = value; 542 } 543 } 544 } 545 embed_widget(EmbeddableWidget & widget)546 void NoteRecentChanges::embed_widget(EmbeddableWidget & widget) 547 { 548 EmbeddableWidget *current = currently_foreground(); 549 if(current == &widget) { 550 return; 551 } 552 if(m_embedded_widget) { 553 unembed_widget(*m_embedded_widget); 554 } 555 m_embedded_widget = &widget; 556 widget.embed(this); 557 if(get_visible()) { 558 foreground_embedded(widget); 559 } 560 } 561 unembed_widget(EmbeddableWidget & widget)562 void NoteRecentChanges::unembed_widget(EmbeddableWidget & widget) 563 { 564 bool show_search = false; 565 if(&widget == m_embedded_widget) { 566 if(is_foreground(widget)) { 567 background_embedded(widget); 568 show_search = true; 569 } 570 m_embedded_widget = nullptr; 571 widget.unembed(); 572 } 573 if(show_search) { 574 foreground_embedded(*m_search_notes_widget); 575 } 576 } 577 foreground_embedded(EmbeddableWidget & widget)578 void NoteRecentChanges::foreground_embedded(EmbeddableWidget & widget) 579 { 580 try { 581 EmbeddableWidget *current_foreground = currently_foreground(); 582 if(current_foreground == &widget) { 583 return; 584 } 585 else if(current_foreground) { 586 background_embedded(*current_foreground); 587 } 588 Gtk::Widget &wid = dynamic_cast<Gtk::Widget&>(widget); 589 m_embed_box.add(wid); 590 wid.show(); 591 widget.foreground(); 592 593 bool maximized = m_preferences.main_window_maximized(); 594 if(get_realized()) { 595 //if window is showing, use actual state 596 maximized = get_window()->get_state() & Gdk::WINDOW_STATE_MAXIMIZED; 597 } 598 int width = 0, height = 0; 599 widget.hint_size(width, height); 600 if(width && height) { 601 set_default_size(width, height); 602 if(!maximized && get_visible()) { 603 resize(width, height); 604 } 605 } 606 widget.size_internals(); 607 608 update_toolbar(widget); 609 if(&widget == m_search_notes_widget) { 610 set_title(_("Gnote")); 611 } 612 else { 613 set_title(widget.get_name()); 614 m_current_embedded_name_slot = widget.signal_name_changed 615 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_embedded_name_changed)); 616 } 617 } 618 catch(std::bad_cast&) { 619 } 620 621 try { 622 HasActions &has_actions = dynamic_cast<HasActions&>(widget); 623 if(m_window_menu_embedded) { 624 m_window_menu_embedded = NULL; 625 } 626 m_signal_popover_widgets_changed_cid = has_actions.signal_popover_widgets_changed 627 .connect(sigc::mem_fun(*this, &NoteRecentChanges::on_popover_widgets_changed)); 628 } 629 catch(std::bad_cast&) { 630 } 631 } 632 background_embedded(EmbeddableWidget & widget)633 void NoteRecentChanges::background_embedded(EmbeddableWidget & widget) 634 { 635 try { 636 if(currently_foreground() != &widget) { 637 return; 638 } 639 Gtk::Widget &wid = dynamic_cast<Gtk::Widget&>(widget); 640 widget.background(); 641 m_embed_box.remove(wid); 642 m_signal_popover_widgets_changed_cid.disconnect(); 643 m_current_embedded_name_slot.disconnect(); 644 645 if(m_window_menu_embedded) { 646 m_window_menu_embedded = NULL; 647 } 648 } 649 catch(std::bad_cast&) { 650 } 651 652 auto children = m_embedded_toolbar.get_children(); 653 for(auto child : children) { 654 m_embedded_toolbar.remove(*child); 655 } 656 } 657 contains(EmbeddableWidget & widget)658 bool NoteRecentChanges::contains(EmbeddableWidget & widget) 659 { 660 if(&widget == m_search_notes_widget) { 661 return true; 662 } 663 664 return &widget == m_embedded_widget; 665 } 666 is_foreground(EmbeddableWidget & widget)667 bool NoteRecentChanges::is_foreground(EmbeddableWidget & widget) 668 { 669 for(Gtk::Widget *wgt : m_embed_box.get_children()) { 670 if(dynamic_cast<EmbeddableWidget*>(wgt) == &widget) { 671 return true; 672 } 673 } 674 675 return false; 676 } 677 add_action(const MainWindowAction::Ptr & action)678 void NoteRecentChanges::add_action(const MainWindowAction::Ptr & action) 679 { 680 m_actions[action->get_name()] = action; 681 MainWindow::add_action(action); 682 } 683 find_action(const Glib::ustring & name)684 MainWindowAction::Ptr NoteRecentChanges::find_action(const Glib::ustring & name) 685 { 686 std::map<Glib::ustring, MainWindowAction::Ptr>::iterator iter = m_actions.find(name); 687 if(iter != m_actions.end()) { 688 return iter->second; 689 } 690 return MainWindowAction::Ptr(); 691 } 692 enabled(bool is_enabled)693 void NoteRecentChanges::enabled(bool is_enabled) 694 { 695 for(auto & iter : m_actions) { 696 if(iter.second->is_modifying()) { 697 iter.second->set_enabled(is_enabled); 698 } 699 } 700 } 701 currently_foreground()702 EmbeddableWidget *NoteRecentChanges::currently_foreground() 703 { 704 std::vector<Gtk::Widget*> children = m_embed_box.get_children(); 705 return children.size() ? dynamic_cast<EmbeddableWidget*>(children[0]) : NULL; 706 } 707 on_map_event(GdkEventAny * evt)708 bool NoteRecentChanges::on_map_event(GdkEventAny *evt) 709 { 710 bool res = MainWindow::on_map_event(evt); 711 if(!m_mapped) { 712 auto widget = currently_foreground(); 713 if(widget) { 714 widget->set_initial_focus(); 715 } 716 } 717 m_mapped = true; 718 return res; 719 } 720 on_entry_key_pressed(GdkEventKey * ev)721 bool NoteRecentChanges::on_entry_key_pressed(GdkEventKey *ev) 722 { 723 guint keyval; 724 gdk_event_get_keyval((GdkEvent*)ev, &keyval); 725 switch(keyval) { 726 case GDK_KEY_Escape: 727 m_search_entry->set_text(""); 728 m_search_button.set_active(false); 729 } 730 731 return false; 732 } 733 on_entry_changed()734 void NoteRecentChanges::on_entry_changed() 735 { 736 if(!m_search_box || !m_search_box->get_visible()) { 737 return; 738 } 739 if(m_entry_changed_timeout == NULL) { 740 m_entry_changed_timeout = new utils::InterruptableTimeout(); 741 m_entry_changed_timeout->signal_timeout 742 .connect(sigc::mem_fun(*this, &NoteRecentChanges::entry_changed_timeout)); 743 } 744 745 Glib::ustring search_text = get_search_text(); 746 if(search_text.empty()) { 747 SearchableItem *searchable_widget = dynamic_cast<SearchableItem*>(currently_foreground()); 748 if(searchable_widget) { 749 searchable_widget->perform_search(search_text); 750 } 751 } 752 else { 753 m_entry_changed_timeout->reset(500); 754 } 755 } 756 on_entry_activated()757 void NoteRecentChanges::on_entry_activated() 758 { 759 if(m_entry_changed_timeout) { 760 m_entry_changed_timeout->cancel(); 761 } 762 763 entry_changed_timeout(); 764 } 765 entry_changed_timeout()766 void NoteRecentChanges::entry_changed_timeout() 767 { 768 if(!m_search_box || !m_search_box->get_visible()) { 769 return; 770 } 771 Glib::ustring search_text = get_search_text(); 772 if(search_text.empty()) { 773 return; 774 } 775 776 SearchableItem *searchable_widget = dynamic_cast<SearchableItem*>(currently_foreground()); 777 if(searchable_widget) { 778 searchable_widget->perform_search(search_text); 779 } 780 } 781 get_search_text()782 Glib::ustring NoteRecentChanges::get_search_text() 783 { 784 Glib::ustring text; 785 if(m_search_box) { 786 text = m_search_entry->get_text(); 787 } 788 else if(m_search_text) { 789 text = *m_search_text; 790 } 791 text = sharp::string_trim(text); 792 return text; 793 } 794 update_toolbar(EmbeddableWidget & widget)795 void NoteRecentChanges::update_toolbar(EmbeddableWidget & widget) 796 { 797 bool search = dynamic_cast<SearchNotesWidget*>(&widget) == m_search_notes_widget; 798 m_all_notes_button->set_visible(!search); 799 m_new_note_button->set_visible(search); 800 dynamic_cast<Gtk::Image*>(m_window_actions_button->get_image())->property_icon_name() = search ? MAIN_MENU_PRIMARY_ICON : MAIN_MENU_SECONDARY_ICON; 801 update_search_bar(widget, true); 802 803 try { 804 HasEmbeddableToolbar & toolbar_provider = dynamic_cast<HasEmbeddableToolbar&>(widget); 805 Gtk::Widget *tool_item = toolbar_provider.embeddable_toolbar(); 806 if(tool_item) { 807 m_embedded_toolbar.add(*tool_item); 808 } 809 } 810 catch(std::bad_cast &) { 811 } 812 } 813 on_all_notes_button_clicked()814 void NoteRecentChanges::on_all_notes_button_clicked() 815 { 816 close_on_escape(false); // intentional switch to search, user probably want to work more with this window 817 present_search(); 818 } 819 on_show_window_menu()820 void NoteRecentChanges::on_show_window_menu() 821 { 822 HasActions *embed_with_actions = dynamic_cast<HasActions*>(currently_foreground()); 823 if(embed_with_actions) { 824 if(m_window_menu_embedded == NULL) { 825 m_window_menu_embedded = make_window_menu(m_window_actions_button, std::move(embed_with_actions->get_popover_widgets())); 826 } 827 m_window_menu_embedded->show_all(); 828 } 829 else { 830 if(m_window_menu_default == NULL) { 831 m_window_menu_default = make_window_menu(m_window_actions_button, std::vector<PopoverWidget>()); 832 } 833 m_window_menu_default->show_all(); 834 } 835 } 836 make_window_menu(Gtk::Button * button,std::vector<PopoverWidget> && items)837 Gtk::PopoverMenu *NoteRecentChanges::make_window_menu(Gtk::Button *button, std::vector<PopoverWidget> && items) 838 { 839 Gtk::PopoverMenu *menu = manage(new Gtk::PopoverMenu); 840 Gtk::Box *menu_box = manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); 841 utils::set_common_popover_widget_props(*menu_box); 842 if(items.size() > 0) { 843 auto iter = items.begin(); 844 auto current_section = iter->section; 845 for(; iter != items.end() && iter->section != APP_CUSTOM_SECTION; ++iter) { 846 if(iter->section != current_section) { 847 current_section = iter->section; 848 menu_box->add(*manage(new Gtk::Separator)); 849 } 850 menu_box->add(*manage(iter->widget)); 851 } 852 853 menu->add(*menu_box); 854 for(; iter != items.end(); ++iter) { 855 PopoverSubmenu *submenu = dynamic_cast<PopoverSubmenu*>(iter->widget); 856 if(submenu) { 857 menu->add(*manage(iter->widget)); 858 menu->child_property_submenu(*iter->widget) = submenu->name(); 859 } 860 else { 861 ERR_OUT(_("Expected widget to be a sub-menu!")); 862 } 863 } 864 } 865 else { 866 menu_box->add(*manage(new Gtk::Label(_("No configured actions")))); 867 menu->add(*menu_box); 868 } 869 870 menu->set_relative_to(*button); 871 menu->set_modal(true); 872 menu->set_position(Gtk::POS_BOTTOM); 873 return menu; 874 } 875 on_embedded_name_changed(const Glib::ustring & name)876 void NoteRecentChanges::on_embedded_name_changed(const Glib::ustring & name) 877 { 878 set_title(name); 879 } 880 on_popover_widgets_changed()881 void NoteRecentChanges::on_popover_widgets_changed() 882 { 883 if(m_window_menu_embedded) { 884 m_window_menu_embedded = NULL; 885 } 886 } 887 on_notes_widget_key_press(GdkEventKey * ev)888 bool NoteRecentChanges::on_notes_widget_key_press(GdkEventKey *ev) 889 { 890 guint keyval; 891 gdk_event_get_keyval((GdkEvent*)ev, &keyval); 892 switch(keyval) { 893 case GDK_KEY_Escape: 894 case GDK_KEY_Delete: 895 case GDK_KEY_Tab: 896 return false; 897 case GDK_KEY_BackSpace: 898 if(m_search_button.get_active()) { 899 Glib::ustring s = m_search_entry->get_text(); 900 if(s.size()) { 901 m_search_entry->set_text(s.substr(0, s.size() - 1)); 902 } 903 } 904 return false; 905 default: 906 { 907 guint32 character = gdk_keyval_to_unicode(keyval); 908 if(character) { // ignore special keys 909 if(!m_search_button.get_active()) { 910 // first show search box, then activate button 911 // because we do not want the box to get selected 912 show_search_bar(false); 913 m_search_button.activate(); 914 } 915 Glib::ustring s; 916 s += character; 917 g_signal_emit_by_name(m_search_entry->gobj(), "insert-at-cursor", s.c_str()); 918 return true; 919 } 920 return false; 921 } 922 } 923 } 924 925 } 926 927