/* Copyright (C) 2001 to 2006 Chris Vine This program is distributed under the General Public Licence, version 2. For particulars of this and relevant disclaimers see the file COPYING distributed with the source files. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // the key codes are here #if GTK_CHECK_VERSION(2,4,0) # undef EFAX_GTK_USE_FILE_SEL #else # define EFAX_GTK_USE_FILE_SEL 1 #endif #ifdef EFAX_GTK_USE_FILE_SEL # include # define EFAX_GTK_FILE_DIALOG_NEW gtk_file_selection_new(0) #else # include # include # define EFAX_GTK_FILE_DIALOG_NEW (gtk_file_chooser_dialog_new(0, 0, \ GTK_FILE_CHOOSER_ACTION_OPEN, \ gettext("View"), GTK_RESPONSE_NONE, \ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, \ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, \ static_cast(0))) #endif #include #include #include "dialogs.h" #include "utils/shared_handle.h" #include "utils/gobj_handle.h" #include "utils/mutex.h" #include "utils/utf8_utils.h" #include "gpl.h" #ifdef ENABLE_NLS #include #endif namespace { // callbacks for internal use only void FileReadSelectDialogCB::file_selected(GtkWidget* widget_p, void* data) { FileReadSelectDialog* instance_p = static_cast(data); gtk_widget_hide_all(GTK_WIDGET(instance_p->get_win())); if (widget_p == instance_p->ok_button_p) instance_p->set_result(); else if (widget_p == instance_p->cancel_button_p) instance_p->result.clear(); else { instance_p->result.clear(); write_error("Callback error in FileReadSelectDialogCB::file_selected()\n"); } instance_p->close(); } void FileReadSelectDialogCB::file_view_file(GtkWidget*, void* data) { static_cast(data)->view_file_impl(); } } // anonymous namespace FileReadSelectDialog::FileReadSelectDialog(int size, bool multi_files, GtkWindow* parent_p): WinBase(gettext("efax-gtk: File to fax"), prog_config.window_icon_h, true, parent_p, GTK_WINDOW(EFAX_GTK_FILE_DIALOG_NEW)), standard_size(size) { GtkWidget* view_button_p; #ifdef EFAX_GTK_USE_FILE_SEL GtkFileSelection* file_object_p = GTK_FILE_SELECTION(get_win()); gtk_file_selection_set_select_multiple(file_object_p, multi_files); if (!prog_config.working_dir.empty()) { std::string temp(prog_config.working_dir); temp += "/faxout/"; gtk_file_selection_set_filename(file_object_p, temp.c_str()); } gtk_file_selection_hide_fileop_buttons(file_object_p); GtkBox* hbox_p = GTK_BOX(file_object_p->action_area); gtk_box_set_homogeneous(hbox_p, false); GtkWidget* label_p = gtk_label_new(0); view_button_p = gtk_button_new_with_label(gettext("View")); gtk_box_pack_start(hbox_p, label_p, true, false, 0); gtk_box_pack_start(hbox_p, view_button_p, false, false, 0); GTK_WIDGET_SET_FLAGS(view_button_p, GTK_CAN_DEFAULT); ok_button_p = file_object_p->ok_button; cancel_button_p = file_object_p->cancel_button; #else GtkFileChooser* file_object_p = GTK_FILE_CHOOSER(get_win()); gtk_file_chooser_set_select_multiple(file_object_p, multi_files); if (!prog_config.working_dir.empty()) { std::string temp(prog_config.working_dir); temp += "/faxout"; gtk_file_chooser_set_current_folder(file_object_p, temp.c_str()); } GtkBox* hbox_p = GTK_BOX(GTK_DIALOG(get_win())->action_area); gtk_box_set_homogeneous(hbox_p, true); GList* button_list = gtk_container_get_children(GTK_CONTAINER(hbox_p)); if (g_list_length(button_list) < 3) { write_error("Button creation error in FileReadSelectDialog::FileReadSelectDialog()\n"); gtk_widget_show_all(GTK_WIDGET(get_win())); g_list_free(button_list); return; } ok_button_p = static_cast(g_list_nth_data(button_list, 0)); cancel_button_p = static_cast(g_list_nth_data(button_list, 1));; view_button_p = static_cast(g_list_nth_data(button_list, 2));; g_list_free(button_list); #endif g_signal_connect(G_OBJECT(ok_button_p), "clicked", G_CALLBACK(FileReadSelectDialogCB::file_selected), this); g_signal_connect(G_OBJECT(cancel_button_p), "clicked", G_CALLBACK(FileReadSelectDialogCB::file_selected), this); g_signal_connect(G_OBJECT(view_button_p), "clicked", G_CALLBACK(FileReadSelectDialogCB::file_view_file), this); gtk_window_set_type_hint(get_win(), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_position(get_win(), GTK_WIN_POS_CENTER_ON_PARENT); gtk_widget_show_all(GTK_WIDGET(get_win())); #ifdef EFAX_GTK_USE_FILE_SEL // now get and set the button size for view_button gtk_widget_set_size_request(view_button_p, cancel_button_p->allocation.width, cancel_button_p->allocation.height); #endif } void FileReadSelectDialog::on_delete_event(void) { gtk_widget_hide_all(GTK_WIDGET(get_win())); result.clear(); close(); } std::pair FileReadSelectDialog::get_view_file_parms(void) { std::vector view_parms; std::string view_cmd; std::string view_name; std::string::size_type end_pos; try { // lock the Prog_config object to stop it being accessed in // FaxListDialog::get_ps_viewer_parms() while we are accessing it here // (this is ultra cautious as it is only copied/checked for emptiness // there, and the GUI interface is insensitive if we are here) Thread::Mutex::Lock lock(*prog_config.mutex_p); view_cmd = Utf8::filename_from_utf8(prog_config.ps_view_cmd); } catch (Utf8::ConversionError&) { write_error("UTF-8 conversion error in FileReadSelectDialog::get_view_file_parms()\n"); return std::pair(0,0); } if ((end_pos = view_cmd.find_first_of(' ')) != std::string::npos) { // we have parms view_name.assign(view_cmd, 0, end_pos); view_parms.push_back(view_name); // find start of next parm std::string::size_type start_pos = view_cmd.find_first_not_of(' ', end_pos); while (start_pos != std::string::npos) { end_pos = view_cmd.find_first_of(' ', start_pos); if (end_pos != std::string::npos) { view_parms.push_back(view_cmd.substr(start_pos, end_pos - start_pos)); start_pos = view_cmd.find_first_not_of(' ', end_pos); // prepare for next interation } else { view_parms.push_back(view_cmd.substr(start_pos, view_cmd.size() - start_pos)); start_pos = end_pos; } } } else { // just a view command without parameters to be passed view_name = view_cmd; view_parms.push_back(view_name); } view_parms.push_back(get_filename_string()); char** exec_parms = new char*[view_parms.size() + 1]; char** temp_pp = exec_parms; std::vector::const_iterator iter; for (iter = view_parms.begin(); iter != view_parms.end(); ++iter, ++temp_pp) { *temp_pp = new char[iter->size() + 1]; std::strcpy(*temp_pp, iter->c_str()); } *temp_pp = 0; char* prog_name = new char[view_name.size() + 1]; std::strcpy(prog_name, view_name.c_str()); return std::pair(prog_name, exec_parms); } void FileReadSelectDialog::view_file_impl(void) { // check pre-conditions std::string filename(get_filename_string()); if (filename.empty() || filename[filename.size() - 1] == '/' || access(filename.c_str(), R_OK)) { beep(); return; } // get the arguments for the exec() call below (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 view_file_parms(get_view_file_parms()); if (view_file_parms.first) { // this will be 0 if get_view_file_parms() // threw a Utf8::ConversionError) pid_t pid = fork(); if (pid == -1) { write_error("Fork error - exiting\n"); std::exit(FORK_ERROR); } if (!pid) { // child process - as soon as everything is set up we are going to do an exec() connect_to_stderr(); execvp(view_file_parms.first, view_file_parms.second); // if we reached this point, then the execvp() call must have failed // report error and end process - use _exit() and not exit() write_error("Can't find the postscript viewer program - please check your installation\n" "and the PATH environmental variable\n"); _exit(0); } // end of view program process // release the memory allocated on the heap for // the redundant view_file_parms // we are in the main parent process here - no worries about // only being able to use async-signal-safe functions delete_parms(view_file_parms); } } void FileReadSelectDialog::delete_parms(std::pair 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; } std::string FileReadSelectDialog::get_filename_string(void) { #ifdef EFAX_GTK_USE_FILE_SEL return std::string((const char*)gtk_file_selection_get_filename(GTK_FILE_SELECTION(get_win()))); #else GcharScopedHandle file_name_h(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_win()))); if (file_name_h.get()) return std::string((const char*)file_name_h.get()); return std::string(); #endif } void FileReadSelectDialog::set_result(void) { result.clear(); #ifdef EFAX_GTK_USE_FILE_SEL gchar** file_array = gtk_file_selection_get_selections(GTK_FILE_SELECTION(get_win())); gchar** temp_p = file_array; for (; *temp_p; temp_p++) { result.push_back((const char*)*temp_p); } g_strfreev (file_array); #else GSList* file_list_p = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(get_win())); void* elem = g_slist_nth_data(file_list_p, 0); // first element for (guint count = 1; elem; count++) { result.push_back((const char*)elem); g_free(elem); elem = g_slist_nth_data(file_list_p, count); } g_slist_free(file_list_p); #endif try { std::transform(result.begin(), result.end(), result.begin(), Utf8::filename_to_utf8); } catch (Utf8::ConversionError&) { write_error("UTF-8 conversion error in FileReadSelectDialog::set_result()\n"); } } namespace { // callbacks for internal use only void GplDialogCB::gpl_selected(GtkWidget* widget_p, void* data) { GplDialog* instance_p = static_cast(data); gtk_widget_hide_all(GTK_WIDGET(instance_p->get_win())); if (widget_p == instance_p->accept_button_p) instance_p->result = GplDialog::accepted; else if (widget_p == instance_p->reject_button_p) instance_p->result = GplDialog::rejected; else write_error("Callback error in GplDialogCB::gpl_selected()\n"); instance_p->close(); } gboolean GplDialogCB::gpl_key_press_event(GtkWidget*, GdkEventKey* event_p, void* data) { GplDialog* instance_p = static_cast(data); int keycode = event_p->keyval; if (keycode == GDK_Escape) instance_p->on_delete_event(); else if (keycode == GDK_Home || keycode == GDK_End || keycode == GDK_Up || keycode == GDK_Down || keycode == GDK_Page_Up || keycode == GDK_Page_Down) { gtk_widget_event(GTK_WIDGET(instance_p->text_view_p), (GdkEvent*)event_p); } return true; // stop processing here } void GplDialogCB::gpl_style_set(GtkWidget*, GtkStyle*, void* data) { GplDialog* instance_p = static_cast(data); //now set the buttons in GplDialog int width = 0; int height = 0; // create a button to work on - the GobjHandle object will claim ownership of the button GobjHandle button_h(gtk_button_new()); GtkStyle* style_p = gtk_widget_get_style(GTK_WIDGET(instance_p->get_win())); gtk_widget_set_style(button_h, style_p); GobjHandle pango_layout_h(gtk_widget_create_pango_layout(button_h, instance_p->max_text.c_str())); pango_layout_get_pixel_size(pango_layout_h, &width, &height); // add padding width += 18; height += 12; // have some sensible minimum width and height if a very small font chosen const int min_width = instance_p->standard_size * 4; const int min_height = 30; if (width < min_width) width = min_width; if (height < min_height) height = min_height; gtk_widget_set_size_request(instance_p->accept_button_p, width, height); gtk_widget_set_size_request(instance_p->reject_button_p, width, height); } } // anonymous namespace GplDialog::GplDialog(int size): WinBase(gettext("efax-gtk: Conditions, Notices and Disclaimers"), prog_config.window_icon_h, true), standard_size(size), result(rejected) { accept_button_p = gtk_button_new_with_label(gettext("Accept")); reject_button_p = gtk_button_new_with_label(gettext("Reject")); GtkWidget* label_p = gtk_label_new(gettext("Do you accept the Conditions, Notices " "and Disclaimers shown above?")); GtkTable* table_p = GTK_TABLE(gtk_table_new(3, 2, false)); text_view_p = GTK_TEXT_VIEW(gtk_text_view_new()); gtk_text_view_set_editable(text_view_p, false); PangoFontDescription* font_description = pango_font_description_from_string(prog_config.fixed_font.c_str()); gtk_widget_modify_font(GTK_WIDGET(text_view_p), font_description); pango_font_description_free(font_description); gtk_text_buffer_set_text(gtk_text_view_get_buffer(text_view_p), gpl_copyright_msg()->c_str(), -1); GtkScrolledWindow* scrolled_window_p = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0)); gtk_scrolled_window_set_shadow_type(scrolled_window_p, GTK_SHADOW_IN); gtk_scrolled_window_set_policy(scrolled_window_p, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(scrolled_window_p), GTK_WIDGET(text_view_p)); get_longest_button_text(); gtk_table_attach(table_p, GTK_WIDGET(scrolled_window_p), 0, 2, 0, 1, GtkAttachOptions(GTK_FILL | GTK_EXPAND), GtkAttachOptions(GTK_FILL | GTK_EXPAND), 0, 0); gtk_table_attach(table_p, label_p, 0, 2, 1, 2, GtkAttachOptions(GTK_FILL | GTK_EXPAND), GTK_SHRINK, 0, standard_size/3); gtk_table_attach(table_p, accept_button_p, 0, 1, 2, 3, GTK_SHRINK, GTK_SHRINK, 0, standard_size/3); gtk_table_attach(table_p, reject_button_p, 1, 2, 2, 3, GTK_SHRINK, GTK_SHRINK, 0, standard_size/3); g_signal_connect(G_OBJECT(get_win()), "style_set", G_CALLBACK(GplDialogCB::gpl_style_set), this); g_signal_connect(G_OBJECT(accept_button_p), "clicked", G_CALLBACK(GplDialogCB::gpl_selected), this); g_signal_connect(G_OBJECT(reject_button_p), "clicked", G_CALLBACK(GplDialogCB::gpl_selected), this); g_signal_connect(G_OBJECT(get_win()), "key_press_event", G_CALLBACK(GplDialogCB::gpl_key_press_event), this); gtk_container_add(GTK_CONTAINER(get_win()), GTK_WIDGET(table_p)); gtk_window_set_default_size(get_win(), standard_size * 25, standard_size * 16); gtk_container_set_border_width(GTK_CONTAINER(get_win()), standard_size/4); gtk_window_set_position(get_win(), GTK_WIN_POS_CENTER_ON_PARENT); gtk_widget_grab_focus(GTK_WIDGET(get_win())); GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(text_view_p), GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(accept_button_p, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(reject_button_p, GTK_CAN_FOCUS); gtk_widget_show_all(GTK_WIDGET(get_win())); } int GplDialog::get_exec_val(void) const { return result; } void GplDialog::on_delete_event(void) { gtk_widget_hide_all(GTK_WIDGET(get_win())); result = rejected; close(); } void GplDialog::get_longest_button_text(void) { std::vector text_vec; text_vec.push_back(gettext("Accept")); text_vec.push_back(gettext("Reject")); // create a button to work on - the GobjHandle object will claim ownership of the button GobjHandle button_h(gtk_button_new()); std::vector::const_iterator temp_iter; std::vector::const_iterator max_width_iter; int width; int height; int max_width; for (temp_iter = text_vec.begin(), max_width_iter = text_vec.begin(), width = 0, height = 0, max_width = 0; temp_iter != text_vec.end(); ++temp_iter) { GobjHandle pango_layout_h(gtk_widget_create_pango_layout(button_h, temp_iter->c_str())); pango_layout_get_pixel_size(pango_layout_h, &width, &height); if (width > max_width) { max_width = width; max_width_iter = temp_iter; } } max_text = *max_width_iter; } namespace { // callbacks for internal use only void InfoDialogCB::info_selected(GtkDialog*, int, void* data) { static_cast(data)->close(); } } // anonymous namespace InfoDialog::InfoDialog(const char* text, const char* caption, GtkMessageType message_type, GtkWindow* parent_p, bool modal): WinBase(caption, prog_config.window_icon_h, modal, parent_p, GTK_WINDOW(gtk_message_dialog_new(parent_p, GtkDialogFlags(0), message_type, GTK_BUTTONS_CLOSE, text))) { // make further specialisations for a message dialog object if (!GTK_CHECK_VERSION(2, 4, 0)) { gtk_dialog_set_has_separator(GTK_DIALOG(get_win()), false); } gtk_window_set_type_hint(get_win(), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_button_box_set_layout(GTK_BUTTON_BOX(GTK_DIALOG(get_win())->action_area), GTK_BUTTONBOX_SPREAD); g_signal_connect(G_OBJECT(get_win()), "response", G_CALLBACK(InfoDialogCB::info_selected), this); gtk_window_set_position(get_win(), GTK_WIN_POS_CENTER_ON_PARENT); gtk_window_set_resizable(get_win(), false); gtk_widget_show_all(GTK_WIDGET(get_win())); } namespace { // callbacks for internal use only void PromptDialogCB::prompt_selected(GtkWidget* widget_p, void* data) { PromptDialog* instance_p = static_cast(data); gtk_widget_hide_all(GTK_WIDGET(instance_p->get_win())); if (widget_p == instance_p->ok_button_p) { instance_p->result = true; instance_p->accepted(); } else if (widget_p == instance_p->cancel_button_p) { instance_p->result = false; instance_p->rejected(); } else write_error("Callback error in PromptDialogCB::prompt_selected()\n"); instance_p->close(); } } // anonymous namespace PromptDialog::PromptDialog(const char* text, const char* caption, int standard_size, GtkWindow* parent_p, bool modal): WinBase(caption, prog_config.window_icon_h, modal, parent_p), result(false) { ok_button_p = gtk_button_new_from_stock(GTK_STOCK_OK); cancel_button_p = gtk_button_new_from_stock(GTK_STOCK_CANCEL); GtkWidget* button_box_p = gtk_hbutton_box_new(); GtkWidget* label_p = gtk_label_new(text); GtkTable* table_p = GTK_TABLE(gtk_table_new(2, 1, false)); gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box_p), GTK_BUTTONBOX_END); gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_box_p), standard_size/2); gtk_label_set_line_wrap(GTK_LABEL(label_p), true); gtk_container_add(GTK_CONTAINER(button_box_p), cancel_button_p); gtk_container_add(GTK_CONTAINER(button_box_p), ok_button_p); gtk_table_attach(table_p, label_p, 0, 1, 0, 1, GtkAttachOptions(GTK_FILL | GTK_EXPAND), GtkAttachOptions(GTK_FILL | GTK_EXPAND), standard_size/2, standard_size/4); gtk_table_attach(table_p, button_box_p, 0, 1, 1, 2, GtkAttachOptions(GTK_FILL | GTK_EXPAND), GTK_SHRINK, standard_size/2, standard_size/4); g_signal_connect(G_OBJECT(ok_button_p), "clicked", G_CALLBACK(PromptDialogCB::prompt_selected), this); g_signal_connect(G_OBJECT(cancel_button_p), "clicked", G_CALLBACK(PromptDialogCB::prompt_selected), this); GTK_WIDGET_SET_FLAGS(ok_button_p, GTK_CAN_DEFAULT); GTK_WIDGET_SET_FLAGS(cancel_button_p, GTK_CAN_DEFAULT); gtk_container_add(GTK_CONTAINER(get_win()), GTK_WIDGET(table_p)); gtk_window_set_type_hint(get_win(), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_container_set_border_width(GTK_CONTAINER(get_win()), standard_size/2); gtk_widget_grab_focus(ok_button_p); gtk_window_set_position(get_win(), GTK_WIN_POS_CENTER_ON_PARENT); gtk_window_set_resizable(get_win(), false); gtk_widget_show_all(GTK_WIDGET(get_win())); } int PromptDialog::get_exec_val(void) const { return result; } void PromptDialog::on_delete_event(void) { gtk_widget_hide_all(GTK_WIDGET(get_win())); result = false; rejected(); close(); }