gtkfilesel.c 91.4 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6
7
8
9
10
11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
16
17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19
20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22
23
24
25
26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

Tor Lillqvist's avatar
Tor Lillqvist committed
27
28
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
29
30
31
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
32
#ifdef HAVE_SYS_PARAM_H
Elliot Lee's avatar
Elliot Lee committed
33
#include <sys/param.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
34
35
#endif
#ifdef HAVE_DIRENT_H
Elliot Lee's avatar
Elliot Lee committed
36
#include <dirent.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
37
#endif
Elliot Lee's avatar
Elliot Lee committed
38
#include <stdlib.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
39
#ifdef HAVE_UNISTD_H
Elliot Lee's avatar
Elliot Lee committed
40
#include <unistd.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
41
#endif
Elliot Lee's avatar
Elliot Lee committed
42
43
#include <string.h>
#include <errno.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
44
#ifdef HAVE_PWD_H
Elliot Lee's avatar
Elliot Lee committed
45
#include <pwd.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
46
#endif
Tor Lillqvist's avatar
Tor Lillqvist committed
47
48
49
#ifdef HAVE_WINSOCK_H
#include <winsock.h>		/* For gethostname */
#endif
Tor Lillqvist's avatar
Tor Lillqvist committed
50

Elliot Lee's avatar
Elliot Lee committed
51
52
53
54
55
56
57
#include "fnmatch.h"

#include "gdk/gdkkeysyms.h"
#include "gtkbutton.h"
#include "gtkentry.h"
#include "gtkfilesel.h"
#include "gtkhbox.h"
58
#include "gtkhbbox.h"
Elliot Lee's avatar
Elliot Lee committed
59
60
61
62
63
#include "gtklabel.h"
#include "gtklist.h"
#include "gtklistitem.h"
#include "gtkmain.h"
#include "gtkscrolledwindow.h"
64
#include "gtkstock.h"
Elliot Lee's avatar
Elliot Lee committed
65
66
#include "gtksignal.h"
#include "gtkvbox.h"
67
68
69
70
71
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtkoptionmenu.h"
#include "gtkclist.h"
#include "gtkdialog.h"
72
#include "gtkmessagedialog.h"
Owen Taylor's avatar
Owen Taylor committed
73
#include "gtkintl.h"
74
75
#include "gtkdnd.h"
#include "gtkeventbox.h"
Elliot Lee's avatar
Elliot Lee committed
76

Manish Singh's avatar
Manish Singh committed
77
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
78
79
80
#define STRICT
#include <windows.h>

Manish Singh's avatar
Manish Singh committed
81
#ifdef G_OS_WIN32
82
#include <direct.h>
83
84
#include <io.h>
#define mkdir(p,m) _mkdir(p)
85
86
87
#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode)&_S_IFDIR)
#endif
88

Manish Singh's avatar
Manish Singh committed
89
#endif /* G_OS_WIN32 */
90

Manish Singh's avatar
Manish Singh committed
91
#endif /* G_OS_WIN32 || G_WITH_CYGWIN */
92

93
94
95
96
#define DIR_LIST_WIDTH   180
#define DIR_LIST_HEIGHT  180
#define FILE_LIST_WIDTH  180
#define FILE_LIST_HEIGHT 180
Elliot Lee's avatar
Elliot Lee committed
97

98
99
100
101
102
103
104
105
106
107
108
109
/* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
 * in here, since the rest of the code in the file does require some
 * fixed maximum.
 */
#ifndef MAXPATHLEN
#  ifdef PATH_MAX
#    define MAXPATHLEN PATH_MAX
#  else
#    define MAXPATHLEN 2048
#  endif
#endif

110
111
112
113
114
115
116
117
118
119
/* I've put this here so it doesn't get confused with the 
 * file completion interface */
typedef struct _HistoryCallbackArg HistoryCallbackArg;

struct _HistoryCallbackArg
{
  gchar *directory;
  GtkWidget *menu_item;
};

Elliot Lee's avatar
Elliot Lee committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

typedef struct _CompletionState    CompletionState;
typedef struct _CompletionDir      CompletionDir;
typedef struct _CompletionDirSent  CompletionDirSent;
typedef struct _CompletionDirEntry CompletionDirEntry;
typedef struct _CompletionUserDir  CompletionUserDir;
typedef struct _PossibleCompletion PossibleCompletion;

/* Non-external file completion decls and structures */

/* A contant telling PRCS how many directories to cache.  Its actually
 * kept in a list, so the geometry isn't important. */
#define CMPL_DIRECTORY_CACHE_SIZE 10

/* A constant used to determine whether a substring was an exact
 * match by first_diff_index()
 */
#define PATTERN_MATCH -1
/* The arguments used by all fnmatch() calls below
 */
#define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)

#define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
143
#define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
Elliot Lee's avatar
Elliot Lee committed
144
145
146
147
148
149
150
151
152

/* This structure contains all the useful information about a directory
 * for the purposes of filename completion.  These structures are cached
 * in the CompletionState struct.  CompletionDir's are reference counted.
 */
struct _CompletionDirSent
{
  ino_t inode;
  time_t mtime;
153
  dev_t device;
Elliot Lee's avatar
Elliot Lee committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

  gint entry_count;
  struct _CompletionDirEntry *entries;
};

struct _CompletionDir
{
  CompletionDirSent *sent;

  gchar *fullname;
  gint fullname_len;

  struct _CompletionDir *cmpl_parent;
  gint cmpl_index;
  gchar *cmpl_text;
};

/* This structure contains pairs of directory entry names with a flag saying
 * whether or not they are a valid directory.  NOTE: This information is used
 * to provide the caller with information about whether to update its completions
 * or try to open a file.  Since directories are cached by the directory mtime,
 * a symlink which points to an invalid file (which will not be a directory),
 * will not be reevaluated if that file is created, unless the containing
 * directory is touched.  I consider this case to be worth ignoring (josh).
 */
