1 # include <iostream> 2 3 # include <gtkmm.h> 4 # include <gtkmm/widget.h> 5 # include <gtkmm/notebook.h> 6 # include <pangomm/fontdescription.h> 7 # include <pango/pango.h> 8 9 # ifndef DISABLE_VTE 10 # include <vte/vte.h> 11 # endif 12 13 # include <boost/filesystem.hpp> 14 15 # include "astroid.hh" 16 # include "poll.hh" 17 # include "main_window.hh" 18 # include "modes/mode.hh" 19 # include "modes/thread_index/thread_index.hh" 20 # include "modes/saved_searches.hh" 21 # include "modes/help_mode.hh" 22 # include "modes/edit_message.hh" 23 # include "modes/log_view.hh" 24 # include "command_bar.hh" 25 # include "actions/action.hh" 26 # include "actions/action_manager.hh" 27 # include "utils/resource.hh" 28 29 using namespace std; 30 namespace bfs = boost::filesystem; 31 32 # ifndef DISABLE_VTE 33 extern "C" { mw_on_terminal_child_exit(VteTerminal * t,gint a,gpointer mw)34 void mw_on_terminal_child_exit (VteTerminal * t, gint a, gpointer mw) { 35 ((Astroid::MainWindow *) mw)->on_terminal_child_exit (t, a); 36 } 37 mw_on_terminal_commit(VteTerminal * t,gchar ** tx,guint sz,gpointer mw)38 void mw_on_terminal_commit (VteTerminal * t, gchar ** tx, guint sz, gpointer mw) { 39 ((Astroid::MainWindow *) mw)->on_terminal_commit (t, tx, sz); 40 } 41 42 # if VTE_CHECK_VERSION(0,48,0) mw_on_terminal_spawn_callback(VteTerminal * t,GPid pid,GError * err,gpointer mw)43 void mw_on_terminal_spawn_callback (VteTerminal * t, GPid pid, GError * err, gpointer mw) 44 { 45 ((Astroid::MainWindow *) mw)->on_terminal_spawn_callback (t, pid, err); 46 } 47 # endif 48 } 49 # endif 50 51 namespace Astroid { 52 atomic<uint> MainWindow::nextid (0); 53 int Notebook::icon_size = 42; 54 Notebook()55 Notebook::Notebook () { 56 set_scrollable (true); 57 58 set_action_widget (&icons, Gtk::PACK_END); 59 icons.show_all (); 60 61 astroid->poll->signal_poll_state ().connect ( 62 sigc::mem_fun (this, &Notebook::poll_state_changed)); 63 signal_size_allocate ().connect ( 64 sigc::mem_fun (this, &Notebook::on_my_size_allocate)); 65 66 poll_state_changed (astroid->poll->get_poll_state()); 67 } 68 on_my_size_allocate(Gtk::Allocation &)69 void Notebook::on_my_size_allocate (Gtk::Allocation &) { 70 icon_size = icons.get_height (); 71 } 72 poll_state_changed(bool state)73 void Notebook::poll_state_changed (bool state) { 74 if (state && !spinner_on) { 75 /* set up spinner for poll */ 76 spinner_on = true; 77 icons.pack_end (poll_spinner, true, true, 5); 78 icons.show_all (); 79 poll_spinner.start (); 80 } else if (!state && spinner_on) { 81 poll_spinner.stop (); 82 icons.remove (poll_spinner); 83 spinner_on = false; 84 } 85 } 86 add_widget(Gtk::Widget * w)87 void Notebook::add_widget (Gtk::Widget * w) { 88 icons.pack_start (*w, true, true, 5); 89 w->show (); 90 icons.show_all (); 91 } 92 remove_widget(Gtk::Widget * w)93 void Notebook::remove_widget (Gtk::Widget * w) { 94 icons.remove (*w); 95 } 96 MainWindow()97 MainWindow::MainWindow () { 98 id = ++nextid; 99 100 actions = astroid->actions; 101 102 LOG (debug) << "mw: init, id: " << id; 103 104 set_title (""); 105 set_default_size (1200, 800); 106 107 path icon = Resource (false, "ui/icons/icon_color.png").get_path (); 108 try { 109 refptr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file (icon.c_str (), 42, 42, true); 110 set_icon (pixbuf); 111 } catch (Gdk::PixbufError &e) { 112 LOG (error) << "mw: could not set icon: " << e.what (); 113 } 114 115 vbox.set_orientation (Gtk::ORIENTATION_VERTICAL); 116 117 command.set_main_window (this); 118 119 command.property_search_mode_enabled().signal_changed().connect( 120 sigc::mem_fun (*this, &MainWindow::on_command_mode_changed) 121 ); 122 123 vbox.pack_start (command, false, true, 0); 124 vbox.pack_start (notebook, Gtk::PACK_EXPAND_WIDGET, 0); 125 126 /* set up yes-no asker */ 127 rev_yes_no = Gtk::manage (new Gtk::Revealer ()); 128 rev_yes_no->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP); 129 130 Gtk::Box * rh = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); 131 132 label_yes_no = Gtk::manage (new Gtk::Label ()); 133 rh->pack_start (*label_yes_no, true, true, 5); 134 label_yes_no->set_halign (Gtk::ALIGN_START); 135 136 /* buttons */ 137 Gtk::Button * yes = Gtk::manage (new Gtk::Button("_Yes")); 138 Gtk::Button * no = Gtk::manage (new Gtk::Button("_No")); 139 140 yes->set_use_underline (true); 141 no->set_use_underline (true); 142 143 rh->pack_start (*yes, false, true, 5); 144 rh->pack_start (*no, false, true, 5); 145 146 rev_yes_no->set_margin_top (0); 147 rh->set_margin_bottom (5); 148 149 rev_yes_no->add (*rh); 150 rev_yes_no->set_reveal_child (false); 151 vbox.pack_end (*rev_yes_no, false, true, 0); 152 153 yes->signal_clicked().connect (sigc::mem_fun (this, &MainWindow::on_yes)); 154 no->signal_clicked().connect (sigc::mem_fun (this, &MainWindow::on_no)); 155 156 /* multi key handler */ 157 rev_multi = Gtk::manage (new Gtk::Revealer ()); 158 rev_multi->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP); 159 160 Gtk::Box * rh_ = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); 161 162 label_multi = Gtk::manage (new Gtk::Label ()); 163 rh_->pack_start (*label_multi, true, true, 5); 164 label_multi->set_halign (Gtk::ALIGN_START); 165 166 rev_multi->set_margin_top (0); 167 rh->set_margin_bottom (5); 168 169 rev_multi->add (*rh_); 170 rev_multi->set_reveal_child (false); 171 vbox.pack_end (*rev_multi, false, true, 0); 172 173 /* terminal */ 174 # ifndef DISABLE_VTE 175 rev_terminal = Gtk::manage (new Gtk::Revealer ()); 176 rev_terminal->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP); 177 rev_terminal->set_reveal_child (false); 178 vbox.pack_end (*rev_terminal, false, true, 0); 179 180 terminal_cwd = bfs::current_path (); 181 # endif 182 183 add (vbox); 184 185 show_all_children (); 186 187 /* connect keys */ 188 add_events (Gdk::KEY_PRESS_MASK); 189 signal_key_press_event ().connect ( 190 sigc::mem_fun(*this, &MainWindow::on_key_press)); 191 192 /* got focus, grab keys */ 193 signal_focus_in_event ().connect ( 194 sigc::mem_fun (this, &MainWindow::on_my_focus_in_event)); 195 signal_focus_out_event ().connect ( 196 sigc::mem_fun (this, &MainWindow::on_my_focus_out_event)); 197 198 /* change page */ 199 notebook.signal_switch_page ().connect ( 200 sigc::mem_fun (this, &MainWindow::on_my_switch_page)); 201 202 /* catch update title events */ 203 update_title_dispatcher.connect ( 204 sigc::mem_fun (this, &MainWindow::on_update_title)); 205 206 /* catch window close events */ 207 signal_delete_event ().connect ( 208 sigc::mem_fun (this, &MainWindow::on_delete_event)); 209 210 /* register keys {{{ */ 211 keys.title = "MainWindow"; 212 213 keys.register_key ("q", 214 "main_window.quit_ask", 215 "Quit astroid", 216 [&] (Key) { 217 if (astroid->get_windows().size () > 1) { 218 /* other windows, just close this one */ 219 quit (); 220 } else { 221 LOG (debug) << "really quit?: " << id; 222 ask_yes_no ("Really quit?", [&](bool yes){ if (yes) quit (); }); 223 } 224 return true; 225 }); 226 227 keys.register_key ("Q", 228 "main_window.quit", 229 "Quit astroid (without asking)", 230 [&] (Key) { 231 quit (); 232 return true; 233 }); 234 235 keys.register_key ("l", { "b" }, 236 "main_window.next_page", 237 "Next page", 238 [&] (Key) 239 { 240 if (notebook.get_current_page () == (notebook.get_n_pages () - 1)) 241 set_active (0); 242 else 243 set_active (notebook.get_current_page() + 1); 244 245 return true; 246 }); 247 248 keys.register_key ("h", { "B" }, 249 "main_window.previous_page", 250 "Previous page", 251 [&] (Key) { 252 if (notebook.get_current_page() == 0) 253 set_active (notebook.get_n_pages()-1); 254 else 255 set_active (notebook.get_current_page() - 1); 256 257 return true; 258 }); 259 260 keys.register_key ("M-1", 261 "main_window.jump_to_page_1", 262 "Jump to page 1", 263 bind (&MainWindow::jump_to_page, this, _1, 1)); 264 265 keys.register_key ("M-2", 266 "main_window.jump_to_page_2", 267 "Jump to page 2", 268 bind (&MainWindow::jump_to_page, this, _1, 2)); 269 270 keys.register_key ("M-3", 271 "main_window.jump_to_page_3", 272 "Jump to page 3", 273 bind (&MainWindow::jump_to_page, this, _1, 3)); 274 275 keys.register_key ("M-4", 276 "main_window.jump_to_page_4", 277 "Jump to page 4", 278 bind (&MainWindow::jump_to_page, this, _1, 4)); 279 280 keys.register_key ("M-5", 281 "main_window.jump_to_page_5", 282 "Jump to page 5", 283 bind (&MainWindow::jump_to_page, this, _1, 5)); 284 285 keys.register_key ("M-6", 286 "main_window.jump_to_page_6", 287 "Jump to page 6", 288 bind (&MainWindow::jump_to_page, this, _1, 6)); 289 290 keys.register_key ("M-7", 291 "main_window.jump_to_page_7", 292 "Jump to page 7", 293 bind (&MainWindow::jump_to_page, this, _1, 7)); 294 295 keys.register_key ("M-8", 296 "main_window.jump_to_page_8", 297 "Jump to page 8", 298 bind (&MainWindow::jump_to_page, this, _1, 8)); 299 300 keys.register_key ("M-9", 301 "main_window.jump_to_page_9", 302 "Jump to page 9", 303 bind (&MainWindow::jump_to_page, this, _1, 9)); 304 305 keys.register_key ("M-0", 306 "main_window.jump_to_page_0", 307 "Jump to page 0", 308 bind (&MainWindow::jump_to_page, this, _1, 0)); 309 310 keys.register_key ("C-w", "main_window.close_page", 311 "Close mode (or window if other windows are open)", 312 [&] (Key) { 313 close_page (); 314 return true; 315 }); 316 317 keys.register_key ("C-W", "main_window.close_page_force", 318 "Force close mode (or window if other windows are open)", 319 [&] (Key) { 320 close_page (true); 321 return true; 322 }); 323 324 keys.register_key ("o", 325 "main_window.search", 326 "Search", 327 [&] (Key) { 328 enable_command (CommandBar::CommandMode::Search, "", NULL); 329 return true; 330 }); 331 332 keys.register_key ("M-s", "main_window.show_saved_searches", 333 "Show saved searches", 334 [&] (Key) { 335 add_mode (new SavedSearches (this)); 336 return true; 337 }); 338 339 keys.register_key ("L", "main_window.search_tag", 340 "Search for tag:", 341 [&] (Key) { 342 enable_command (CommandBar::CommandMode::Search, "tag:", NULL); 343 return true; 344 }); 345 346 347 keys.register_key (Key (GDK_KEY_question), "main_window.show_help", 348 "Show help", 349 [&] (Key) { 350 HelpMode * h = new HelpMode (this); 351 h->show_help ((Mode*) notebook.get_nth_page (notebook.get_current_page())); 352 add_mode (h); 353 return true; 354 }); 355 356 keys.register_key ("z", "main_window.show_log", 357 "Show log window", 358 [&] (Key) { 359 add_mode (new LogView (this)); 360 return true; 361 }); 362 363 keys.register_key ("u", "main_window.undo", 364 "Undo last action", 365 [&] (Key) { 366 actions->undo (); 367 return true; 368 }); 369 370 keys.register_key ("m", "main_window.new_mail", 371 "Compose new mail", 372 [&] (Key) { 373 add_mode (new EditMessage (this)); 374 return true; 375 }); 376 377 keys.register_key ("P", "main_window.poll", 378 "Start manual poll", 379 [&] (Key) { 380 astroid->poll->poll (); 381 return true; 382 }); 383 384 keys.register_key ("M-p", "main_window.toggle_auto_poll", 385 "Toggle auto poll", 386 [&] (Key) { 387 astroid->poll->toggle_auto_poll (); 388 return true; 389 }); 390 391 keys.register_key ("C-c", "main_window.cancel_poll", 392 "Cancel ongoing poll", 393 [&] (Key) { 394 astroid->poll->cancel_poll (); 395 return true; 396 }); 397 398 keys.register_key ("C-o", "main_window.open_new_window", 399 "Open new main window", 400 [&] (Key) { 401 astroid->open_new_window (); 402 return true; 403 }); 404 405 keys.register_key ("\"", "main_window.clipboard", 406 "Set target clipboard", 407 [&] (Key k) { 408 multi_key (clipboard, k); 409 return true; 410 }); 411 412 clipboard.register_key ("+", "main_window.clipboard.clipboard", 413 "Set target clipboard to CLIPBOARD (default)", 414 [&] (Key) { 415 astroid->clipboard_target = GDK_SELECTION_CLIPBOARD; 416 return true; 417 }); 418 419 clipboard.register_key ("*", "main_window.clipboard.primary", 420 "Set target clipboard to PRIMARY", 421 [&] (Key) { 422 astroid->clipboard_target = GDK_SELECTION_PRIMARY; 423 return true; 424 }); 425 426 # ifndef DISABLE_VTE 427 keys.register_key ("|", "main_window.open_terminal", 428 "Open terminal", 429 [&] (Key) { 430 enable_terminal (); 431 return true; 432 }); 433 # endif 434 435 // }}} 436 } 437 jump_to_page(Key,int pg)438 bool MainWindow::jump_to_page (Key, int pg) { 439 if (pg == 0) { 440 pg = notebook.get_n_pages () - 1; 441 } else { 442 pg--; 443 } 444 445 LOG (debug) << "mw: swapping to page: " << pg; 446 447 if (notebook.get_current_page () != pg) { 448 set_active (pg); 449 } 450 451 return true; 452 } 453 is_current(Mode * m)454 bool MainWindow::is_current (Mode * m) { 455 int i = notebook.get_current_page (); 456 return (m == ((Mode *) notebook.get_nth_page (i))); 457 } 458 set_title(ustring t)459 void MainWindow::set_title (ustring t) { 460 461 ustring tt = ustring::compose( "Astroid (%1)", Astroid::version); 462 463 if (t.size() > 0) { 464 tt = t + " - " + tt; 465 } 466 467 Gtk::Window::set_title (tt); 468 } 469 on_update_title()470 void MainWindow::on_update_title () { 471 int n = notebook.get_current_page(); 472 473 if (n >= 0 && n <= notebook.get_n_pages()-1) { 474 set_title (((Mode*) notebook.get_nth_page(n))->get_label()); 475 } else { 476 set_title (""); 477 } 478 } 479 enable_command(CommandBar::CommandMode m,ustring title,ustring cmd,function<void (ustring)> f)480 void MainWindow::enable_command (CommandBar::CommandMode m, ustring title, ustring cmd, function<void(ustring)> f) { 481 ungrab_active (); 482 command.enable_command (m, title, cmd, f); 483 active_mode = Command; 484 command.add_modal_grab (); 485 } 486 enable_command(CommandBar::CommandMode m,ustring cmd,function<void (ustring)> f)487 void MainWindow::enable_command (CommandBar::CommandMode m, ustring cmd, function<void(ustring)> f) { 488 ungrab_active (); 489 command.enable_command (m, cmd, f); 490 active_mode = Command; 491 command.add_modal_grab (); 492 } 493 disable_command()494 void MainWindow::disable_command () { 495 // hides itself 496 command.remove_modal_grab(); 497 set_active (current); 498 active_mode = Window; 499 } 500 on_command_mode_changed()501 void MainWindow::on_command_mode_changed () { 502 if (!command.get_search_mode()) { 503 disable_command (); 504 } 505 } 506 507 /* Terminal {{{ */ 508 # ifndef DISABLE_VTE enable_terminal()509 void MainWindow::enable_terminal () { 510 rev_terminal->set_reveal_child (true); 511 ungrab_active (); 512 active_mode = Terminal; 513 514 vte_term = vte_terminal_new (); 515 gtk_container_add (GTK_CONTAINER(rev_terminal->gobj ()), vte_term); 516 rev_terminal->show_all (); 517 518 /* load font settings */ 519 ustring font_desc_string = astroid->config("terminal").get<string> ("font_description"); 520 521 if (font_desc_string == "" || font_desc_string == "default") { 522 auto settings = Gio::Settings::create ("org.gnome.desktop.interface"); 523 font_desc_string = settings->get_string ("monospace-font-name"); 524 } 525 526 auto font_description = Pango::FontDescription (font_desc_string); 527 528 /* https://developer.gnome.org/pangomm/stable/classPango_1_1FontDescription.html#details */ 529 if (font_description.get_size () == 0) { 530 LOG (warn) << "terminal.font_description: no size specified, expect weird behaviour."; 531 } 532 533 vte_terminal_set_font (VTE_TERMINAL(vte_term), font_description.gobj ()); 534 vte_terminal_set_size (VTE_TERMINAL (vte_term), 1, astroid->config("terminal").get<int> ("height")); 535 536 /* start shell */ 537 char * shell = vte_get_user_shell (); 538 539 char * args[2] = { shell, NULL }; 540 char * envs[1] = { NULL }; 541 542 LOG (info) << "mw: starting terminal..: " << shell; 543 544 if (!bfs::exists (terminal_cwd)) { 545 terminal_cwd = bfs::current_path (); 546 } 547 548 # if VTE_CHECK_VERSION(0,48,0) 549 vte_terminal_spawn_async (VTE_TERMINAL(vte_term), 550 VTE_PTY_DEFAULT, 551 terminal_cwd.c_str(), 552 args, 553 envs, 554 G_SPAWN_DEFAULT, 555 NULL, 556 NULL, 557 NULL, 558 -1, 559 NULL, 560 mw_on_terminal_spawn_callback, 561 this); 562 # else 563 GError * err = NULL; 564 vte_terminal_spawn_sync (VTE_TERMINAL(vte_term), 565 VTE_PTY_DEFAULT, 566 terminal_cwd.c_str(), 567 args, 568 envs, 569 G_SPAWN_DEFAULT, 570 NULL, 571 NULL, 572 &terminal_pid, 573 NULL, 574 (err = NULL, &err)); 575 576 on_terminal_spawn_callback (VTE_TERMINAL(vte_term), terminal_pid, err); 577 578 # endif 579 580 gtk_widget_grab_focus (vte_term); 581 gtk_grab_add (vte_term); 582 } 583 on_terminal_spawn_callback(VteTerminal * vte_term,GPid pid,GError * err)584 void MainWindow::on_terminal_spawn_callback (VteTerminal * vte_term, GPid pid, GError * err) 585 { 586 if (err) { 587 LOG (error) << "mw: terminal: " << err->message; 588 disable_terminal (); 589 } else { 590 terminal_pid = pid; 591 592 LOG (debug) << "mw: terminal started: " << terminal_pid; 593 g_signal_connect (vte_term, "child-exited", 594 G_CALLBACK (mw_on_terminal_child_exit), 595 (gpointer) this); 596 597 g_signal_connect (vte_term, "commit", 598 G_CALLBACK (mw_on_terminal_commit), 599 (gpointer) this); 600 601 } 602 } 603 disable_terminal()604 void MainWindow::disable_terminal () { 605 LOG (info) << "mw: disabling terminal.."; 606 rev_terminal->set_reveal_child (false); 607 set_active (current); 608 active_mode = Window; 609 gtk_grab_remove (vte_term); 610 611 gtk_widget_destroy (vte_term); 612 } 613 on_terminal_child_exit(VteTerminal *,gint)614 void MainWindow::on_terminal_child_exit (VteTerminal *, gint) { 615 LOG (info) << "mw: terminal exited (cwd: " << terminal_cwd.c_str () << ")"; 616 disable_terminal (); 617 } 618 on_terminal_commit(VteTerminal *,gchar **,guint)619 void MainWindow::on_terminal_commit (VteTerminal *, gchar **, guint) { 620 bfs::path pth = bfs::path(ustring::compose ("/proc/%1/cwd", terminal_pid).c_str ()); 621 622 if (bfs::exists (pth)) { 623 terminal_cwd = bfs::canonical (pth); 624 } 625 } 626 # endif 627 628 // }}} 629 quit()630 void MainWindow::quit () { 631 LOG (info) << "mw: quit: " << id; 632 in_quit = true; 633 634 /* focus out */ 635 ungrab_active (); 636 637 for (int c = notebook.get_n_pages () -1; c >= 0; c--) { 638 /* ((Mode*) notebook.get_nth_page (c))->pre_close (); */ 639 del_mode (c); 640 } 641 642 close (); // Gtk::Window::close () 643 } 644 on_delete_event(GdkEventAny *)645 bool MainWindow::on_delete_event (GdkEventAny *) { 646 if (!in_quit) 647 quit (); 648 return false; 649 } 650 on_yes()651 void MainWindow::on_yes () { 652 answer_yes_no (true); 653 } 654 on_no()655 void MainWindow::on_no () { 656 answer_yes_no (false); 657 } 658 659 mode_key_handler(GdkEventKey * event)660 bool MainWindow::mode_key_handler (GdkEventKey * event) { 661 if (yes_no_waiting) { 662 switch (event->keyval) { 663 case GDK_KEY_Y: 664 case GDK_KEY_y: 665 answer_yes_no (true); 666 return true; 667 668 case GDK_KEY_Escape: 669 case GDK_KEY_N: 670 case GDK_KEY_n: 671 answer_yes_no (false); 672 return true; 673 } 674 675 /* swallow all other keys */ 676 return true; 677 678 } else if (multi_waiting) { 679 bool res = false; 680 681 switch (event->keyval) { 682 case GDK_KEY_Escape: 683 { 684 res = true; 685 } 686 break; 687 688 default: 689 { 690 res = multi_keybindings.handle (event); 691 } 692 break; 693 } 694 695 /* close rev */ 696 multi_waiting = !res; 697 698 if (res) { 699 rev_multi->set_reveal_child (false); 700 multi_keybindings.clear (); 701 } 702 703 return true; // swallow all keys 704 705 # ifndef DISABLE_VTE 706 } else if (active_mode == Terminal) { 707 return true; 708 } 709 # else 710 } 711 # endif 712 713 return false; 714 } 715 on_key_press(GdkEventKey * event)716 bool MainWindow::on_key_press (GdkEventKey * event) { 717 if (mode_key_handler (event)) return true; 718 719 if (active_mode == Command) { 720 command.command_handle_event (event); 721 return true; 722 } 723 724 return keys.handle (event); 725 } 726 add_mode(Mode * m)727 void MainWindow::add_mode (Mode * m) { 728 m = Gtk::manage (m); 729 730 Gtk::Widget * w = m; 731 732 int n = notebook.insert_page ((*w), m->tab_label, current+1); 733 734 notebook.show_all (); 735 736 set_active (n); 737 } 738 ask_yes_no(ustring question,std::function<void (bool)> closure)739 void MainWindow::ask_yes_no ( 740 ustring question, 741 std::function <void (bool)> closure) 742 { 743 using std::endl; 744 LOG (info) << "mw: " << question; 745 746 if (yes_no_waiting || multi_waiting) { 747 LOG (warn) << "mw: already waiting for answer to previous question, discarding this one."; 748 return; 749 } 750 751 yes_no_waiting = true; 752 yes_no_closure = closure; 753 754 rev_yes_no->set_reveal_child (true); 755 label_yes_no->set_text (question + " [y/n]"); 756 } 757 answer_yes_no(bool yes)758 void MainWindow::answer_yes_no (bool yes) { 759 using std::endl; 760 rev_yes_no->set_reveal_child (false); 761 762 if (yes) { 763 LOG (info) << "mw: yes-no: got yes!"; 764 } else { 765 LOG (info) << "mw: yes-no: got no :/"; 766 } 767 768 if (yes_no_waiting) { 769 if (yes_no_closure != NULL) { 770 yes_no_closure (yes); 771 } 772 } 773 774 yes_no_closure = NULL; 775 yes_no_waiting = false; 776 } 777 multi_key(Keybindings & kb,Key)778 bool MainWindow::multi_key (Keybindings & kb, Key /* k */) 779 { 780 using std::endl; 781 LOG (info) << "mw: starting multi key."; 782 783 if (yes_no_waiting || multi_waiting) { 784 LOG (warn) << "mw: already waiting for answer to previous question, discarding this one."; 785 return true; 786 } 787 788 multi_waiting = true; 789 multi_keybindings = kb; 790 791 rev_multi->set_reveal_child (true); 792 label_multi->set_markup (kb.short_help ()); 793 794 return true; 795 } 796 del_mode(int c)797 void MainWindow::del_mode (int c) { 798 // LOG (debug) << "mw: del mode: " << c; 799 if (notebook.get_n_pages() > 1) { 800 if (c >= 0) { 801 if (c == 0) { 802 set_active (c + 1); 803 } else { 804 set_active (c - 1); 805 } 806 807 ((Mode*) notebook.get_nth_page (c))->pre_close (); 808 notebook.remove_page (c); // this should free the widget (?) 809 } else { 810 LOG (warn) << "mw: attempt to remove negative page"; 811 } 812 } else { 813 if (!in_quit && astroid->get_windows().size () > 1) { 814 LOG (debug) << "mw: other windows available, closing this one."; 815 quit (); 816 } 817 } 818 } 819 close_page(Mode * m,bool force)820 void MainWindow::close_page (Mode * m, bool force) { 821 m->close (force); 822 } 823 close_page(bool force)824 void MainWindow::close_page (bool force) { 825 int c = notebook.get_current_page (); 826 827 ((Mode*) notebook.get_nth_page (c))->close (force); 828 } 829 ungrab_active()830 void MainWindow::ungrab_active () { 831 if (current >= 0) { 832 if (notebook.get_n_pages() > current) { 833 //LOG (debug) << "mw: release modal, from: " << current; 834 ((Mode*) notebook.get_nth_page (current))->release_modal(); 835 active = false; 836 } 837 } 838 } 839 on_my_switch_page(Gtk::Widget *,guint no)840 void MainWindow::on_my_switch_page (Gtk::Widget * /* w */, guint no) { 841 grab_active (no); 842 } 843 grab_active(int n)844 void MainWindow::grab_active (int n) { 845 //LOG (debug) << "mw: set active: " << n << ", current: " << current; 846 847 ungrab_active (); 848 849 //LOG (debug) << "mw: grab modal to: " << n; 850 851 if (_has_focus) { 852 /* we have focus */ 853 ((Mode*) notebook.get_nth_page (n))->grab_modal(); 854 } else { 855 LOG (debug) << "mw: does not have focus, will not grab modal."; 856 } 857 858 current = n; 859 active = true; 860 861 on_update_title (); 862 } 863 set_active(int n)864 void MainWindow::set_active (int n) { 865 LOG (debug) << "mw: set active: " << n << ", current: " << current; 866 867 if (n >= 0 && n <= notebook.get_n_pages()-1) { 868 869 if (notebook.get_current_page() != n) { 870 notebook.set_current_page (n); 871 } 872 873 grab_active (n); 874 875 } else { 876 // LOG (debug) << "mw: set active: page is out of range: " << n; 877 on_update_title (); 878 } 879 } 880 on_my_focus_in_event(GdkEventFocus *)881 bool MainWindow::on_my_focus_in_event (GdkEventFocus * /* event */) { 882 _has_focus = true; 883 if (!in_quit && active) set_active (current); 884 LOG (debug) << "mw: focus-in: " << id << " active: " << active << ", in_quit: " << in_quit; 885 return false; 886 } 887 on_my_focus_out_event(GdkEventFocus *)888 bool MainWindow::on_my_focus_out_event (GdkEventFocus * /* event */) { 889 //LOG (debug) << "mw: focus-out: " << id; 890 _has_focus = false; 891 if ((current < notebook.get_n_pages ()) && (current >= 0)) 892 ((Mode*) notebook.get_nth_page (current))->release_modal(); 893 return false; 894 } 895 } 896 897