Logo Search packages:      
Sourcecode: efax-gtk version File versions

dialogs.cpp

/* Copyright (C) 2001 2002 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 <sys/types.h>
#include <unistd.h>

#include <algorithm>
#include <cstdlib>
#include <cstring>

#include <gtkmm/main.h>
#include <gdk/gdkkeysyms.h> // the key codes are here
#include <gdkmm/pixbuf.h>
#include <gtkmm/stock.h>
#include <glibmm/timer.h>
#include <glibmm/convert.h>

#if GTKMM_VERSION >= 24
#include <gtk/gtkfilechooser.h>
#include <glibmm/slisthandle.h>
#endif

#include "dialogs.h"
#include "shared_handle.h"
#include "gpl.h"

#ifdef ENABLE_NLS
#include <libintl.h>
#endif

FileReadSelectDialog::FileReadSelectDialog(int size, bool multi_files, Gtk::Window& window):
                                           FileDialog(gettext("efax-gtk: File to fax")),
                                 in_exec_loop(false), standard_size(size),
                                 view_button(gettext("View")),
                                 parent(window) {

  if (multi_files) set_select_multiple();

  if (!prog_config.working_dir.empty()) {
    std::string temp(prog_config.working_dir);
#if GTKMM_VERSION >= 24
    temp +=  "/faxout";
    // gtkmm-2.4 incorrectly uses Glib::ustring for filename parameters
    // where the contents will not necessarily comprise valid UTF-8, so
    // use the raw C function
    gtk_file_chooser_set_current_folder(static_cast<Gtk::FileChooser*>(this)->gobj(),
                              (const gchar*)temp.c_str());
#else
    temp +=  "/faxout/";
    set_filename(temp);
#endif
  }

#if GTKMM_VERSION >= 24
  get_action_area()->set_homogeneous(true);

  get_action_area()->pack_start(view_button, false, false, 0);
  view_button.signal_clicked().connect(sigc::mem_fun(*this, &FileReadSelectDialog::view_file));

  Gtk::Button* button_p = manage(new Gtk::Button(Gtk::Stock::CANCEL));
  get_action_area()->pack_start(*button_p, false, false, 0);
  button_p->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &FileReadSelectDialog::selected), false));
  button_p->set_flags(Gtk::CAN_DEFAULT);

  button_p = manage(new Gtk::Button(Gtk::Stock::OK));
  get_action_area()->pack_start(*button_p, false, false, 0);
  button_p->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &FileReadSelectDialog::selected), true));
  button_p->set_flags(Gtk::CAN_DEFAULT);

  view_button.set_flags(Gtk::CAN_DEFAULT);

#else

  hide_fileop_buttons();
  get_action_area()->set_homogeneous(false);

  Gtk::Label* label_p = manage (new Gtk::Label);
  get_action_area()->pack_start(*label_p, true, false, 0);
  get_action_area()->pack_start(view_button, false, false, 0);
  view_button.signal_clicked().connect(sigc::mem_fun(*this, &FileReadSelectDialog::view_file));

  get_ok_button()->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &FileReadSelectDialog::selected), true));
  get_cancel_button()->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &FileReadSelectDialog::selected), false));
  view_button.set_flags(Gtk::CAN_DEFAULT);

#endif

  set_transient_for(parent);
  set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG);
  parent.set_sensitive(false);
  set_modal(true);
  set_position(Gtk::WIN_POS_CENTER_ON_PARENT);

  set_icon(prog_config.window_icon_r);

  show_all();

#if GTKMM_VERSION < 24
  // now get and set the button size for view_button
  view_button.set_size_request(get_cancel_button()->get_width(), get_cancel_button()->get_height());
#endif
}

std::pair<const char*, char* const*> FileReadSelectDialog::get_view_file_parms(void) {

  std::vector<std::string> 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)
    Glib::Mutex::Lock lock(*prog_config.mutex_p);
    view_cmd = Glib::filename_from_utf8(prog_config.ps_view_cmd);
  }
  catch (Glib::ConvertError&) {
    write_error("UTF-8 conversion error in FileReadSelectDialog::get_view_file_parms()\n");
    return std::pair<const char*, char* const*>(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<std::string>::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<const char*, char* const*>(prog_name, exec_parms);
}

void FileReadSelectDialog::view_file(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<const char*, char* const*> view_file_parms(get_view_file_parms());

  if (view_file_parms.first) { // this will be 0 if get_view_file_parms()
                               // threw a Glib::ConvertError)

    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<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;
}

std::vector<Glib::ustring> FileReadSelectDialog::exec(void) {
  in_exec_loop = true;
  Gtk::Main::run();
  return result;
}

std::string FileReadSelectDialog::get_filename_string(void) {
#if GTKMM_VERSION >= 24
  // in gtkmm-2.4, Gtk::FileChooser::get_filename() erroneously
  // returns a Glib::ustring object  without converting
  // to UTF-8 - we therefore need to get the gchar* string to handle
  // correctly whatever the filesystem codeset is
  Gchar_scoped_handle file_name(gtk_file_chooser_get_filename(static_cast<Gtk::FileChooser*>(this)->gobj()));
  std::string return_val;
  if (file_name.get()) return_val = (const char*)file_name.get();
  return return_val;
#else
  return get_filename();
#endif
}

std::vector<std::string> FileReadSelectDialog::get_filenames_vec(void) {

#if GTKMM_VERSION >= 24
  // in gtkmm-2.4, Gtk::FileChooser::get_filenames() erroneously
  // returns a Glib::SListHandle<Glib::ustring> without converting
  // to UTF-8 - we therefore need to get a Glib::SListHandle<std::string>
  // object to handle correctly whatever the filesystem codeset is
  return Glib::SListHandle<std::string>(
             gtk_file_chooser_get_filenames(static_cast<Gtk::FileChooser*>(this)->gobj()),
             Glib::OWNERSHIP_DEEP);
#else
  return get_selections();
#endif
}

void FileReadSelectDialog::selected(bool accept) {
  if (accept) {
    std::vector<std::string> vec(get_filenames_vec());

    try {
      std::transform(vec.begin(), vec.end(), back_inserter(result),
                 Glib::filename_to_utf8);
    }
    catch (Glib::ConvertError&) {
      write_error("UTF-8 conversion error in FileReadSelectDialog::selected()\n");
    }
  }
  finish();
}

void FileReadSelectDialog::finish(void) {
  parent.set_sensitive(true);
  hide_all();
  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 FileReadSelectDialog::on_delete_event(GdkEventAny*) {
  finish();
  return true; // returning true prevents destroy sig being emitted
}

GplDialog::GplDialog(int size): Gtk::Window(Gtk::WINDOW_TOPLEVEL),
                             in_exec_loop(false), standard_size(size),
                       result(rejected), accept_button(gettext("Accept")),
                       reject_button(gettext("Reject")),
                         label(gettext("Do you accept the Conditions, Notices "
                                 "and Disclaimers shown above?")),
                       table(3, 2, false) {

  editbox.set_editable(false);
  editbox.modify_font(Pango::FontDescription(prog_config.fixed_font));
  editbox.get_buffer()->set_text(copyright_msg);

  scrolled_window.set_shadow_type(Gtk::SHADOW_IN);
  scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
  scrolled_window.add(editbox);

  get_longest_button_text();

  table.attach(scrolled_window, 0, 2, 0, 1, Gtk::FILL | Gtk::EXPAND,
             Gtk::FILL | Gtk::EXPAND, 0, 0);
  table.attach(label, 0, 2, 1, 2, Gtk::FILL | Gtk::EXPAND,
             Gtk::SHRINK, 0, standard_size/3);
  table.attach(accept_button, 0, 1, 2, 3, Gtk::SHRINK,
             Gtk::SHRINK, 0, standard_size/3);
  table.attach(reject_button, 1, 2, 2, 3, Gtk::SHRINK,
             Gtk::SHRINK, 0, standard_size/3);

  accept_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &GplDialog::selected), accepted));
  reject_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &GplDialog::selected), rejected));

  add(table);
  
  set_title(gettext("efax-gtk: Conditions, Notices and Disclaimers"));
  set_modal(true);

  set_default_size(standard_size * 25, standard_size * 16);
  
  set_border_width(standard_size/4);
  set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
  
  grab_focus();
  accept_button.unset_flags(Gtk::CAN_FOCUS);
  reject_button.unset_flags(Gtk::CAN_FOCUS);
  editbox.unset_flags(Gtk::CAN_FOCUS);
  
  show_all();
}

int GplDialog::exec(void) {
  in_exec_loop = true;
  Gtk::Main::run();
  return result;
}

void GplDialog::selected(Result selection) {
  hide_all();
  result = selection;
  if (in_exec_loop) Gtk::Main::quit(); // this will cause the exec() method to return, with result as its return value
}

bool GplDialog::on_delete_event(GdkEventAny*) {
  selected(rejected);
  return true; // returning true prevents destroy sig being emitted
}

bool GplDialog::on_key_press_event(GdkEventKey* event_p) {

  int keycode = event_p->keyval;
  
  if (keycode == GDK_Escape) selected(rejected);
  
  else if (keycode == GDK_Home || keycode == GDK_End
         || keycode == GDK_Up || keycode == GDK_Down
         || keycode == GDK_Page_Up || keycode == GDK_Page_Down) {
    editbox.on_key_press_event(event_p);
    return false;
  }
  return true;
}

void GplDialog::get_longest_button_text(void) {
  std::vector<Glib::ustring> text_vec;
  text_vec.push_back(gettext("Accept"));
  text_vec.push_back(gettext("Reject"));

  Gtk::Button button;
  std::vector<Glib::ustring>::const_iterator temp_iter;
  std::vector<Glib::ustring>::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) {

    button.create_pango_layout(*temp_iter)->get_pixel_size(width, height);

    if (width > max_width) {
      max_width = width;
      max_width_iter = temp_iter;
    }
  }
  max_text = *max_width_iter;
}

void GplDialog::on_style_changed(const Glib::RefPtr<Gtk::Style>& prev_style) {

  // before we set the buttons, do anything else necessary with the new style
  Gtk::Window::on_style_changed(prev_style);

  //now set the buttons in GplDialog
  int width = 0;
  int height = 0;
  Gtk::Button button;

  button.set_style(get_style());
  button.create_pango_layout(max_text)->get_pixel_size(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 = standard_size * 4;
  const int min_height = 30;
  if (width < min_width) width = min_width;
  if (height < min_height) height = min_height;

  accept_button.set_size_request(width, height);
  reject_button.set_size_request(width, height);
}

InfoDialog::InfoDialog(const Glib::ustring& text, const Glib::ustring& caption, int standard_size,
                       Gtk::MessageType message_type, Gtk::Window& window, bool modal):
#if GTKMM_VERSION >= 24
                             Gtk::MessageDialog(text, false, message_type, Gtk::BUTTONS_CLOSE),
#else
                             Gtk::MessageDialog(text, message_type, Gtk::BUTTONS_CLOSE),
#endif
                       in_exec_loop(false), is_modal(modal),
                       parent(window) {

  set_title(caption);
  set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG);

  if (is_modal) {
    set_transient_for(parent);
    parent.set_sensitive(false);
    set_modal(true);
  }

  get_action_area()->set_layout(Gtk::BUTTONBOX_SPREAD);
  Gtk::Button* button_p = static_cast<Gtk::Button*>(get_action_area()->children().front().get_widget());
  button_p->signal_clicked().connect(sigc::mem_fun(*this, &InfoDialog::selected));

  set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
  set_resizable(false);

  set_icon(prog_config.window_icon_r);

  show_all();
}

void InfoDialog::exec(void) {
  in_exec_loop = true;
  Gtk::Main::run();
}

void InfoDialog::selected(void) {
  if (is_modal) parent.set_sensitive(true);
  hide_all();
  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 InfoDialog::on_delete_event(GdkEventAny*) {
  selected();
  return true; // returning true prevents destroy sig being emitted
}

bool InfoDialog::on_key_press_event(GdkEventKey* event_p) {
  if (event_p->keyval == GDK_Return || event_p->keyval == GDK_Escape) selected();
  return false;
}

PromptDialog::PromptDialog(const Glib::ustring& text, const Glib::ustring& caption, int standard_size, Gtk::Window& window, bool modal):
                             Gtk::Window(Gtk::WINDOW_TOPLEVEL),
                       in_exec_loop(false), is_modal(modal),
                       result(false), ok_button(Gtk::Stock::OK),
                       cancel_button(Gtk::Stock::CANCEL),
                       button_box(Gtk::BUTTONBOX_END, standard_size/2),
                       label(text), table(2, 1, false), parent(window) {

  label.set_line_wrap(true);

  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(button_box, 0, 1, 1, 2, Gtk::FILL | Gtk::EXPAND,
       Gtk::SHRINK, standard_size/2, standard_size/4);

  ok_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &PromptDialog::selected), true));
  cancel_button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &PromptDialog::selected), false));

  ok_button.set_flags(Gtk::CAN_DEFAULT);
  cancel_button.set_flags(Gtk::CAN_DEFAULT);

  add(table);
  
  set_title(caption);
  set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG);

  if (is_modal) {
    set_transient_for(parent);
    parent.set_sensitive(false);
    set_modal(true);
  }

  set_border_width(standard_size/2);
  ok_button.grab_focus();
  set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
  set_resizable(false);

  set_icon(prog_config.window_icon_r);

  show_all();
}

bool PromptDialog::exec(void) {
  in_exec_loop = true;
  Gtk::Main::run();
  return result;
}

void PromptDialog::selected(bool accept) {
  if (is_modal) parent.set_sensitive(true); // do this before we emit accepted()
  hide_all();
  result = accept;
  if (accept) accepted();
  else rejected();
  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 PromptDialog::on_delete_event(GdkEventAny*) {
  selected(false);
  return true; // returning true prevents destroy sig being emitted
}

bool PromptDialog::on_key_press_event(GdkEventKey* event_p) {
  if (event_p->keyval == GDK_Escape) selected(false);
  if (event_p->keyval == GDK_Return || event_p->keyval == GDK_Tab) Gtk::Window::on_key_press_event(event_p);
  return false;
}

Generated by  Doxygen 1.6.0   Back to index