struct _CompletionDirEntry
{
181
  gboolean is_dir;
Elliot Lee's avatar
Elliot Lee committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  gchar *entry_name;
};

struct _CompletionUserDir
{
  gchar *login;
  gchar *homedir;
};

struct _PossibleCompletion
{
  /* accessible fields, all are accessed externally by functions
   * declared above
   */
  gchar *text;
  gint is_a_completion;
198
  gboolean is_directory;
Elliot Lee's avatar
Elliot Lee committed
199
200
201
202
203
204
205
206
207
208
209
210

  /* Private fields
   */
  gint text_alloc;
};

struct _CompletionState
{
  gint last_valid_char;
  gchar *updated_text;
  gint updated_text_len;
  gint updated_text_alloc;
211
  gboolean re_complete;
Elliot Lee's avatar
Elliot Lee committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

  gchar *user_dir_name_buffer;
  gint user_directories_len;

  gchar *last_completion_text;

  gint user_completion_index; /* if >= 0, currently completing ~user */

  struct _CompletionDir *completion_dir; /* directory completing from */
  struct _CompletionDir *active_completion_dir;

  struct _PossibleCompletion the_completion;

  struct _CompletionDir *reference_dir; /* initial directory */

  GList* directory_storage;
  GList* directory_sent_storage;

  struct _CompletionUserDir *user_directories;
};

233
234
235
236
237
enum {
  PROP_0,
  PROP_SHOW_FILEOPS,
  PROP_FILENAME
};
Elliot Lee's avatar
Elliot Lee committed
238
239
240
241
242
243
244
245

/* File completion functions which would be external, were they used
 * outside of this file.
 */

static CompletionState*    cmpl_init_state        (void);
static void                cmpl_free_state        (CompletionState *cmpl_state);
static gint                cmpl_state_okay        (CompletionState* cmpl_state);
Owen Taylor's avatar
Owen Taylor committed
246
static const gchar*        cmpl_strerror          (gint);
Elliot Lee's avatar
Elliot Lee committed
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
						   gchar          **remaining_text,
						   CompletionState *cmpl_state);

/* Returns a name for consideration, possibly a completion, this name
 * will be invalid after the next call to cmpl_next_completion.
 */
static char*               cmpl_this_completion   (PossibleCompletion*);

/* True if this completion matches the given text.  Otherwise, this
 * output can be used to have a list of non-completions.
 */
static gint                cmpl_is_a_completion   (PossibleCompletion*);

/* True if the completion is a directory
 */
264
static gboolean            cmpl_is_directory      (PossibleCompletion*);
Elliot Lee's avatar
Elliot Lee committed
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

/* Obtains the next completion, or NULL
 */
static PossibleCompletion* cmpl_next_completion   (CompletionState*);

/* Updating completions: the return value of cmpl_updated_text() will
 * be text_to_complete completed as much as possible after the most
 * recent call to cmpl_completion_matches.  For the present
 * application, this is the suggested replacement for the user's input
 * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
 * been received.
 */
static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);

/* After updating, to see if the completion was a directory, call
 * this.  If it was, you should consider re-calling completion_matches.
 */
282
static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
Elliot Lee's avatar
Elliot Lee committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298

/* Current location: if using file completion, return the current
 * directory, from which file completion begins.  More specifically,
 * the cwd concatenated with all exact completions up to the last
 * directory delimiter('/').
 */
static gchar*              cmpl_reference_position (CompletionState* cmpl_state);

/* backing up: if cmpl_completion_matches returns NULL, you may query
 * the index of the last completable character into cmpl_updated_text.
 */
static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);

/* When the user selects a non-directory, call cmpl_completion_fullname
 * to get the full name of the selected file.
 */
Owen Taylor's avatar
Owen Taylor committed
299
static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
Elliot Lee's avatar
Elliot Lee committed
300
301
302
303
304
305


/* Directory operations. */
static CompletionDir* open_ref_dir         (gchar* text_to_complete,
					    gchar** remaining_text,
					    CompletionState* cmpl_state);
Manish Singh's avatar
Manish Singh committed
306
#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
307
308
309
static gboolean       check_dir            (gchar *dir_name, 
					    struct stat *result, 
					    gboolean *stat_subdirs);
Tor Lillqvist's avatar
Tor Lillqvist committed
310
#endif
Elliot Lee's avatar
Elliot Lee committed
311
312
static CompletionDir* open_dir             (gchar* dir_name,
					    CompletionState* cmpl_state);
Tor Lillqvist's avatar
Tor Lillqvist committed
313
#ifdef HAVE_PWD_H
Owen Taylor's avatar
Owen Taylor committed
314
static CompletionDir* open_user_dir        (const gchar* text_to_complete,
Elliot Lee's avatar
Elliot Lee committed
315
					    CompletionState *cmpl_state);
Tor Lillqvist's avatar
Tor Lillqvist committed
316
#endif
Elliot Lee's avatar
Elliot Lee committed
317
318
static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
					    CompletionState *cmpl_state);
319
320
321
static CompletionDirSent* open_new_dir     (gchar* dir_name, 
					    struct stat* sbuf,
					    gboolean stat_subdirs);
Elliot Lee's avatar
Elliot Lee committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
static gint           correct_parent       (CompletionDir* cmpl_dir,
					    struct stat *sbuf);
static gchar*         find_parent_dir_fullname    (gchar* dirname);
static CompletionDir* attach_dir           (CompletionDirSent* sent,
					    gchar* dir_name,
					    CompletionState *cmpl_state);
static void           free_dir_sent (CompletionDirSent* sent);
static void           free_dir      (CompletionDir  *dir);
static void           prune_memory_usage(CompletionState *cmpl_state);

/* Completion operations */
static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
						      CompletionState *cmpl_state);
static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
static CompletionDir* find_completion_dir(gchar* text_to_complete,
					  gchar** remaining_text,
					  CompletionState* cmpl_state);
static PossibleCompletion* append_completion_text(gchar* text,
						  CompletionState* cmpl_state);
Tor Lillqvist's avatar
Tor Lillqvist committed
342
#ifdef HAVE_PWD_H
Elliot Lee's avatar
Elliot Lee committed
343
344
static gint get_pwdb(CompletionState* cmpl_state);
static gint compare_user_dir(const void* a, const void* b);
Tor Lillqvist's avatar
Tor Lillqvist committed
345
346
#endif
static gint first_diff_index(gchar* pat, gchar* text);
Elliot Lee's avatar
Elliot Lee committed
347
348
349
350
351
static gint compare_cmpl_dir(const void* a, const void* b);
static void update_cmpl(PossibleCompletion* poss,
			CompletionState* cmpl_state);

static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
352
353
354
355
356
357
358
359
static void gtk_file_selection_set_property  (GObject         *object,
					      guint            prop_id,
					      const GValue    *value,
					      GParamSpec      *pspec);
static void gtk_file_selection_get_property  (GObject         *object,
					      guint            prop_id,
					      GValue          *value,
					      GParamSpec      *pspec);
Elliot Lee's avatar
Elliot Lee committed
360
static void gtk_file_selection_init          (GtkFileSelection      *filesel);
Owen Taylor's avatar
Owen Taylor committed
361
static void gtk_file_selection_finalize      (GObject               *object);
Elliot Lee's avatar
Elliot Lee committed
362
363
364
365
static void gtk_file_selection_destroy       (GtkObject             *object);
static gint gtk_file_selection_key_press     (GtkWidget             *widget,
					      GdkEventKey           *event,
					      gpointer               user_data);
366
367
368
369
370
static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
					      const gchar           *new_text,
					      gint                   new_text_length,
					      gint                  *position,
					      gpointer               user_data);
371
372
373
374

static void gtk_file_selection_file_button (GtkWidget *widget,
					    gint row, 
					    gint column, 
375
					    GdkEventButton *bevent,
376
377
378
379
380
					    gpointer user_data);

static void gtk_file_selection_dir_button (GtkWidget *widget,
					   gint row, 
					   gint column, 
381
					   GdkEventButton *bevent,
382
383
					   gpointer data);

Elliot Lee's avatar
Elliot Lee committed
384
385
386
387
static void gtk_file_selection_populate      (GtkFileSelection      *fs,
					      gchar                 *rel_path,
					      gint                   try_complete);
static void gtk_file_selection_abort         (GtkFileSelection      *fs);
388
389
390
391
392
393
394

static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
						    gchar                  *current_dir);

static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
Elliot Lee's avatar
Elliot Lee committed
395
396
397



398
static GtkWindowClass *parent_class = NULL;
Elliot Lee's avatar
Elliot Lee committed
399
400
401
402

/* Saves errno when something cmpl does fails. */
static gint cmpl_errno;

Manish Singh's avatar
Manish Singh committed
403
#ifdef G_WITH_CYGWIN
Tor Lillqvist's avatar
Tor Lillqvist committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/*
 * Take the path currently in the file selection
 * entry field and translate as necessary from
 * a WIN32 style to CYGWIN32 style path.  For
 * instance translate:
 * x:\somepath\file.jpg
 * to:
 * //x/somepath/file.jpg
 *
 * Replace the path in the selection text field.
 * Return a boolean value concerning whether a
 * translation had to be made.
 */
int
418
translate_win32_path (GtkFileSelection *filesel)
Tor Lillqvist's avatar
Tor Lillqvist committed
419
420
421
422
423
424
425
426
427
428
429
430
431
{
  int updated = 0;
  gchar *path;

  /*
   * Retrieve the current path
   */
  path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));

  /*
   * Translate only if this looks like a DOS-ish
   * path... First handle any drive letters.
   */
432
  if (isalpha (path[0]) && (path[1] == ':')) {
Tor Lillqvist's avatar
Tor Lillqvist committed
433
434
435
436
437
438
439
440
    /*
     * This part kind of stinks... It isn't possible
     * to know if there is enough space in the current
     * string for the extra character required in this
     * conversion.  Assume that there isn't enough space
     * and use the set function on the text field to
     * set the newly created string.
     */
441
    gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
Tor Lillqvist's avatar
Tor Lillqvist committed
442
443
444
445
446
447
448
449
450
451
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);

    path = newPath;
    updated = 1;
  }

  /*
   * Now, replace backslashes with forward slashes 
   * if necessary.
   */
452
453
454
455
456
457
458
459
460
  if (strchr (path, '\\'))
    {
      int index;
      for (index = 0; path[index] != '\0'; index++)
	if (path[index] == '\\')
	  path[index] = '/';
      
      updated = 1;
    }
Tor Lillqvist's avatar
Tor Lillqvist committed
461
462
463
464
465
    
  return updated;
}
#endif

466
GtkType
467
gtk_file_selection_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
468
{
469
  static GtkType file_selection_type = 0;
Elliot Lee's avatar
Elliot Lee committed
470
471
472

  if (!file_selection_type)
    {
473
      static const GtkTypeInfo filesel_info =
Elliot Lee's avatar
Elliot Lee committed
474
475
476
477
478
479
      {
	"GtkFileSelection",
	sizeof (GtkFileSelection),
	sizeof (GtkFileSelectionClass),
	(GtkClassInitFunc) gtk_file_selection_class_init,
	(GtkObjectInitFunc) gtk_file_selection_init,
480
481
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
482
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
483
484
      };

485
      file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
Elliot Lee's avatar
Elliot Lee committed
486
487
488
489
490
491
492
493
    }

  return file_selection_type;
}

static void
gtk_file_selection_class_init (GtkFileSelectionClass *class)
{
494
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
495
496
  GtkObjectClass *object_class;

497
  gobject_class = (GObjectClass*) class;
Elliot Lee's avatar
Elliot Lee committed
498
499
  object_class = (GtkObjectClass*) class;

500
  parent_class = gtk_type_class (GTK_TYPE_DIALOG);
Elliot Lee's avatar
Elliot Lee committed
501

Owen Taylor's avatar
Owen Taylor committed
502
  gobject_class->finalize = gtk_file_selection_finalize;
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  gobject_class->set_property = gtk_file_selection_set_property;
  gobject_class->get_property = gtk_file_selection_get_property;
   
  g_object_class_install_property (gobject_class,
                                   PROP_FILENAME,
                                   g_param_spec_string ("filename",
                                                        _("Filename"),
                                                        _("The currently selected filename."),
                                                        NULL,
                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
				   PROP_SHOW_FILEOPS,
				   g_param_spec_boolean ("show_fileops",
							 _("Show file operations"),
							 _("Whether buttons for creating/manipulating files should be displayed."),
							 FALSE,
							 G_PARAM_READABLE |
							 G_PARAM_WRITABLE));
Elliot Lee's avatar
Elliot Lee committed
521
522
523
  object_class->destroy = gtk_file_selection_destroy;
}

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
static void gtk_file_selection_set_property (GObject         *object,
					     guint            prop_id,
					     const GValue    *value,
					     GParamSpec      *pspec)
{
  GtkFileSelection *filesel;

  filesel = GTK_FILE_SELECTION (object);

  switch (prop_id)
    {
    case PROP_FILENAME:
      gtk_file_selection_set_filename (filesel,
                                       g_value_get_string (value));
      break;
      
    case PROP_SHOW_FILEOPS:
      if (g_value_get_boolean (value))
	 gtk_file_selection_show_fileop_buttons (filesel);
      else
	 gtk_file_selection_hide_fileop_buttons (filesel);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void gtk_file_selection_get_property (GObject         *object,
					     guint            prop_id,
					     GValue          *value,
					     GParamSpec      *pspec)
{
  GtkFileSelection *filesel;

  filesel = GTK_FILE_SELECTION (object);

  switch (prop_id)
    {
    case PROP_FILENAME:
      g_value_set_string (value,
                          gtk_file_selection_get_filename(filesel));
      break;

    case PROP_SHOW_FILEOPS:
      /* This is a little bit hacky, but doing otherwise would require
       * adding a field to the object.
       */
      g_value_set_boolean (value, (filesel->fileop_c_dir && 
				   filesel->fileop_del_file &&
				   filesel->fileop_ren_file));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

582
583
584
585
586
587
588
static gboolean
grab_default (GtkWidget *widget)
{
  gtk_widget_grab_default (widget);
  return FALSE;
}
     
Elliot Lee's avatar
Elliot Lee committed
589
590
591
592
593
594
static void
gtk_file_selection_init (GtkFileSelection *filesel)
{
  GtkWidget *entry_vbox;
  GtkWidget *label;
  GtkWidget *list_hbox;
595
  GtkWidget *confirm_area;
596
  GtkWidget *pulldown_hbox;
597
  GtkWidget *scrolled_win;
598
  GtkWidget *eventbox;
599
600
  GtkDialog *dialog;
  
Owen Taylor's avatar
Owen Taylor committed
601
602
  char *dir_title [2];
  char *file_title [2];
603
604

  dialog = GTK_DIALOG (filesel);
605

Elliot Lee's avatar
Elliot Lee committed
606
  filesel->cmpl_state = cmpl_init_state ();
607

608
  /* The dialog-sized vertical box  */
609
  filesel->main_vbox = dialog->vbox;
610
  gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
Elliot Lee's avatar
Elliot Lee committed
611

612
  /* The horizontal box containing create, rename etc. buttons */
613
  filesel->button_area = gtk_hbutton_box_new ();
614
  gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
615
  gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
616
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
617
		      FALSE, FALSE, 0);
618
  gtk_widget_show (filesel->button_area);
619
  
620
  gtk_file_selection_show_fileop_buttons (filesel);
621

622
623
624
625
626
627
628
629
630
631
632
  /* hbox for pulldown menu */
  pulldown_hbox = gtk_hbox_new (TRUE, 5);
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
  gtk_widget_show (pulldown_hbox);
  
  /* Pulldown menu */
  filesel->history_pulldown = gtk_option_menu_new ();
  gtk_widget_show (filesel->history_pulldown);
  gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
		      FALSE, FALSE, 0);
    
Elliot Lee's avatar
Elliot Lee committed
633
  /*  The horizontal box containing the directory and file listboxes  */
634
  list_hbox = gtk_hbox_new (FALSE, 5);
Elliot Lee's avatar
Elliot Lee committed
635
636
637
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
  gtk_widget_show (list_hbox);

638
  /* The directories clist */
639
640
  dir_title[0] = _("Directories");
  dir_title[1] = NULL;
641
  filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
642
643
644
645
  gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
  gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
		      (GtkSignalFunc) gtk_file_selection_dir_button, 
		      (gpointer) filesel);
646
  gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
647
  gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
648
649
650
651
652

  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
653
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
654
  gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
Elliot Lee's avatar
Elliot Lee committed
655
  gtk_widget_show (filesel->dir_list);
656
  gtk_widget_show (scrolled_win);
Elliot Lee's avatar
Elliot Lee committed
657

658
  /* The files clist */
Owen Taylor's avatar
Owen Taylor committed
659
660
  file_title[0] = _("Files");
  file_title[1] = NULL;
661
  filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
662
663
664
665
  gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
  gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
		      (GtkSignalFunc) gtk_file_selection_file_button, 
		      (gpointer) filesel);
666
  gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
667
  gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
668
669
670
671
672

  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
673
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
674
  gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
Elliot Lee's avatar
Elliot Lee committed
675
  gtk_widget_show (filesel->file_list);
676
  gtk_widget_show (scrolled_win);
Elliot Lee's avatar
Elliot Lee committed
677

678
679
680
681
682
683
684
  /* action area for packing buttons into. */
  filesel->action_area = gtk_hbox_new (TRUE, 0);
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
		      FALSE, FALSE, 0);
  gtk_widget_show (filesel->action_area);
  
  /*  The OK/Cancel button area */
685
  confirm_area = dialog->action_area;
Elliot Lee's avatar
Elliot Lee committed
686
687

  /*  The OK button  */
688
  filesel->ok_button = gtk_dialog_add_button (dialog,
689
                                              GTK_STOCK_OK,
690
691
                                              GTK_RESPONSE_OK);
  
Elliot Lee's avatar
Elliot Lee committed
692
693
694
  gtk_widget_grab_default (filesel->ok_button);

  /*  The Cancel button  */
695
  filesel->cancel_button = gtk_dialog_add_button (dialog,
696
                                                  GTK_STOCK_CANCEL,
697
                                                  GTK_RESPONSE_CANCEL);
Elliot Lee's avatar
Elliot Lee committed
698
699
700

  /*  The selection entry widget  */
  entry_vbox = gtk_vbox_new (FALSE, 2);
701
  gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
Elliot Lee's avatar
Elliot Lee committed
702
  gtk_widget_show (entry_vbox);
703
704
  
  eventbox = gtk_event_box_new ();
Elliot Lee's avatar
Elliot Lee committed
705
706
  filesel->selection_text = label = gtk_label_new ("");
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
707
708
  gtk_container_add (GTK_CONTAINER (eventbox), label);
  gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
Elliot Lee's avatar
Elliot Lee committed
709
  gtk_widget_show (label);
710
  gtk_widget_show (eventbox);
Elliot Lee's avatar
Elliot Lee committed
711
712
713
714

  filesel->selection_entry = gtk_entry_new ();
  gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
		      (GtkSignalFunc) gtk_file_selection_key_press, filesel);
715
716
  gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
		      (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
Elliot Lee's avatar
Elliot Lee committed
717
  gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
718
			     (GtkSignalFunc) grab_default,
Elliot Lee's avatar
Elliot Lee committed
719
720
721
722
723
724
725
726
727
728
729
			     GTK_OBJECT (filesel->ok_button));
  gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
                             (GtkSignalFunc) gtk_button_clicked,
                             GTK_OBJECT (filesel->ok_button));
  gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
  gtk_widget_show (filesel->selection_entry);

  if (!cmpl_state_okay (filesel->cmpl_state))
    {
      gchar err_buf[256];

Owen Taylor's avatar
Owen Taylor committed
730
      sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
Elliot Lee's avatar
Elliot Lee committed
731

Owen Taylor's avatar
Owen Taylor committed
732
      gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
Elliot Lee's avatar
Elliot Lee committed
733
734
735
736
737
738
739
740
741
    }
  else
    {
      gtk_file_selection_populate (filesel, "", FALSE);
    }

  gtk_widget_grab_focus (filesel->selection_entry);
}

742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
static gchar *
uri_list_extract_first_uri (const gchar* uri_list)
{
  const gchar *p, *q;
  
  g_return_val_if_fail (uri_list != NULL, NULL);
  
  p = uri_list;
  /* We don't actually try to validate the URI according to RFC
   * 2396, or even check for allowed characters - we just ignore
   * comments and trim whitespace off the ends.  We also
   * allow LF delimination as well as the specified CRLF.
   *
   * We do allow comments like specified in RFC 2483.
   */
  while (p)
    {
      if (*p != '#')
	{
	  while (g_ascii_isspace(*p))
	    p++;
	  
	  q = p;
	  while (*q && (*q != '\n') && (*q != '\r'))
	    q++;
	  
	  if (q > p)
	    {
	      q--;
	      while (q > p && g_ascii_isspace (*q))
		q--;

	      if (q > p)
		return g_strndup (p, q - p + 1);
	    }
	}
      p = strchr (p, '\n');
      if (p)
	p++;
    }
  return NULL;
}

static void
dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
{
  gchar *filename;
  
  if (response_id == GTK_RESPONSE_YES)
    {
      filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");

      gtk_file_selection_set_filename (fs, filename);
    }
  
  gtk_widget_destroy (dialog);
}


static void
filenames_dropped (GtkWidget        *widget,
		   GdkDragContext   *context,
		   gint              x,
		   gint              y,
		   GtkSelectionData *selection_data,
		   guint             info,
		   guint             time)
{
  char *uri = NULL;
  char *filename = NULL;
  char *hostname;
  char this_hostname[257];
  int res;
  GError *error = NULL;
	
  if (!selection_data->data)
    return;

  uri = uri_list_extract_first_uri ((char *)selection_data->data);
  
  if (!uri)
    return;

  filename = g_filename_from_uri (uri, &hostname, &error);
  g_free (uri);
  
  if (!filename)
    {
      g_warning ("Error getting dropped filename: %s\n",
		 error->message);
      g_error_free (error);
      return;
    }

  res = gethostname (this_hostname, 256);
  this_hostname[256] = 0;
  
  if ((hostname == NULL) ||
      (res == 0 && strcmp (hostname, this_hostname) == 0) ||
      (strcmp (hostname, "localhost") == 0))
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
				     filename);
  else
    {
      GtkWidget *dialog;
      
      dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
				       GTK_DIALOG_DESTROY_WITH_PARENT,
				       GTK_MESSAGE_QUESTION,
				       GTK_BUTTONS_YES_NO,
				       _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
					 "Are you sure that you want to select it?"), filename, hostname);

      g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
      
      g_signal_connect_data (dialog, "response",
			     (GCallback) dnd_really_drop, 
			     widget, NULL, 0);
      
      gtk_widget_show (dialog);
    }

  g_free (hostname);
  g_free (filename);
}

enum
{
  TARGET_URILIST,
  TARGET_UTF8_STRING,
  TARGET_STRING,
  TARGET_TEXT,
  TARGET_COMPOUND_TEXT
};


static void
filenames_drag_get (GtkWidget        *widget,
		    GdkDragContext   *context,
		    GtkSelectionData *selection_data,
		    guint             info,
		    guint             time,
		    GtkFileSelection *filesel)
{
Owen Taylor's avatar
Owen Taylor committed
886
  const gchar *file;
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  gchar *uri_list;
  char hostname[256];
  int res;
  GError *error;

  file = gtk_file_selection_get_filename (filesel);

  if (file)
    {
      if (info == TARGET_URILIST)
	{
	  res = gethostname (hostname, 256);
	  
	  error = NULL;
	  uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
	  if (!uri_list)
	    {
	      g_warning ("Error getting filename: %s\n",
			 error->message);
	      g_error_free (error);
	      return;
	    }
	  
	  gtk_selection_data_set (selection_data,
				  selection_data->target, 8,
				  (void *)uri_list, strlen((char *)uri_list));
	  g_free (uri_list);
	}
      else
	{
	  g_print ("Setting text: '%s'\n", file);
918
	  gtk_selection_data_set_text (selection_data, file, -1);
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
	}
    }
}

static void
file_selection_setup_dnd (GtkFileSelection *filesel)
{
  GtkWidget *eventbox;
  static GtkTargetEntry drop_types[] = {
    { "text/uri-list", 0, TARGET_URILIST}
  };
  static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
  static GtkTargetEntry drag_types[] = {
    { "text/uri-list", 0, TARGET_URILIST},
    { "UTF8_STRING", 0, TARGET_UTF8_STRING },
    { "STRING", 0, 0 },
    { "TEXT",   0, 0 }, 
    { "COMPOUND_TEXT", 0, 0 }
  };
  static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);

  gtk_drag_dest_set (GTK_WIDGET (filesel),
		     GTK_DEST_DEFAULT_ALL,
		     drop_types, n_drop_types,
		     GDK_ACTION_COPY);

  gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
		      GTK_SIGNAL_FUNC(filenames_dropped), NULL);

  eventbox = gtk_widget_get_parent (filesel->selection_text);
  gtk_drag_source_set (eventbox,
		       GDK_BUTTON1_MASK,
		       drag_types, n_drag_types,
		       GDK_ACTION_COPY);

  gtk_signal_connect (GTK_OBJECT (eventbox),
		      "drag_data_get",
		      GTK_SIGNAL_FUNC (filenames_drag_get),
		      filesel);
}

Elliot Lee's avatar
Elliot Lee committed
960
961
962
963
964
GtkWidget*
gtk_file_selection_new (const gchar *title)
{
  GtkFileSelection *filesel;

965
  filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
Elliot Lee's avatar
Elliot Lee committed
966
  gtk_window_set_title (GTK_WINDOW (filesel), title);
967
  gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
Elliot Lee's avatar
Elliot Lee committed
968

969
970
  file_selection_setup_dnd (filesel);
  
Elliot Lee's avatar
Elliot Lee committed
971
972
973
  return GTK_WIDGET (filesel);
}

974
975
976
977
978
979
980
981
void
gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
{
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
    
  /* delete, create directory, and rename */
  if (!filesel->fileop_c_dir) 
    {
Owen Taylor's avatar
Owen Taylor committed
982
      filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
983
984
985
986
987
988
989
990
991
992
      gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
			  (GtkSignalFunc) gtk_file_selection_create_dir, 
			  (gpointer) filesel);
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
			  filesel->fileop_c_dir, TRUE, TRUE, 0);
      gtk_widget_show (filesel->fileop_c_dir);
    }
	
  if (!filesel->fileop_del_file) 
    {
Owen Taylor's avatar
Owen Taylor committed
993
      filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
994
995
996
997
998
999
1000
1001
1002
1003
      gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
			  (GtkSignalFunc) gtk_file_selection_delete_file, 
			  (gpointer) filesel);
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
			  filesel->fileop_del_file, TRUE, TRUE, 0);
      gtk_widget_show (filesel->fileop_del_file);
    }

  if (!filesel->fileop_ren_file)
    {
Owen Taylor's avatar
Owen Taylor committed
1004
      filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
1005
1006
1007
1008
1009
1010
1011
      gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
			  (GtkSignalFunc) gtk_file_selection_rename_file, 
			  (gpointer) filesel);
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
			  filesel->fileop_ren_file, TRUE, TRUE, 0);
      gtk_widget_show (filesel->fileop_ren_file);
    }
1012
  g_object_notify (G_OBJECT (filesel), "show_fileops");
1013
  gtk_widget_queue_resize (GTK_WIDGET (filesel));
1014
1015
1016
1017
1018
1019
1020
}

void       
gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
{
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
    
1021
1022
1023
1024
1025
  if (filesel->fileop_ren_file)
    {
      gtk_widget_destroy (filesel->fileop_ren_file);
      filesel->fileop_ren_file = NULL;
    }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037

  if (filesel->fileop_del_file)
    {
      gtk_widget_destroy (filesel->fileop_del_file);
      filesel->fileop_del_file = NULL;
    }

  if (filesel->fileop_c_dir)
    {
      gtk_widget_destroy (filesel->fileop_c_dir);
      filesel->fileop_c_dir = NULL;
    }
1038
  g_object_notify (G_OBJECT (filesel), "show_fileops");
1039
1040
1041
1042
}



Elliot Lee's avatar
Elliot Lee committed
1043
1044
1045
1046
void
gtk_file_selection_set_filename (GtkFileSelection *filesel,
				 const gchar      *filename)
{
1047
  gchar *buf;
Elliot Lee's avatar
Elliot Lee committed
1048
1049
1050
1051
1052
  const char *name, *last_slash;

  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
  g_return_if_fail (filename != NULL);

Tor Lillqvist's avatar
Tor Lillqvist committed
1053
  last_slash = strrchr (filename, G_DIR_SEPARATOR);
Elliot Lee's avatar
Elliot Lee committed
1054
1055
1056

  if (!last_slash)
    {
1057
      buf = g_strdup ("");
Elliot Lee's avatar
Elliot Lee committed
1058
1059
1060
1061
      name = filename;
    }
  else
    {
1062
1063
      buf = g_strdup (filename);
      buf[last_slash - filename + 1] = 0;
Elliot Lee's avatar
Elliot Lee committed
1064
1065
1066
1067
1068
1069
1070
      name = last_slash + 1;
    }

  gtk_file_selection_populate (filesel, buf, FALSE);

  if (filesel->selection_entry)
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1071
  g_free (buf);
1072
  g_object_notify (G_OBJECT (filesel), "filename");
Elliot Lee's avatar
Elliot Lee committed
1073
1074
}

1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
/**
 * gtk_file_selection_get_filename:
 * @filesel: a #GtkFileSelection
 * 
 * This function returns the selected filename in the C runtime's
 * multibyte string encoding, which may or may not be the same as that
 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
 * The returned string points to a statically allocated buffer and
 * should be copied if you plan to keep it around.
 * 
 * Return value: currently-selected filename in locale's encoding
 **/
G_CONST_RETURN gchar*
Elliot Lee's avatar
Elliot Lee committed
1088
1089
gtk_file_selection_get_filename (GtkFileSelection *filesel)
{
1090
1091
  static gchar nothing[2] = "";
  static gchar something[MAXPATHLEN*2];
1092
  char *sys_filename;
Owen Taylor's avatar
Owen Taylor committed
1093
  const char *text;
Elliot Lee's avatar
Elliot Lee committed
1094
1095
1096

  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);

Manish Singh's avatar
Manish Singh committed
1097
#ifdef G_WITH_CYGWIN
1098
  translate_win32_path (filesel);
Tor Lillqvist's avatar
Tor Lillqvist committed
1099
#endif
Elliot Lee's avatar
Elliot Lee committed
1100
1101
1102
  text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
  if (text)
    {
1103
      sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1104
      if (!sys_filename)
1105
	return nothing;
1106
1107
      strncpy (something, sys_filename, sizeof (something));
      g_free (sys_filename);
1108
      return something;
Elliot Lee's avatar
Elliot Lee committed
1109
1110
1111
1112
1113
    }

  return nothing;
}

1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
void
gtk_file_selection_complete (GtkFileSelection *filesel,
			     const gchar      *pattern)
{
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
  g_return_if_fail (pattern != NULL);

  if (filesel->selection_entry)
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
  gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
}

Elliot Lee's avatar
Elliot Lee committed
1126
1127
1128
1129
static void
gtk_file_selection_destroy (GtkObject *object)
{
  GtkFileSelection *filesel;
1130
1131
  GList *list;
  HistoryCallbackArg *callback_arg;
1132
  
Elliot Lee's avatar
Elliot Lee committed
1133
  g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1134
  
Elliot Lee's avatar
Elliot Lee committed
1135
  filesel = GTK_FILE_SELECTION (object);
1136
1137
  
  if (filesel->fileop_dialog)
1138
1139
1140
1141
    {
      gtk_widget_destroy (filesel->fileop_dialog);
      filesel->fileop_dialog = NULL;
    }
1142
  
1143
1144
1145
1146
1147
1148
1149
  if (filesel->history_list)
    {
      list = filesel->history_list;
      while (list)
	{
	  callback_arg = list->data;
	  g_free (callback_arg->directory);
1150
	  g_free (callback_arg);
1151
1152
1153
1154
	  list = list->next;
	}
      g_list_free (filesel->history_list);
      filesel->history_list = NULL;
1155
    }
Elliot Lee's avatar
Elliot Lee committed
1156

1157
1158
1159
1160
1161
1162
1163
  if (filesel->cmpl_state)
    {
      cmpl_free_state (filesel->cmpl_state);
      filesel->cmpl_state = NULL;
    }
  
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
Elliot Lee's avatar
Elliot Lee committed
1164
1165
}

Owen Taylor's avatar
Owen Taylor committed
1166
1167
1168
1169
1170
1171
1172
1173
static void
gtk_file_selection_finalize (GObject *object)
{
  GtkFileSelection *filesel = GTK_FILE_SELECTION (object);

  g_free (filesel->fileop_file);
}

1174
1175
1176
/* Begin file operations callbacks */

static void
1177
1178
gtk_file_selection_fileop_error (GtkFileSelection *fs,
				 gchar            *error_message)
1179
1180
{
  GtkWidget *dialog;
1181
    
1182
1183
  g_return_if_fail (error_message != NULL);

1184
1185
1186
1187
1188
1189
  /* main dialog */
  dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
				   GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
				   GTK_BUTTONS_CLOSE,
				   "%s", error_message);
1190
1191
1192

  /* yes, we free it */
  g_free (error_message);
1193
1194
1195
1196

  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);

  gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
1197
1198
1199
1200
1201
1202
1203
			     (GtkSignalFunc) gtk_widget_destroy, 
			     (gpointer) dialog);

  gtk_widget_show (dialog);
}

static void
1204
1205
gtk_file_selection_fileop_destroy (GtkWidget *widget,
				   gpointer   data)
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
{
  GtkFileSelection *fs = data;

  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
  
  fs->fileop_dialog = NULL;
}


static void
1216
1217
gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
					 gpointer   data)
1218
1219
{
  GtkFileSelection *fs = data;
Owen Taylor's avatar
Owen Taylor committed
1220
  const gchar *dirname;
1221
1222
  gchar *path;
  gchar *full_path;
1223
  gchar *sys_full_path;
1224
  gchar *buf;
1225
  GError *error = NULL;
1226
1227
1228
1229
1230
1231
1232
1233
  CompletionState *cmpl_state;
  
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));

  dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
  cmpl_state = (CompletionState*) fs->cmpl_state;
  path = cmpl_reference_position (cmpl_state);
  
Tor Lillqvist's avatar
Tor Lillqvist committed
1234
  full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
  if (error)
    {
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
	buf = g_strdup_printf (_("The directory name \"%s\" contains symbols that are not allowed in filenames"), dirname);
      else
	buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n%s"), dirname, error->message,
			       _("You probably used symbols not allowed in filenames."));
      gtk_file_selection_fileop_error (fs, buf);
      g_error_free (error);
      goto out;
    }

