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

socket_list.cpp

/* Copyright (C) 2001 to 2005 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 <cstdlib>
#include <vector>

#include <gdkmm/pixbuf.h>
#include <gtkmm/image.h>
#include <gtkmm/stock.h>
#include <gtkmm/label.h>
#include <gtkmm/tooltips.h>
#include <glibmm/timer.h>
#include <glibmm/convert.h>

#include "socket_list.h"
#include "dialogs.h"
#include "socket_list_icons.h"

#ifdef HAVE_STRINGSTREAM
#include <sstream>
#else
#include <strstream>
#endif

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

int SocketListDialog::is_socket_list = 0;


SocketListDialog::SocketListDialog(std::pair<Shared_ptr<FilenamesList>,
                                         Shared_ptr<Glib::Mutex::Lock> > filenames_pair,
                           const int standard_size_):
                               Gtk::Window(Gtk::WINDOW_TOPLEVEL),
                         standard_size(standard_size_), socket_list_box(false, 0),
                         table(2, 1, false),
                         close_button(Gtk::Stock::CLOSE),
                         button_box(Gtk::BUTTONBOX_SPREAD) {

  // notify the existence of this object
  is_socket_list++;

  socket_list_scroll_window.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
  socket_list_scroll_window.add(tree_view);
  socket_list_scroll_window.set_shadow_type(Gtk::SHADOW_IN);

  // create the tree model:
  list_store_r = Gtk::ListStore::create(model_columns);
  // connect it to the tree view
  tree_view.set_model(list_store_r);
  // add the first column of the tree model to tree view
  // (the second column is hidden and just contains data (a file name) to
  // which the first column relates)
  tree_view.append_column(gettext("Queued print jobs"), model_columns.fax_label);
  // single line selection
  tree_view.get_selection()->set_mode(Gtk::SELECTION_SINGLE);
  // populate the socket file list
  set_socket_list_rows(filenames_pair);

  // set up the tool bar
  tool_bar.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
  tool_bar.set_toolbar_style(Gtk::TOOLBAR_ICONS);

  // first make the image widgets
  Gtk::Image* view_image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(view_xpm)));
  Gtk::Image* remove_image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(remove_xpm)));
  Gtk::Image* send_image_p = manage(new Gtk::Image(Gdk::Pixbuf::create_from_xpm_data(send_fax_xpm)));

  tool_bar.set_tooltips(true);

  Gtk::HBox* send_hbox_p = manage(new Gtk::HBox);
  Gtk::Label* send_label_p = manage(new Gtk::Label(gettext("Enter selected fax to send")));
  send_hbox_p->pack_start(*send_image_p, Gtk::PACK_SHRINK, 4);
  send_hbox_p->pack_start(*send_label_p, Gtk::PACK_SHRINK, 4);

#if GTKMM_VERSION >= 24

  // if using GTK+ 2.4 or higher, normalise the height of images in the
  // Gtk::Toolbar object (I have no idea why this should be necessary)
  view_image_p->set_size_request(-1, 26);
  remove_image_p->set_size_request(-1, 26);
  send_image_p->set_size_request(-1, 26);

  tool_bar.set_toolbar_style(Gtk::TOOLBAR_ICONS);

  Gtk::Tooltips* tooltips_p = tool_bar.get_tooltips_object();
  tooltips_p->enable();

  view_button_p = manage(new Gtk::ToolButton(*view_image_p));
  tool_bar.append(*view_button_p, sigc::mem_fun(*this, &SocketListDialog::view_file));
  view_button_p->set_sensitive(false);
  view_button_p->set_tooltip(*tooltips_p, gettext("View selected file"));

  remove_button_p = manage(new Gtk::ToolButton(*remove_image_p));
  tool_bar.append(*remove_button_p, sigc::mem_fun(*this, &SocketListDialog::remove_file_prompt));
  remove_button_p->set_sensitive(false);
  remove_button_p->set_tooltip(*tooltips_p, gettext("Remove selected file from list"));

  send_button_p = manage(new Gtk::ToolButton(*send_hbox_p));
  tool_bar.append(*send_button_p, sigc::mem_fun(*this, &SocketListDialog::send_fax_slot));
  send_button_p->set_sensitive(false);
  send_button_p->set_tooltip(*tooltips_p, gettext("Choose the selected fax for sending"));

#else
  {

    using namespace Gtk::Toolbar_Helpers;
    
    send_button.add(*send_hbox_p);

    ToolList& tool_list = tool_bar.tools();
    tool_list.push_back(ButtonElem(*view_image_p, SigC::slot(*this, &SocketListDialog::view_file),
                           gettext("View selected file")));
    view_button_p = static_cast<Gtk::Button*>(tool_list.back().get_widget());
    view_button_p->set_relief(Gtk::RELIEF_NONE);
    view_button_p->set_sensitive(false);

    tool_list.push_back(ButtonElem(*remove_image_p, SigC::slot(*this, &SocketListDialog::remove_file_prompt),
                           gettext("Remove selected file from list")));
    remove_button_p = static_cast<Gtk::Button*>(tool_list.back().get_widget());
    remove_button_p->set_relief(Gtk::RELIEF_NONE);
    remove_button_p->set_sensitive(false);

    tool_list.push_back(Element(send_button, gettext("Choose the selected fax for sending")));
    send_button.signal_clicked().connect(SigC::slot(*this, &SocketListDialog::send_fax_slot));

    send_button.set_relief(Gtk::RELIEF_NONE);
    send_button.set_sensitive(false);
  }
#endif

  button_box.add(close_button);

  table.attach(socket_list_scroll_window, 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL,
         Gtk::EXPAND | Gtk::FILL, standard_size/3, standard_size/3);
  table.attach(button_box, 0, 1, 1, 2, Gtk::EXPAND | Gtk::FILL,
       Gtk::SHRINK, standard_size/3, standard_size/3);

  socket_list_box.pack_start(tool_bar, false, false);
  socket_list_box.pack_start(table, true, true);

  // now connect up the signal which indicates a selection has been made
  tree_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SocketListDialog::set_buttons_slot));
  close_button.signal_clicked().connect(sigc::mem_fun(*this, &SocketListDialog::close_slot));

  close_button.set_flags(Gtk::CAN_DEFAULT);

  table.set_border_width(standard_size/3);
  set_title(gettext("efax-gtk: Queued faxes from socket"));
  //set_position(Gtk::WIN_POS_CENTER);
  add(socket_list_box);

  set_default_size(standard_size * 15, standard_size * 12);

  set_icon(prog_config.window_icon_r);

  show_all();

#if GTKMM_VERSION >= 24
  tool_bar.set_size_request(send_button_p->get_width() + view_button_p->get_width()
                      + remove_button_p->get_width() + 12, send_button_p->get_height());
#endif
}

SocketListDialog::~SocketListDialog(void) {
  // notify the destruction of this object
  is_socket_list--;
}

void SocketListDialog::send_fax_slot(void) {

  std::pair<Glib::ustring, std::string> fax_to_send;
  Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected();
  if (row_iter) {
    fax_to_send.first = (*row_iter)[model_columns.fax_label];
    fax_to_send.second = (*row_iter)[model_columns.fax_filename];
    
    selected_fax(fax_to_send);
  }
}

void SocketListDialog::close_slot(void) {
  hide_all();
  dialog_closed();
  delete this;  // this is completely safe as the dialog is self-owning and modeless
}

bool SocketListDialog::on_delete_event(GdkEventAny*) {
  close_slot();
  return true; // returning true prevents destroy sig being emitted
}

void SocketListDialog::set_socket_list_rows(std::pair<Shared_ptr<FilenamesList>,
                                            Shared_ptr<Glib::Mutex::Lock> > filenames_pair) {

  FilenamesList::const_iterator iter;

  list_store_r->clear();

  for (iter = filenames_pair.first->begin(); iter != filenames_pair.first->end(); ++iter) {

    // iter->first will contain the temporary file name created by mkstemp
    // in void Socket_server::accept_client()
    // iter->second will contain the print job number assigned by
    // Socket_serve::add_file()

    // get a list store row to insert the file name
    Gtk::TreeModel::Row row = *(list_store_r->append());

    // manufacture the fax label
#ifdef HAVE_STRINGSTREAM
    std::ostringstream strm;
    strm << gettext("PRINT JOB: ") << iter->second;
    row[model_columns.fax_label] = strm.str();
#else
    std::ostrstream strm;
    strm << gettext("PRINT JOB: ") << iter->second << std::ends;
    const char* fax_label_p = strm.str();
    row[model_columns.fax_label] = fax_label_p;
    delete[] fax_label_p;
#endif

    // insert the fax filename (this is a hidden column, which will not
    // be displayed in the tree view)
    row[model_columns.fax_filename] = iter->first;
  }
}

void SocketListDialog::set_buttons_slot(void) {

  // see if anything is selected
  if (tree_view.get_selection()->get_selected()) {
    view_button_p->set_sensitive(true);
    remove_button_p->set_sensitive(true);

#if GTKMM_VERSION >= 24
    send_button_p->set_sensitive(true);
#else
    view_button_p->set_relief(Gtk::RELIEF_NORMAL);
    remove_button_p->set_relief(Gtk::RELIEF_NORMAL);
    send_button.set_sensitive(true);
    send_button.set_relief(Gtk::RELIEF_NORMAL);
#endif
  }
    
  else {
    view_button_p->set_sensitive(false);
    remove_button_p->set_sensitive(false);

#if GTKMM_VERSION >= 24
    send_button_p->set_sensitive(false);
#else
    view_button_p->set_relief(Gtk::RELIEF_NONE);
    remove_button_p->set_relief(Gtk::RELIEF_NONE);
    send_button.set_sensitive(false);
    send_button.set_relief(Gtk::RELIEF_NONE);
#endif
  }
}

std::pair<const char*, char* const*> SocketListDialog::get_view_file_parms(const string& file_name) {

  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)
    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 SocketListDialog::view_file()\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(file_name);

  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 SocketListDialog::view_file(void) {

  Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected();

  bool is_ps_view_cmd_empty;
  { // 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)
    Glib::Mutex::Lock lock(*prog_config.mutex_p);
    is_ps_view_cmd_empty = prog_config.ps_view_cmd.empty();
  }
  if (row_iter && !is_ps_view_cmd_empty) {
    
    const std::string file_name((*row_iter)[model_columns.fax_filename]);

    // 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(file_name));

    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 SocketListDialog::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 SocketListDialog::remove_file_prompt(void) {

  Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected();
  if (row_iter) {
    Glib::ustring msg(gettext("Remove "));
    msg += (*row_iter)[model_columns.fax_label] + gettext(" from the list?");
    
    PromptDialog* dialog_p = new PromptDialog(msg, gettext("efax-gtk: Remove queued fax"), standard_size, *this);
    dialog_p->accepted.connect(sigc::mem_fun(*this, &SocketListDialog::remove_file));
    // there is no memory leak -- the memory will be deleted when PromptDialog closes
  }
}

void SocketListDialog::remove_file(void) {

  Gtk::TreeModel::iterator row_iter = tree_view.get_selection()->get_selected();
  if (row_iter) {

    remove_from_socket_server_filelist((*row_iter)[model_columns.fax_filename]);

    // the last call will cause the Socket_server object to invoke
    // Socket_server::Glib::Dispatcher::filelist_changed_notify() which
    // will in turn cause the MainWindow object to invoke
    // set_socket_list_rows() above and so refresh the socket file
    // list. The server object will also clean up by deleting the temporary
    // file it created.  We do not need to do anything else here.
  }
}

Generated by  Doxygen 1.6.0   Back to index