Logo Search packages:      
Sourcecode: ardour version File versions  Download package

newsavedialog.cc

#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <algorithm>

#include <gtk--/separator.h>

#include "i18n.h"

#include <pbd/error.h>
#include <pbd/basename.h>
#include <pbd/dirname.h>

#include <gtkmmext/newsavedialog.h>
#include <gtkmmext/utils.h>

using namespace std;
using namespace Gtkmmext;
using namespace Gtk;

NewSaveDialog::NewSaveDialog (string label, string action)
      : table (2, 2),
        entry_label (label),
        where_label (_("Location:")),
        where_browse (_("Browse ...")),
        cancel_button (_("Cancel")),
        op_button (action),
        new_dir_button (_("New folder")),
        add_to_favorites_button (_("Add to favorites")),
        remove_from_favorites_button (_("Remove from favorites")),
        show_hidden_check (_("Show Hidden")),
        ignore_selection(false),
        ignore_path_entry(false),
        ignore_where_combo(false),
        show_hidden_dirs (false)
{
      set_usize_to_display_given_text (entry, 
                               "A long file name and then some", 5, 10);

      entry_label.set_alignment (1.0, 0.5);
      where_label.set_alignment (1.0, 0.5);

      table.set_col_spacings (7);
      table.set_row_spacings (12);
      table.attach (entry_label, 0, 1, 0, 1);
      table.attach (where_label, 0, 1, 1, 2);

      table.attach (entry, 1, 2, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
      table.attach (where_hbox, 1, 2, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);

      set_usize_to_display_given_text (where_browse, _("Hide browser"), 20, 10);
      
      where_hbox.set_spacing (5);
      where_hbox.pack_start (where_combo, true, true);
      where_hbox.pack_start (where_browse, false, false);

      button_box.set_spacing (7);
      set_usize_to_display_given_text (op_button, _(action.c_str()), 20, 15);
      set_usize_to_display_given_text (cancel_button, _("Cancel"), 20, 15);
      button_box.pack_end (op_button, false, false);
      button_box.pack_end (cancel_button, false, false);
      op_button.set_sensitive(false);

      dir_scroller.set_policy (GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
      dir_scroller.add_with_viewport (dir_lists_box);
      dir_scroller.set_usize (-1, 175);

      show_hidden_box.set_spacing (2);
      show_hidden_box.pack_start(show_hidden_check, false, false);
      show_hidden_box.pack_end (add_to_favorites_button, false, false);
      show_hidden_box.pack_end (remove_from_favorites_button, false, false);

      path_label_box.pack_start(path_entry, true, true);

      set_usize_to_display_given_text (new_dir_entry, 
                                     "A long dir name basically ok what", 5, 10);

      expansion_button_box.set_spacing (4);
      expansion_button_box.pack_end (new_dir_button, false, false);
      expansion_button_box.pack_end (new_dir_entry, false, false);

      expansion_vbox.set_spacing (4);
      //expansion_vbox.pack_start (*(manage(new HSeparator)), false, false);
      expansion_vbox.pack_start (show_hidden_box, false, false);
      expansion_vbox.pack_start (path_label_box, false, false);
      expansion_vbox.pack_start (dir_scroller);
      expansion_vbox.pack_start (expansion_button_box, false, false);
      expansion_vbox.pack_start (*(manage(new HSeparator)), false, false);


      where_combo.set_use_arrows_always (true);
      where_combo.get_popwin()->unmap_event.connect (slot (*this, &NewSaveDialog::where_combo_chosen));
      where_combo.get_entry()->changed.connect (slot (*this, &NewSaveDialog::where_combo_changed));

      where_browse.clicked.connect (slot (*this, &NewSaveDialog::toggle_expansion));
      show_hidden_check.toggled.connect (slot (*this, &NewSaveDialog::toggle_hidden));

      new_dir_button.clicked.connect (slot (*this, &NewSaveDialog::new_dir_clicked));
      add_to_favorites_button.clicked.connect (slot (*this, &NewSaveDialog::add_favorites_clicked));
      remove_from_favorites_button.clicked.connect (slot (*this, &NewSaveDialog::remove_favorites_clicked));
      entry.changed.connect (slot (*this, &NewSaveDialog::entry_changed));

      path_entry.changed.connect (slot (*this, &NewSaveDialog::path_entry_changed));
      
      /* initialize the dir lists */
      set_path ("");
}

NewSaveDialog::~NewSaveDialog ()
{
}

string
NewSaveDialog::get_path()
{
      //string dir ((char *) where_combo.get_entry()->get_data ("path"));
      string dir = _fullpath;
      
      dir += '/';
      dir += entry.get_text ();

      return dir;
}

void
NewSaveDialog::set_path (string path, bool force)
{
      char buf[PATH_MAX+1];
      string cwd = path;

      if (cwd == "" ||
          (::access(cwd.c_str(), R_OK) != 0
           && ::access((cwd = PBD::dirname(path)).c_str(), R_OK) != 0)) {
            getcwd (buf, sizeof (buf));
            cwd = buf;
      }

      /* remove all trailing slashes except the first (root)*/
      while (cwd[cwd.length()-1] == '/' && cwd.length() > 1) {
            cwd = cwd.substr(0, cwd.length()-1);
      }
      
      string::size_type pos = 0;
      string::size_type len = cwd.length();
      int nlists = 0;

      if (!force && _fullpath == cwd) {
            return;
      }

      // remove all existing columns
      dir_lists.clear();
      dir_lists_box.children().clear();
      
      ignore_selection = true;
      
      while (pos < len) {

            if (cwd[pos] == '/' || pos == len-1) {

                  CList* clist;

                  clist = manage (new CList (1));
                  clist->set_data_full ("path", strdup (cwd.substr (0, pos+1).c_str()), free); 
                  clist->select_row.connect (bind (slot (*this, &NewSaveDialog::row_selected), nlists));
                  clist->set_selection_mode(GTK_SELECTION_SINGLE);
                  
                  ScrolledWindow* swin = manage (new ScrolledWindow);
                  
                  swin->set_policy (GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
                  swin->add (*clist);
                  
                  clist->show();
                  swin->show ();
                  
                  refill_dir_list (*clist, *swin);

                  /* select the one we want from the previous clist */
                  if (nlists) {
                        CList* prev = dir_lists.back();
                        string base = PBD::basename (cwd.substr (0, pos + ((pos == len-1) ? 1: 0 )));
                        for (unsigned int n = 0; n < prev->rows().size(); ++n) {
                              if (prev->row(n)[0].get_text() == base) {
                                    prev->row(n).select ();
                                    prev->row(n).moveto (1.0,0.0);
                                    
                                    break;
                              }
                        }
                  }


                  /* connect now */

                  dir_lists.push_back (clist);

                  if (pos == len-1) {
                        dir_lists_box.pack_start (*swin, true, true, 1);
                  }
                  else {
                        dir_lists_box.pack_start (*swin, false, false, 1);
                  }

                  nlists++;
                  
            }
            pos++;
      }

      if (nlists) {
            CList* prev = dir_lists.back();
            /* drop terminal slash */
            string base = PBD::basename (cwd.substr (0, cwd.length()));
            
            for (unsigned int n = 0; n < prev->rows().size(); ++n) {
                  if (prev->row(n)[0].get_text() == base) {
                        prev->row(n).select ();
                        prev->row(n).moveto (1.0,0.0);
                        break;
                  }
            }
      }

      ignore_selection = false;

      /* scroll all the way right */
      
      dir_scroller.get_hadjustment()->set_value 
            (dir_scroller.get_hadjustment()->get_upper());

      _fullpath = cwd;

      string base = PBD::basename (path);
      struct stat statbuf;

      if (stat (path.c_str(), &statbuf) == 0) {
            if (!S_ISDIR (statbuf.st_mode)) {
                  entry.set_text (base);
            }
      } else {
            if (errno == ENOENT) {
                  /* new path */
                  entry.set_text (base);
            }
      }

      ignore_path_entry = true;
      ignore_where_combo = true;

      where_combo.get_entry()->set_text (_fullpath);
      where_combo.get_entry()->set_data_full ("path", strdup (_fullpath.c_str()), free);
      path_entry.set_text (_fullpath);

      ignore_path_entry = false;
      ignore_where_combo = false;
}

void
NewSaveDialog::toggle_expansion ()
{
      if (expansion_vbox.is_visible()) {
            expansion_vbox.hide_all ();
            static_cast<Label*>(where_browse.get_child())->set_text (_("Browse ..."));
            Expanded (false);
      } else {
            expansion_vbox.show_all ();
            static_cast<Label*>(where_browse.get_child())->set_text (_("Hide browser"));
            Expanded (true);
      }
}

void
NewSaveDialog::hidden_dirs_shown(bool val)
{
      if (val != show_hidden_dirs) {
            show_hidden_dirs = val;

            set_path (_fullpath, true);
      }
}

void
NewSaveDialog::toggle_hidden ()
{
      hidden_dirs_shown (show_hidden_check.get_active());
}

void
NewSaveDialog::refill_dir_list (CList& clist, Widget& parent)
{
      DIR *dir;
      struct dirent *dentry;
      const char *path;
      string longest;
      vector<string> dirs;

      path = (const char *) clist.get_data ("path");
      if ((dir = opendir (path)) == 0) {
            error << "cannot open directory " 
                  << path
                  << endmsg;
            return;
      }
      
      clist.clear ();
       
      while ((dentry = readdir (dir)) != 0) {

            if (strcmp(dentry->d_name, ".")==0 || strcmp(dentry->d_name, "..")==0 ) {
                  continue;
            }

            if (!show_hidden_dirs && dentry->d_name[0] == '.') {
                  /* hidden */
                  continue;
            }
            
            struct stat statbuf;
            string fullpath;

            fullpath = path;
            if (fullpath != "/") {
                  fullpath += '/';
            }
            
            fullpath += dentry->d_name;

            if (stat (fullpath.c_str(), &statbuf)) {
                  continue;
            }
            
            if (!S_ISDIR(statbuf.st_mode)) {
                  continue;
            }

            if (strlen (dentry->d_name) > longest.length()) {
                  longest = dentry->d_name;
            }
            
            dirs.push_back (fullpath);
      }

      sort (dirs.begin(), dirs.end());

      for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {

            const gchar *rowdata[1];
            rowdata[0] = PBD::basename ((*i).c_str());
            int row = clist.append_row (rowdata);
            clist.set_row_data_full (row, strdup ((*i).c_str()), free);
      }

      /* really should get string display size, then use set_optimal_column_width,
         but this is going to change to TreeView in GTK+2 so don't bother.
      */

      set_usize_to_display_given_text (parent, longest.c_str(), 40, 100);
}

void
NewSaveDialog::row_selected (gint row, gint col, GdkEvent* ev, int which_dirlist)
{
      if (ignore_selection) {
            return;
      }

      CList* clist;
      string path ((char *) dir_lists[which_dirlist]->get_data ("path"));
      string rowtext = dir_lists[which_dirlist]->row(row)[0].get_text ();

      if (path.empty() || path[path.size()-1] != '/') { 
            path += '/';
      }
      
      path += rowtext;
      
      if ((vector<CList*>::size_type) which_dirlist == dir_lists.size() - 1) {
            
            /* last pane, row selected, so add a new pane */

            clist = manage (new CList (1));
            clist->select_row.connect (bind (slot (*this, &NewSaveDialog::row_selected), which_dirlist+1));
            clist->set_data_full ("path", strdup (path.c_str()), free); 
            clist->set_selection_mode(GTK_SELECTION_SINGLE);

            ScrolledWindow* swin = manage (new ScrolledWindow);

            swin->set_policy (GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
            swin->add (*clist);

            clist->show();
            swin->show ();

            dir_lists.push_back (clist);
            dir_lists_box.pack_start (*swin, true, true, 1);

      } else {

            /* not the last pane, remove all but the selected
               one, and add a new last one
            */

            while ((int) dir_lists.size() > which_dirlist + 1) {

                  clist = dir_lists.back();
                  dir_lists.pop_back ();
                  dir_lists_box.remove (*(clist->get_parent ()));
            }

            
            clist = manage (new CList (1));
            clist->select_row.connect (bind (slot (*this, &NewSaveDialog::row_selected), which_dirlist+1));
            clist->set_data_full ("path", strdup (path.c_str()), free); 
            clist->set_selection_mode(GTK_SELECTION_SINGLE);

            ScrolledWindow* swin = manage (new ScrolledWindow);

            swin->set_policy (GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
            swin->add (*clist);

            clist->show();
            swin->show ();

            dir_lists.push_back (clist);
            dir_lists_box.pack_start (*swin, true, true, 1);
      }

      //where_combo.get_entry()->set_text (rowtext);
      where_combo.get_entry()->set_text (path);
      where_combo.get_entry()->set_data_full ("path", strdup (path.c_str()), free);
      _fullpath = path;

      ignore_path_entry = true;
      path_entry.set_text (_fullpath);
      ignore_path_entry = false;

      /* refill the last one */

      refill_dir_list (*(dir_lists.back()), *(dir_lists.back()->get_parent()));

      if (dir_lists[which_dirlist+1]->rows().size() == 0) {
            clist = dir_lists.back ();
            dir_lists.pop_back ();
            dir_lists_box.remove (*(clist->get_parent ()));
      }

      /* scroll all the way right */
      
      dir_scroller.get_hadjustment()->set_value 
            (dir_scroller.get_hadjustment()->get_upper());

}

void
NewSaveDialog::new_dir_clicked ()
{
      /* create new directory in current path */

      if (::access (_fullpath.c_str(), R_OK|W_OK) == 0) {
            string path = _fullpath;
            path += '/';

            /* popup and get new folder name */
            string newdir = new_dir_entry.get_text();

            if (!newdir.empty()) {
                  path += newdir;
                  if (::mkdir (path.c_str(), 0777)) {
                        error << "couldn't create new dir: " <<  path << endmsg;
                  }

                  /* set fullpath */
                  set_path (path);

                  new_dir_entry.set_text("");
            }
      }
}

void
NewSaveDialog::entry_changed ()
{
      if (entry.get_text_length()) {
            op_button.set_sensitive(true);
      } else {
            op_button.set_sensitive(false);
      }
}

void
NewSaveDialog::path_entry_changed ()
{
      struct stat st;

      if (ignore_path_entry) return;

      string path = path_entry.get_text();

      if (stat (path.c_str(), &st) == 0) {
            if (S_ISDIR(st.st_mode)) {
                  set_path (path);
            } 
      }
}


void
NewSaveDialog::add_favorites_clicked ()
{
      /* add current fullpath to favorites */

      if (find (favorite_dirs.begin(), favorite_dirs.end(), _fullpath) == favorite_dirs.end())
      {
            favorite_dirs.push_back (_fullpath);

            /* update combo */
            update_fav_combo ();
      }
}

void
NewSaveDialog::remove_favorites_clicked ()
{
      /* remove current fullpath to favorites */

      FavoriteDirs::iterator found = find (favorite_dirs.begin(), favorite_dirs.end(), _fullpath);
      
      if (found != favorite_dirs.end())
      {
            favorite_dirs.erase (found);

            /* update combo */
            update_fav_combo ();
      }
}

void
NewSaveDialog::set_favorites (vector<string> favs)
{
      favorite_dirs.clear();
      favorite_dirs.insert (favorite_dirs.begin(), favs.begin(), favs.end());

      /* TODO: go through and strip trailing slashes */
      
      /* update combo */
      update_fav_combo ();
}

void
NewSaveDialog::get_favorites (vector<string> & favs)
{
      favs.clear();
      favs.insert (favs.begin(), favorite_dirs.begin(), favorite_dirs.end());
}

void
NewSaveDialog::update_fav_combo ()
{
      FavoriteDirs bases;

      for (FavoriteDirs::iterator fav = favorite_dirs.begin(); fav != favorite_dirs.end(); ++fav) {
            bases.push_back (*fav);
      }
      
      ignore_path_entry = true;
      where_combo.set_popdown_strings (bases);
      ignore_path_entry = false;

      ignore_where_combo = true;
      where_combo.get_entry()->set_text (_fullpath);
      ignore_where_combo = false;
}

gint
NewSaveDialog::where_combo_chosen (GdkEventAny *ev)
{
      if (ignore_where_combo) return FALSE;
      
      string txt = where_combo.get_entry()->get_text();

      path_entry.set_text (txt);
      
      return TRUE;
}

void
NewSaveDialog::where_combo_changed ()
{
      if (ignore_where_combo) return;
      
      string txt = where_combo.get_entry()->get_text();

      path_entry.set_text (txt);
}


Generated by  Doxygen 1.6.0   Back to index