1248
  if (mkdir (sys_full_path, 0755) < 0) 
1249
    {
1250
1251
      buf = g_strdup_printf (_("Error creating directory \"%s\": %s\n"), dirname,
			     g_strerror (errno));
1252
      gtk_file_selection_fileop_error (fs, buf);
1253
    }
1254
1255

 out:
1256
  g_free (full_path);
1257
  g_free (sys_full_path);
1258
1259
1260
1261
1262
1263
  
  gtk_widget_destroy (fs->fileop_dialog);
  gtk_file_selection_populate (fs, "", FALSE);
}
  
static void
1264
1265
gtk_file_selection_create_dir (GtkWidget *widget,
			       gpointer   data)
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
{
  GtkFileSelection *fs = data;
  GtkWidget *label;
  GtkWidget *dialog;
  GtkWidget *vbox;
  GtkWidget *button;

  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));

  if (fs->fileop_dialog)
1276
    return;
1277
1278
  
  /* main dialog */
1279
1280
  dialog = gtk_dialog_new ();
  fs->fileop_dialog = dialog;
1281
1282
1283
  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
		      (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
		      (gpointer) fs);
Owen Taylor's avatar
Owen Taylor committed
1284
  gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
1285
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1286
1287
1288

  /* If file dialog is grabbed, grab option dialog */
  /* When option dialog is closed, file dialog will be grabbed again */
1289
1290
  if (GTK_WINDOW (fs)->modal)
      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1291

1292
1293
1294
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1295
		     FALSE, FALSE, 0);
1296
  gtk_widget_show( vbox);
1297
  
1298
  label = gtk_label_new_with_mnemonic (_("_Directory name:"));
1299
1300
1301
  gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
  gtk_widget_show (label);
1302
1303
1304

  /*  The directory entry widget  */
  fs->fileop_entry = gtk_entry_new ();
1305
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1306
1307
  gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
		      TRUE, TRUE, 5);
1308
  GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1309
1310
1311
  gtk_widget_show (fs->fileop_entry);
  
  /* buttons */
Owen Taylor's avatar
Owen Taylor committed
1312
  button = gtk_button_new_with_label (_("Create"));
1313
1314
1315
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
		      (gpointer) fs);
1316
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1317
		     button, TRUE, TRUE, 0);
1318
1319
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_show (button);
1320
  
Owen Taylor's avatar
Owen Taylor committed
1321
  button = gtk_button_new_with_label (_("Cancel"));
1322
1323
1324
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
			     (GtkSignalFunc) gtk_widget_destroy, 
			     (gpointer) dialog);
1325
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1326
		     button, TRUE, TRUE, 0);
1327
1328
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default (button);
1329
  gtk_widget_show (button);
1330

1331
1332
  gtk_widget_grab_focus (fs->fileop_entry);

1333
  gtk_widget_show (dialog);
1334
1335
1336
}

static void
1337
1338
gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
					  gpointer   data)
1339
1340
1341
1342
1343
{
  GtkFileSelection *fs = data;
  CompletionState *cmpl_state;
  gchar *path;
  gchar *full_path;
1344
  gchar *sys_full_path;
1345
  GError *error = NULL;
1346
1347
1348
1349
1350
1351
1352
  gchar *buf;
  
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));

  cmpl_state = (CompletionState*) fs->cmpl_state;
  path = cmpl_reference_position (cmpl_state);
  
Tor Lillqvist's avatar
Tor Lillqvist committed
1353
  full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
  if (error)
    {
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
	buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
			       fs->fileop_file);
      else
	buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
			       fs->fileop_file, error->message,
			       _("It probably contains symbols not allowed in filenames."));
      
      gtk_file_selection_fileop_error (fs, buf);
      g_error_free (error);
      goto out;
    }

1370
  if (unlink (sys_full_path) < 0) 
1371
    {
1372
1373
      buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
			     fs->fileop_file, g_strerror (errno));
1374
      gtk_file_selection_fileop_error (fs, buf);
1375
    }
1376
1377
  
 out:
1378
  g_free (full_path);
1379
  g_free (sys_full_path);
1380
1381
1382
1383
1384
1385
  
  gtk_widget_destroy (fs->fileop_dialog);
  gtk_file_selection_populate (fs, "", FALSE);
}

static void
1386
1387
gtk_file_selection_delete_file (GtkWidget *widget,
				gpointer   data)
1388
1389
1390
1391
1392
1393
{
  GtkFileSelection *fs = data;
  GtkWidget *label;
  GtkWidget *vbox;
  GtkWidget *button;
  GtkWidget *dialog;
Owen Taylor's avatar
Owen Taylor committed
1394
  const gchar *filename;
1395
1396
1397
1398
1399
1400
1401
  gchar *buf;
  
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));

  if (fs->fileop_dialog)
	  return;

Manish Singh's avatar
Manish Singh committed
1402
#ifdef G_WITH_CYGWIN
1403
  translate_win32_path (fs);
Tor Lillqvist's avatar
Tor Lillqvist committed
1404
1405
#endif

1406
  filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1407
1408
  if (strlen (filename) < 1)
    return;
1409

Owen Taylor's avatar
Owen Taylor committed
1410
1411
  g_free (fs->fileop_file);
  fs->fileop_file = g_strdup (filename);
1412
1413
1414
1415
1416
1417
  
  /* main dialog */
  fs->fileop_dialog = dialog = gtk_dialog_new ();
  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
		      (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
		      (gpointer) fs);
Owen Taylor's avatar
Owen Taylor committed
1418
  gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1419
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1420
1421
1422

  /* If file dialog is grabbed, grab option dialog */
  /* When option dialog is closed, file dialog will be grabbed again */
1423
1424
  if (GTK_WINDOW (fs)->modal)
      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1425
<