📄 fax_list.cpp
字号:
delete_parms(print_from_stdin_parms); } delete_parms(fax_to_ps_parms);}void FaxListDialog::view_fax(void) { Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected(); // we do not need a mutex before reading prog_config.ps_view_cmd // as although read in FaxListDialog::get_ps_viewer_parms(), // the thread which executes that method is launched below as // FaxListsDialog::view_fax_thread() and is therefore not running // at this stage if (row_iter && !prog_config.ps_view_cmd.empty()) { // fax_basename_sem is initialised to a count of 1, so this will stop // fax_basename being overwritten until Sem_sync::post() is called in // print_fax_thread() or view_fax_thread() or on Glib::ThreadError being // caught fax_basename_sem.wait(); fax_basename = prog_config.homedir; if (mode == received) fax_basename += "/faxin/"; else fax_basename += "/faxsent/"; fax_basename += (*row_iter)[model_columns.fax_name]; fax_basename += '/'; fax_basename += (*row_iter)[model_columns.fax_name]; fax_basename += '.'; // now block off the signals for which we have set handlers so that the socket server // thread does not receive the signals, otherwise we will have memory synchronisation // issues in multi-processor systems - we will unblock in the initial (GUI) thread // as soon as the socket server thread has been launched sigset_t sig_mask; sigemptyset(&sig_mask); sigaddset(&sig_mask, SIGCHLD); sigaddset(&sig_mask, SIGQUIT); sigaddset(&sig_mask, SIGTERM); sigaddset(&sig_mask, SIGINT); sigaddset(&sig_mask, SIGHUP);#ifdef HAVE_PTHREAD_SIGMASK pthread_sigmask(SIG_BLOCK, &sig_mask, 0);#else sigprocmask(SIG_BLOCK, &sig_mask, 0);#endif try { Glib::Thread::create(sigc::mem_fun(*this, &FaxListDialog::view_fax_thread), false); } catch (Glib::ThreadError&) { write_error("Cannot start new view fax thread, fax cannot be viewed\n"); fax_basename_sem.post(); } // now unblock the signals so that the initial (GUI) thread can receive them#ifdef HAVE_PTHREAD_SIGMASK pthread_sigmask(SIG_UNBLOCK, &sig_mask, 0);#else sigprocmask(SIG_UNBLOCK, &sig_mask, 0);#endif }}void FaxListDialog::view_fax_thread(void) { // get a temporary file char filename[] = "/tmp/efax-gtk-view.XXXXXX"; int file_fd = mkstemp(filename); if (file_fd == -1) { write_error("Failed to make temporary file:\n" "please check permissions in /tmp directory"); _exit(0); } // now create a pipe and proceed to fork to write the postscript to temporary file Pipe_fifo fork_pipe(Pipe_fifo::block); // get the arguments for the exec() call to convert the fax files to // PS format following the next fork() call (because this is a multi- // threaded program, we must do this before fork()ing because we use // functions to get the arguments which are not async-signal-safe) std::pair<const char*, char* const*> fax_to_ps_parms(get_fax_to_ps_parms(fax_basename, false)); // release main GUI thread in print_fax() or view_fax() // waiting for this use of fax_basename to finish fax_basename_sem.post(); // now fork to create the process which will write postscript to stdout pid_t pid = fork(); if (pid == -1) { write_error("Fork error - exiting\n"); _exit(FORK_ERROR); } if (!pid) { // child process which will send postscript to stdout // this is the child process to write to stdin // now we have fork()ed we can connect to stderr connect_to_stderr(); fork_pipe.connect_to_stdout(); execvp(fax_to_ps_parms.first, fax_to_ps_parms.second); // if we reached this point, then the execvp() call must have failed // report error and then end process - use _exit(), not exit() write_error("Can't find the efix-0.9a program - please check your installation\n" "and the PATH environmental variable\n"); _exit(0); } // this is the parent process, which will convert the fax to Postscript // and then pipe it on stdout to write_from_stdin() // first, release the memory allocated on the heap for // the redundant fax_to_ps_parms // we are in the main parent process here - no worries about // only being able to use async-signal-safe functions delete_parms(fax_to_ps_parms); // now write stdin in (containing the generated PS text) to file fork_pipe.connect_to_stdin(); write_from_stdin(file_fd); // the temporary file to be viewed has now been created // so launch a new process to control the viewing process // we will reset handlers and then fork() again having done // so, so that we can wait on the child of child // first get the arguments for the exec() call to launch the fax // viewer following the next fork() call (because this is a multi- // threaded program, we must do this before fork()ing because we use // functions to get the arguments which are not async-signal-safe) std::pair<const char*, char* const*> ps_viewer_parms(get_ps_viewer_parms(filename)); if (ps_viewer_parms.first) { // this will be 0 if get_ps_viewer_parms() // threw a Glib::ConvertError) pid = fork(); if (pid == -1) { write_error("Fork error\n"); std::exit(FORK_ERROR); } if (!pid) { // child view-control process // restore the default SIGCHLD signal handler with SA_RESTART // for systems (such as Linux) which require it. Also unblock // signals in case we are blocking them on launching a thread // elsewhere in this program (I cannot work out from IEEE 1003.1 // whether all masks are emptied on a fork() in any event) sigset_t sig_mask; sigemptyset(&sig_mask); sigaddset(&sig_mask, SIGCHLD); sigaddset(&sig_mask, SIGQUIT); sigaddset(&sig_mask, SIGTERM); sigaddset(&sig_mask, SIGINT); sigaddset(&sig_mask, SIGHUP); // this child process is single threaded, so we can use sigprocmask() // rather than pthread_sigmask() (and should do so as sigprocmask() // is guaranteed to be async-signal-safe) sigprocmask(SIG_UNBLOCK, &sig_mask, 0); struct sigaction sig_act; sig_act.sa_handler = SIG_DFL; sigemptyset(&sig_act.sa_mask);#ifdef SA_RESTART sig_act.sa_flags = SA_RESTART;#else sig_act.sa_flags = 0;#endif sigaction(SIGCHLD, &sig_act, 0); connect_to_stderr(); // now fork() again pid_t pid = fork(); if (pid == -1) { write_error("Fork error - exiting\n"); _exit(FORK_ERROR); } if (!pid) { // child process to call ps_viewer() from execvp(ps_viewer_parms.first, ps_viewer_parms.second); // if we reached this point, then the execvp() call must have failed // report error and then end process - use _exit(), not exit() write_error("Can't find the ps viewer program - please check your installation\n" "and the PATH environmental variable\n"); _exit(0); } // now we are back to the main viewing process again // delete the temporary file // first wait till the ps_viewer() process is closed by the user exiting // the postscript viewing program wait(0); unlink(filename); // now terminate the process _exit(0); } // release the memory allocated on the heap for // the redundant ps_viewer_parms // we are in the main parent process here - no worries about // only being able to use async-signal-safe functions delete_parms(ps_viewer_parms); }}void FaxListDialog::delete_parms(std::pair<const char*, char* const*> parms_pair) { delete[] parms_pair.first; char* const* temp_pp = parms_pair.second; for(; *temp_pp != 0; ++temp_pp) { delete[] *temp_pp; } delete[] parms_pair.second;}void FaxListDialog::delete_fax_prompt(void) { Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected(); if (row_iter) { std::string faxnumber((*row_iter)[model_columns.fax_name]); Glib::ustring msg(gettext("Delete fax ")); msg += faxnumber + (gettext("?")); PromptDialog* dialog_p = new PromptDialog(msg, gettext("Delete fax"), standard_size, *this); dialog_p->accepted.connect(sigc::mem_fun(*this, &FaxListDialog::delete_fax)); // there is no memory leak -- the memory will be deleted when PromptDialog closes }}void FaxListDialog::delete_fax(void) { Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected(); if (row_iter) { // get the name of the fax to be moved and the new name std::string old_dirname(prog_config.homedir); std::string new_dirname(prog_config.homedir); if (mode == received) { old_dirname += "/faxin/"; new_dirname += "/faxin/oldfax/"; } else { old_dirname += "/faxsent/"; new_dirname += "/faxsent/oldfax/"; } old_dirname += (*row_iter)[model_columns.fax_name]; new_dirname += (*row_iter)[model_columns.fax_name]; // make a new directory to copy the files into mkdir(new_dirname.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); // it should be possible to use link()/unlink() as the target and source // are sub-directories of $HOME/faxin or $HOME/faxsent. If because of a // very odd filesystem arrangement this won't work, the method will do nothing std::vector<std::string> filelist; struct dirent* direntry; struct stat statinfo; DIR* dir_p; if ((dir_p = opendir(old_dirname.c_str())) == 0) { std::string msg("Can't open directory "); msg += old_dirname; msg += '\n'; write_error(msg.c_str()); } else { chdir(old_dirname.c_str()); while ((direntry = readdir(dir_p)) != 0) { stat(direntry->d_name, &statinfo); if (S_ISREG(statinfo.st_mode)) { filelist.push_back(direntry->d_name); } } closedir(dir_p); bool failure = false; std::vector<std::string>::const_iterator iter; std::string old_filename; std::string new_filename; for (iter = filelist.begin(); iter != filelist.end(); ++iter) { old_filename = old_dirname + '/'; old_filename += *iter; new_filename = new_dirname + '/'; new_filename += *iter; if (link(old_filename.c_str(), new_filename.c_str())) { failure = true; break; } } if (failure) { // recover position std::vector<std::string>::const_iterator recover_iter; for (recover_iter = filelist.begin(); recover_iter != iter; ++recover_iter) { new_filename = new_dirname + '/'; new_filename += *recover_iter; unlink(new_filename.c_str()); } rmdir(new_dirname.c_str()); std::string msg("Can't move directory "); msg += old_dirname; msg += " to "; msg += new_dirname; msg += '\n'; write_error(msg.c_str()); } else { for (iter = filelist.begin(); iter != filelist.end(); ++iter) { old_filename = old_dirname + '/'; old_filename += *iter; unlink(old_filename.c_str()); } if (rmdir(old_dirname.c_str())) { std::string msg("Can't delete directory "); msg += old_dirname; msg += "\nThe contents should have been moved to "; msg += new_dirname; msg += "\nand it should now be empty -- please check\n"; write_error(msg.c_str()); } else get_fax_list_rows(); } // reset current directory std::string temp(prog_config.homedir + "/faxin"); chdir(temp.c_str()); } }}void FaxListDialog::refresh_slot(void) { get_fax_list_rows(); set_buttons_slot();}DescriptionDialog::DescriptionDialog(const int standard_size, const std::string& text, Gtk::Window& window): Gtk::Window(Gtk::WINDOW_TOPLEVEL), in_exec_loop(false), ok_button(Gtk::Stock::OK), cancel_button(Gtk::Stock::CANCEL), button_box(Gtk::BUTTONBOX_END, standard_size/2), label(gettext("Fax description?")), table(3, 1, false), parent(window) { button_box.add(cancel_button); button_box.add(ok_button); table.attach(label, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND, standard_size/2, standard_size/4); table.attach(entry, 0, 1, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::EXPAND, standard_size/2, standard_size/4); table.attach(button_box, 0, 1, 2, 3, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK, standard_size/2, standard_size/4); ok_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &DescriptionDialog::selected), true)); cancel_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &DescriptionDialog::selected), false)); ok_button.set_flags(Gtk::CAN_DEFAULT); cancel_button.set_flags(Gtk::CAN_DEFAULT); add(table); entry.set_text(text); set_title(gettext("Fax description")); set_transient_for(parent); set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); parent.set_sensitive(false); set_modal(true); entry.set_size_request(standard_size * 9, standard_size); set_border_width(standard_size/2); entry.grab_focus(); set_position(Gtk::WIN_POS_CENTER_ON_PARENT); set_resizable(false); set_icon(prog_config.window_icon_r); show_all(); // we now need to deselect what is in the entry entry.select_region(0,0);}void DescriptionDialog::exec(void) { in_exec_loop = true; Gtk::Main::run();}void DescriptionDialog::selected(bool accept) { parent.set_sensitive(true); // do this before we emit accepted() hide_all(); if (accept) accepted(entry.get_text()); if (in_exec_loop) Gtk::Main::quit(); // if we have not called exec(), then this dialog is self-owning and it is safe to call `delete this' else delete this;}bool DescriptionDialog::on_delete_event(GdkEventAny*) { selected(false); return true; // returning true prevents destroy sig being emitted}bool DescriptionDialog::on_key_press_event(GdkEventKey* event_p) { if (event_p->keyval == GDK_Escape) selected(false); else if (event_p->keyval == GDK_Return && !cancel_button.has_focus()) selected(true); else Gtk::Window::on_key_press_event(event_p); return true; // processing ends here}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -