~canonical-dx-team/ubuntu/maverick/gtk+2.0/menuproxy

« back to all changes in this revision

Viewing changes to gtk/gtkfilesel.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-05-04 12:24:25 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20070504122425-0m8midgzrp40y8w2
Tags: 2.10.12-1ubuntu1
* Sync with Debian
* New upstream version:
  Fixed bugs:
  - 379414 file chooser warnings when changing path in the entry
  - 418585 GtkFileChooserDefault sizing code is not DPI independent
  - 419568 Crash in search if start with special letter
  - 435062 build dies with icon cache validation
  - 379399 Segfault to call gtk_print_operation_run twice.
  - 387889 cups backend has problems when there are too many printers
  - 418531 invalid read to gtkicontheme.c gtk_icon_theme_lookup_icon...
  - 423916 crash in color scheme code
  - 424042 Segmentation fault while quickly pressing Alt+arrows
  - 415260 Protect against negative indices when setting values in G...
  - 419171 XGetVisualInfo() may not set nxvisuals
  - 128852 Gdk cursors don't look good on win32
  - 344657 Ctrl-H doesn't toggle "Show Hidden Files" setting
  - 345345 PrintOperation::paginate is not emitted for class handler
  - 347567 GtkPrintOperation::end-print is not emitted if it's cance...
  - 369112 gtk_ui_manager_add_ui should accept unnamed separator
  - 392015 Selected menu item invisible on Windows Vista
  - 399253 MS-Windows Theme Bottom Tab placement rendering glitches
  - 399425 gtk_input_dialog_fill_axes() adds child to gtkscrolledwin...
  - 403251 [patch] little memory leak in GtkPrintJob
  - 403267 [patch] memory leak in GtkPageSetupUnixDialog
  - 403470 MS-Windows Theme tab placement other than on top leaks a ...
  - 404506 Windows system fonts that have multi-byte font names cann...
  - 405089 Incorrect window placement for GtkEventBox private window
  - 405515 Minor leak in gtkfilesystemmodel.c
  - 405539 gdk_pixbuf_save() for PNG saver can return FALSE without ...
  - 415681 gdk_window_clear_area includes an extra line and column o...
  - 418219 GtkRecentChooser should apply filter before sorting and c...
  - 418403 Scroll to printer after selecting it from settings
  - 421985 _gtk_print_operation_platform_backend_launch_preview
  - 421990 gtk_print_job_get_surface
  - 421993 gtk_print_operation_init
  - 423064 Conditional jump or move depends on uninitialised value(s...
  - 423722 Fix printing header in gtk-demo
  - 424168 gtk_print_operation_run on async preview
  - 425655 Don't install gtk+-unix-print-2.0.pc on non-UNIX platforms
  - 425786 GDK segfaults if XineramaQueryScreens fails
  - 428665 Lpr Backend gets stuck in infinite loop during gtk_enumer...
  - 429902 GtkPrintOperation leaks cairo contextes
  - 431997 First delay of GdkPixbufAnimationIter is wrong
  - 433242 Inconsistent scroll arrow position calculations
  - 433972 Placing gtk.Expander inside a gtk.TextView() changes gtk....
  - 434261 _gtk_toolbar_elide_underscores incorrectly handles some s...
  - 383354 ctrl-L should make 'Location' entry disappear
  - 418673 gtk_recent_manager_add_item
  - 429732 gtk_accel_group_finalize accesses invalid memory
  - 435028 WM_CLIENT_LEADER is wrong on the leader_window
  - 431067 Background of the header window is not updated
  - 338843 add recent files support inside the ui manager
  - 148535 add drop shadow to menus, tooltips, etc. under Windows XP
* debian/control.in:
  - Conflicts on ubuntulooks (<= 0.9.11-1)
* debian/patches/15_default-fallback-icon-theme.patch:
  - patch from Debian, fallback on gnome icon theme

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GTK - The GIMP Toolkit
 
2
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
 
 
20
/*
 
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 
22
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 
23
 * files for a list of changes.  These files are distributed with
 
24
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 
25
 */
 
26
 
 
27
#include <config.h>
 
28
 
 
29
#include <stdio.h>
 
30
#include <sys/types.h>
 
31
#include <sys/stat.h>
 
32
#ifdef HAVE_SYS_PARAM_H
 
33
#include <sys/param.h>
 
34
#endif
 
35
#include <stdlib.h>
 
36
#ifdef HAVE_UNISTD_H
 
37
#include <unistd.h>
 
38
#endif
 
39
#include <string.h>
 
40
#include <errno.h>
 
41
#ifdef HAVE_PWD_H
 
42
#include <pwd.h>
 
43
#endif
 
44
 
 
45
#include <glib.h>               /* Include early to get G_OS_WIN32 etc */
 
46
#include <glib/gstdio.h>
 
47
 
 
48
#if defined(G_PLATFORM_WIN32)
 
49
#include <ctype.h>
 
50
#define STRICT
 
51
#include <windows.h>
 
52
#undef STRICT
 
53
#endif /* G_PLATFORM_WIN32 */
 
54
 
 
55
#include "gdk/gdkkeysyms.h"
 
56
#include "gtkbutton.h"
 
57
#include "gtkcellrenderertext.h"
 
58
#include "gtkentry.h"
 
59
#include "gtkfilesel.h"
 
60
#include "gtkhbox.h"
 
61
#include "gtkhbbox.h"
 
62
#include "gtkintl.h"
 
63
#include "gtklabel.h"
 
64
#include "gtkliststore.h"
 
65
#include "gtkmain.h"
 
66
#include "gtkprivate.h"
 
67
#include "gtkscrolledwindow.h"
 
68
#include "gtkstock.h"
 
69
#include "gtktreeselection.h"
 
70
#include "gtktreeview.h"
 
71
#include "gtkvbox.h"
 
72
#include "gtkmenu.h"
 
73
#include "gtkmenuitem.h"
 
74
#include "gtkdialog.h"
 
75
#include "gtkmessagedialog.h"
 
76
#include "gtkdnd.h"
 
77
#include "gtkeventbox.h"
 
78
 
 
79
#undef GTK_DISABLE_DEPRECATED
 
80
#include "gtkoptionmenu.h"
 
81
 
 
82
#define WANT_HPANED 1
 
83
#include "gtkhpaned.h"
 
84
 
 
85
#include "gtkalias.h"
 
86
 
 
87
#ifdef G_OS_WIN32
 
88
#include <direct.h>
 
89
#include <io.h>
 
90
#ifndef S_ISDIR
 
91
#define S_ISDIR(mode) ((mode)&_S_IFDIR)
 
92
#endif
 
93
#endif /* G_OS_WIN32 */
 
94
 
 
95
#ifdef G_WITH_CYGWIN
 
96
#include <sys/cygwin.h>         /* For cygwin_conv_to_posix_path */
 
97
#endif
 
98
 
 
99
#define DIR_LIST_WIDTH   180
 
100
#define DIR_LIST_HEIGHT  180
 
101
#define FILE_LIST_WIDTH  180
 
102
#define FILE_LIST_HEIGHT 180
 
103
 
 
104
/* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
 
105
 * in here, since the rest of the code in the file does require some
 
106
 * fixed maximum.
 
107
 */
 
108
#ifndef MAXPATHLEN
 
109
#  ifdef PATH_MAX
 
110
#    define MAXPATHLEN PATH_MAX
 
111
#  else
 
112
#    define MAXPATHLEN 2048
 
113
#  endif
 
114
#endif
 
115
 
 
116
/* I've put this here so it doesn't get confused with the 
 
117
 * file completion interface */
 
118
typedef struct _HistoryCallbackArg HistoryCallbackArg;
 
119
 
 
120
struct _HistoryCallbackArg
 
121
{
 
122
  gchar *directory;
 
123
  GtkWidget *menu_item;
 
124
};
 
125
 
 
126
 
 
127
typedef struct _CompletionState    CompletionState;
 
128
typedef struct _CompletionDir      CompletionDir;
 
129
typedef struct _CompletionDirSent  CompletionDirSent;
 
130
typedef struct _CompletionDirEntry CompletionDirEntry;
 
131
typedef struct _CompletionUserDir  CompletionUserDir;
 
132
typedef struct _PossibleCompletion PossibleCompletion;
 
133
 
 
134
/* Non-external file completion decls and structures */
 
135
 
 
136
/* A contant telling PRCS how many directories to cache.  Its actually
 
137
 * kept in a list, so the geometry isn't important. */
 
138
#define CMPL_DIRECTORY_CACHE_SIZE 10
 
139
 
 
140
/* A constant used to determine whether a substring was an exact
 
141
 * match by first_diff_index()
 
142
 */
 
143
#define PATTERN_MATCH -1
 
144
#define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
 
145
#define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
 
146
 
 
147
/* This structure contains all the useful information about a directory
 
148
 * for the purposes of filename completion.  These structures are cached
 
149
 * in the CompletionState struct.  CompletionDir's are reference counted.
 
150
 */
 
151
struct _CompletionDirSent
 
152
{
 
153
#ifndef G_PLATFORM_WIN32
 
154
  ino_t inode;
 
155
  time_t mtime;
 
156
  dev_t device;
 
157
#endif
 
158
 
 
159
  gint entry_count;
 
160
  struct _CompletionDirEntry *entries;
 
161
};
 
162
 
 
163
struct _CompletionDir
 
164
{
 
165
  CompletionDirSent *sent;
 
166
 
 
167
  gchar *fullname;
 
168
  gint fullname_len;
 
169
 
 
170
  struct _CompletionDir *cmpl_parent;
 
171
  gint cmpl_index;
 
172
  gchar *cmpl_text;
 
173
};
 
174
 
 
175
/* This structure contains pairs of directory entry names with a flag saying
 
176
 * whether or not they are a valid directory.  NOTE: This information is used
 
177
 * to provide the caller with information about whether to update its completions
 
178
 * or try to open a file.  Since directories are cached by the directory mtime,
 
179
 * a symlink which points to an invalid file (which will not be a directory),
 
180
 * will not be reevaluated if that file is created, unless the containing
 
181
 * directory is touched.  I consider this case to be worth ignoring (josh).
 
182
 */
 
183
struct _CompletionDirEntry
 
184
{
 
185
  gboolean is_dir;
 
186
  gchar *entry_name;
 
187
  gchar *sort_key;
 
188
};
 
189
 
 
190
struct _CompletionUserDir
 
191
{
 
192
  gchar *login;
 
193
  gchar *homedir;
 
194
};
 
195
 
 
196
struct _PossibleCompletion
 
197
{
 
198
  /* accessible fields, all are accessed externally by functions
 
199
   * declared above
 
200
   */
 
201
  gchar *text;
 
202
  gint is_a_completion;
 
203
  gboolean is_directory;
 
204
 
 
205
  /* Private fields
 
206
   */
 
207
  gint text_alloc;
 
208
};
 
209
 
 
210
struct _CompletionState
 
211
{
 
212
  gint last_valid_char;
 
213
  gchar *updated_text;
 
214
  gint updated_text_len;
 
215
  gint updated_text_alloc;
 
216
  gboolean re_complete;
 
217
 
 
218
  gchar *user_dir_name_buffer;
 
219
  gint user_directories_len;
 
220
 
 
221
  gchar *last_completion_text;
 
222
 
 
223
  gint user_completion_index; /* if >= 0, currently completing ~user */
 
224
 
 
225
  struct _CompletionDir *completion_dir; /* directory completing from */
 
226
  struct _CompletionDir *active_completion_dir;
 
227
 
 
228
  struct _PossibleCompletion the_completion;
 
229
 
 
230
  struct _CompletionDir *reference_dir; /* initial directory */
 
231
 
 
232
  GList* directory_storage;
 
233
  GList* directory_sent_storage;
 
234
 
 
235
  struct _CompletionUserDir *user_directories;
 
236
};
 
237
 
 
238
enum {
 
239
  PROP_0,
 
240
  PROP_SHOW_FILEOPS,
 
241
  PROP_FILENAME,
 
242
  PROP_SELECT_MULTIPLE
 
243
};
 
244
 
 
245
enum {
 
246
  DIR_COLUMN
 
247
};
 
248
 
 
249
enum {
 
250
  FILE_COLUMN
 
251
};
 
252
 
 
253
/* File completion functions which would be external, were they used
 
254
 * outside of this file.
 
255
 */
 
256
 
 
257
static CompletionState*    cmpl_init_state        (void);
 
258
static void                cmpl_free_state        (CompletionState *cmpl_state);
 
259
static gint                cmpl_state_okay        (CompletionState* cmpl_state);
 
260
static const gchar*        cmpl_strerror          (gint);
 
261
 
 
262
static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
 
263
                                                   gchar          **remaining_text,
 
264
                                                   CompletionState *cmpl_state);
 
265
 
 
266
/* Returns a name for consideration, possibly a completion, this name
 
267
 * will be invalid after the next call to cmpl_next_completion.
 
268
 */
 
269
static char*               cmpl_this_completion   (PossibleCompletion*);
 
270
 
 
271
/* True if this completion matches the given text.  Otherwise, this
 
272
 * output can be used to have a list of non-completions.
 
273
 */
 
274
static gint                cmpl_is_a_completion   (PossibleCompletion*);
 
275
 
 
276
/* True if the completion is a directory
 
277
 */
 
278
static gboolean            cmpl_is_directory      (PossibleCompletion*);
 
279
 
 
280
/* Obtains the next completion, or NULL
 
281
 */
 
282
static PossibleCompletion* cmpl_next_completion   (CompletionState*);
 
283
 
 
284
/* Updating completions: the return value of cmpl_updated_text() will
 
285
 * be text_to_complete completed as much as possible after the most
 
286
 * recent call to cmpl_completion_matches.  For the present
 
287
 * application, this is the suggested replacement for the user's input
 
288
 * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
 
289
 * been received.
 
290
 */
 
291
static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
 
292
 
 
293
/* After updating, to see if the completion was a directory, call
 
294
 * this.  If it was, you should consider re-calling completion_matches.
 
295
 */
 
296
static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
 
297
 
 
298
/* Current location: if using file completion, return the current
 
299
 * directory, from which file completion begins.  More specifically,
 
300
 * the cwd concatenated with all exact completions up to the last
 
301
 * directory delimiter('/').
 
302
 */
 
303
static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
 
304
 
 
305
#if 0
 
306
/* This doesn't work currently and would require changes
 
307
 * to fnmatch.c to get working.
 
308
 */
 
309
/* backing up: if cmpl_completion_matches returns NULL, you may query
 
310
 * the index of the last completable character into cmpl_updated_text.
 
311
 */
 
312
static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
 
313
#endif
 
314
 
 
315
/* When the user selects a non-directory, call cmpl_completion_fullname
 
316
 * to get the full name of the selected file.
 
317
 */
 
318
static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
 
319
 
 
320
 
 
321
/* Directory operations. */
 
322
static CompletionDir* open_ref_dir         (gchar* text_to_complete,
 
323
                                            gchar** remaining_text,
 
324
                                            CompletionState* cmpl_state);
 
325
#ifndef G_PLATFORM_WIN32
 
326
static gboolean       check_dir            (gchar *dir_name, 
 
327
                                            struct stat *result, 
 
328
                                            gboolean *stat_subdirs);
 
329
#endif
 
330
static CompletionDir* open_dir             (gchar* dir_name,
 
331
                                            CompletionState* cmpl_state);
 
332
#ifdef HAVE_PWD_H
 
333
static CompletionDir* open_user_dir        (const gchar* text_to_complete,
 
334
                                            CompletionState *cmpl_state);
 
335
#endif
 
336
static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
 
337
                                            CompletionState *cmpl_state);
 
338
static CompletionDirSent* open_new_dir     (gchar* dir_name, 
 
339
                                            struct stat* sbuf,
 
340
                                            gboolean stat_subdirs);
 
341
static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
 
342
static gint           correct_parent       (CompletionDir* cmpl_dir,
 
343
                                            struct stat *sbuf);
 
344
#ifndef G_PLATFORM_WIN32
 
345
static gchar*         find_parent_dir_fullname    (gchar* dirname);
 
346
#endif
 
347
static CompletionDir* attach_dir           (CompletionDirSent* sent,
 
348
                                            gchar* dir_name,
 
349
                                            CompletionState *cmpl_state);
 
350
static void           free_dir_sent (CompletionDirSent* sent);
 
351
static void           free_dir      (CompletionDir  *dir);
 
352
static void           prune_memory_usage(CompletionState *cmpl_state);
 
353
 
 
354
/* Completion operations */
 
355
#ifdef HAVE_PWD_H
 
356
static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
 
357
                                                      CompletionState *cmpl_state);
 
358
#endif
 
359
static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
 
360
static CompletionDir* find_completion_dir(gchar* text_to_complete,
 
361
                                          gchar** remaining_text,
 
362
                                          CompletionState* cmpl_state);
 
363
static PossibleCompletion* append_completion_text(gchar* text,
 
364
                                                  CompletionState* cmpl_state);
 
365
#ifdef HAVE_PWD_H
 
366
static gint get_pwdb(CompletionState* cmpl_state);
 
367
static gint compare_user_dir(const void* a, const void* b);
 
368
#endif
 
369
static gint first_diff_index(gchar* pat, gchar* text);
 
370
static gint compare_cmpl_dir(const void* a, const void* b);
 
371
static void update_cmpl(PossibleCompletion* poss,
 
372
                        CompletionState* cmpl_state);
 
373
 
 
374
static void gtk_file_selection_set_property  (GObject         *object,
 
375
                                              guint            prop_id,
 
376
                                              const GValue    *value,
 
377
                                              GParamSpec      *pspec);
 
378
static void gtk_file_selection_get_property  (GObject         *object,
 
379
                                              guint            prop_id,
 
380
                                              GValue          *value,
 
381
                                              GParamSpec      *pspec);
 
382
static void gtk_file_selection_finalize      (GObject               *object);
 
383
static void gtk_file_selection_destroy       (GtkObject             *object);
 
384
static void gtk_file_selection_map           (GtkWidget             *widget);
 
385
static gint gtk_file_selection_key_press     (GtkWidget             *widget,
 
386
                                              GdkEventKey           *event,
 
387
                                              gpointer               user_data);
 
388
static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
 
389
                                              const gchar           *new_text,
 
390
                                              gint                   new_text_length,
 
391
                                              gint                  *position,
 
392
                                              gpointer               user_data);
 
393
static void gtk_file_selection_update_fileops (GtkFileSelection     *filesel);
 
394
 
 
395
static void gtk_file_selection_file_activate (GtkTreeView       *tree_view,
 
396
                                              GtkTreePath       *path,
 
397
                                              GtkTreeViewColumn *column,
 
398
                                              gpointer           user_data);
 
399
static void gtk_file_selection_file_changed  (GtkTreeSelection  *selection,
 
400
                                              gpointer           user_data);
 
401
static void gtk_file_selection_dir_activate  (GtkTreeView       *tree_view,
 
402
                                              GtkTreePath       *path,
 
403
                                              GtkTreeViewColumn *column,
 
404
                                              gpointer           user_data);
 
405
 
 
406
static void gtk_file_selection_populate      (GtkFileSelection      *fs,
 
407
                                              gchar                 *rel_path,
 
408
                                              gboolean               try_complete,
 
409
                                              gboolean               reset_entry);
 
410
static void gtk_file_selection_abort         (GtkFileSelection      *fs);
 
411
 
 
412
static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
 
413
                                                    gchar                  *current_dir);
 
414
 
 
415
static void gtk_file_selection_create_dir  (GtkWidget *widget, gpointer data);
 
416
static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
 
417
static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
 
418
 
 
419
static void free_selected_names (GPtrArray *names);
 
420
 
 
421
#ifndef G_PLATFORM_WIN32
 
422
 
 
423
#define compare_utf8_filenames(a, b) strcmp(a, b)
 
424
#define compare_sys_filenames(a, b) strcmp(a, b)
 
425
 
 
426
#else
 
427
 
 
428
static gint
 
429
compare_utf8_filenames (const gchar *a,
 
430
                        const gchar *b)
 
431
{
 
432
  gchar *a_folded, *b_folded;
 
433
  gint retval;
 
434
 
 
435
  a_folded = g_utf8_strdown (a, -1);
 
436
  b_folded = g_utf8_strdown (b, -1);
 
437
 
 
438
  retval = strcmp (a_folded, b_folded);
 
439
 
 
440
  g_free (a_folded);
 
441
  g_free (b_folded);
 
442
 
 
443
  return retval;
 
444
}
 
445
 
 
446
static gint
 
447
compare_sys_filenames (const gchar *a,
 
448
                       const gchar *b)
 
449
{
 
450
  gchar *a_utf8, *b_utf8;
 
451
  gint retval;
 
452
 
 
453
  a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL);
 
454
  b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL);
 
455
 
 
456
  retval = compare_utf8_filenames (a_utf8, b_utf8);
 
457
 
 
458
  g_free (a_utf8);
 
459
  g_free (b_utf8);
 
460
 
 
461
  return retval;
 
462
}
 
463
 
 
464
#endif
 
465
 
 
466
/* Saves errno when something cmpl does fails. */
 
467
static gint cmpl_errno;
 
468
 
 
469
#ifdef G_WITH_CYGWIN
 
470
/*
 
471
 * Take the path currently in the file selection
 
472
 * entry field and translate as necessary from
 
473
 * a Win32 style to Cygwin style path.  For
 
474
 * instance translate:
 
475
 * x:\somepath\file.jpg
 
476
 * to:
 
477
 * /cygdrive/x/somepath/file.jpg
 
478
 *
 
479
 * Replace the path in the selection text field.
 
480
 * Return a boolean value concerning whether a
 
481
 * translation had to be made.
 
482
 */
 
483
static int
 
484
translate_win32_path (GtkFileSelection *filesel)
 
485
{
 
486
  int updated = 0;
 
487
  const gchar *path;
 
488
  gchar newPath[MAX_PATH];
 
489
 
 
490
  /*
 
491
   * Retrieve the current path
 
492
   */
 
493
  path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
 
494
 
 
495
  cygwin_conv_to_posix_path (path, newPath);
 
496
  updated = (strcmp (path, newPath) != 0);
 
497
 
 
498
  if (updated)
 
499
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
 
500
    
 
501
  return updated;
 
502
}
 
503
#endif
 
504
 
 
505
G_DEFINE_TYPE (GtkFileSelection, gtk_file_selection, GTK_TYPE_DIALOG)
 
506
 
 
507
static void
 
508
gtk_file_selection_class_init (GtkFileSelectionClass *class)
 
509
{
 
510
  GObjectClass *gobject_class;
 
511
  GtkObjectClass *object_class;
 
512
  GtkWidgetClass *widget_class;
 
513
 
 
514
  gobject_class = (GObjectClass*) class;
 
515
  object_class = (GtkObjectClass*) class;
 
516
  widget_class = (GtkWidgetClass*) class;
 
517
 
 
518
  gobject_class->finalize = gtk_file_selection_finalize;
 
519
  gobject_class->set_property = gtk_file_selection_set_property;
 
520
  gobject_class->get_property = gtk_file_selection_get_property;
 
521
   
 
522
  g_object_class_install_property (gobject_class,
 
523
                                   PROP_FILENAME,
 
524
                                   g_param_spec_string ("filename",
 
525
                                                        P_("Filename"),
 
526
                                                        P_("The currently selected filename"),
 
527
                                                        NULL,
 
528
                                                        GTK_PARAM_READWRITE));
 
529
  g_object_class_install_property (gobject_class,
 
530
                                   PROP_SHOW_FILEOPS,
 
531
                                   g_param_spec_boolean ("show-fileops",
 
532
                                                         P_("Show file operations"),
 
533
                                                         P_("Whether buttons for creating/manipulating files should be displayed"),
 
534
                                                         FALSE,
 
535
                                                         GTK_PARAM_READWRITE));
 
536
  g_object_class_install_property (gobject_class,
 
537
                                   PROP_SELECT_MULTIPLE,
 
538
                                   g_param_spec_boolean ("select-multiple",
 
539
                                                         P_("Select Multiple"),
 
540
                                                         P_("Whether to allow multiple files to be selected"),
 
541
                                                         FALSE,
 
542
                                                         GTK_PARAM_READWRITE));
 
543
  object_class->destroy = gtk_file_selection_destroy;
 
544
  widget_class->map = gtk_file_selection_map;
 
545
}
 
546
 
 
547
static void gtk_file_selection_set_property (GObject         *object,
 
548
                                             guint            prop_id,
 
549
                                             const GValue    *value,
 
550
                                             GParamSpec      *pspec)
 
551
{
 
552
  GtkFileSelection *filesel;
 
553
 
 
554
  filesel = GTK_FILE_SELECTION (object);
 
555
 
 
556
  switch (prop_id)
 
557
    {
 
558
    case PROP_FILENAME:
 
559
      gtk_file_selection_set_filename (filesel,
 
560
                                       g_value_get_string (value));
 
561
      break;
 
562
    case PROP_SHOW_FILEOPS:
 
563
      if (g_value_get_boolean (value))
 
564
         gtk_file_selection_show_fileop_buttons (filesel);
 
565
      else
 
566
         gtk_file_selection_hide_fileop_buttons (filesel);
 
567
      break;
 
568
    case PROP_SELECT_MULTIPLE:
 
569
      gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
 
570
      break;
 
571
    default:
 
572
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
573
      break;
 
574
    }
 
575
}
 
576
 
 
577
static void gtk_file_selection_get_property (GObject         *object,
 
578
                                             guint            prop_id,
 
579
                                             GValue          *value,
 
580
                                             GParamSpec      *pspec)
 
581
{
 
582
  GtkFileSelection *filesel;
 
583
 
 
584
  filesel = GTK_FILE_SELECTION (object);
 
585
 
 
586
  switch (prop_id)
 
587
    {
 
588
    case PROP_FILENAME:
 
589
      g_value_set_string (value,
 
590
                          gtk_file_selection_get_filename(filesel));
 
591
      break;
 
592
 
 
593
    case PROP_SHOW_FILEOPS:
 
594
      /* This is a little bit hacky, but doing otherwise would require
 
595
       * adding a field to the object.
 
596
       */
 
597
      g_value_set_boolean (value, (filesel->fileop_c_dir && 
 
598
                                   filesel->fileop_del_file &&
 
599
                                   filesel->fileop_ren_file));
 
600
      break;
 
601
    case PROP_SELECT_MULTIPLE:
 
602
      g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
 
603
      break;
 
604
    default:
 
605
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
606
      break;
 
607
    }
 
608
}
 
609
 
 
610
static gboolean
 
611
grab_default (GtkWidget *widget)
 
612
{
 
613
  gtk_widget_grab_default (widget);
 
614
  return FALSE;
 
615
}
 
616
     
 
617
static void
 
618
gtk_file_selection_init (GtkFileSelection *filesel)
 
619
{
 
620
  GtkWidget *entry_vbox;
 
621
  GtkWidget *label;
 
622
  GtkWidget *list_hbox, *list_container;
 
623
  GtkWidget *pulldown_hbox;
 
624
  GtkWidget *scrolled_win;
 
625
  GtkWidget *eventbox;
 
626
  GtkWidget *spacer;
 
627
  GtkDialog *dialog;
 
628
 
 
629
  GtkListStore *model;
 
630
  GtkTreeViewColumn *column;
 
631
  
 
632
  gtk_widget_push_composite_child ();
 
633
 
 
634
  dialog = GTK_DIALOG (filesel);
 
635
 
 
636
  filesel->cmpl_state = cmpl_init_state ();
 
637
 
 
638
  /* The dialog-sized vertical box  */
 
639
  filesel->main_vbox = dialog->vbox;
 
640
  gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
 
641
 
 
642
  /* The horizontal box containing create, rename etc. buttons */
 
643
  filesel->button_area = gtk_hbutton_box_new ();
 
644
  gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
 
645
  gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
 
646
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
 
647
                      FALSE, FALSE, 0);
 
648
  gtk_widget_show (filesel->button_area);
 
649
  
 
650
  gtk_file_selection_show_fileop_buttons (filesel);
 
651
 
 
652
  /* hbox for pulldown menu */
 
653
  pulldown_hbox = gtk_hbox_new (TRUE, 5);
 
654
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
 
655
  gtk_widget_show (pulldown_hbox);
 
656
  
 
657
  /* Pulldown menu */
 
658
  filesel->history_pulldown = gtk_option_menu_new ();
 
659
  gtk_widget_show (filesel->history_pulldown);
 
660
  gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
 
661
                      FALSE, FALSE, 0);
 
662
    
 
663
  /*  The horizontal box containing the directory and file listboxes  */
 
664
 
 
665
  spacer = gtk_hbox_new (FALSE, 0);
 
666
  gtk_widget_set_size_request (spacer, -1, 5);
 
667
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
 
668
  gtk_widget_show (spacer);
 
669
  
 
670
  list_hbox = gtk_hbox_new (FALSE, 5);
 
671
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
 
672
  gtk_widget_show (list_hbox);
 
673
  if (WANT_HPANED)
 
674
    list_container = g_object_new (GTK_TYPE_HPANED,
 
675
                                   "visible", TRUE,
 
676
                                   "parent", list_hbox,
 
677
                                   "border_width", 0,
 
678
                                   NULL);
 
679
  else
 
680
    list_container = list_hbox;
 
681
 
 
682
  spacer = gtk_hbox_new (FALSE, 0);
 
683
  gtk_widget_set_size_request (spacer, -1, 5);
 
684
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);  
 
685
  gtk_widget_show (spacer);
 
686
  
 
687
  /* The directories list */
 
688
 
 
689
  model = gtk_list_store_new (1, G_TYPE_STRING);
 
690
  filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
 
691
  g_object_unref (model);
 
692
 
 
693
  column = gtk_tree_view_column_new_with_attributes (_("Folders"),
 
694
                                                     gtk_cell_renderer_text_new (),
 
695
                                                     "text", DIR_COLUMN,
 
696
                                                     NULL);
 
697
  label = gtk_label_new_with_mnemonic (_("Fol_ders"));
 
698
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
 
699
  gtk_widget_show (label);
 
700
  gtk_tree_view_column_set_widget (column, label);
 
701
  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 
702
  gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
 
703
 
 
704
  gtk_widget_set_size_request (filesel->dir_list,
 
705
                               DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
 
706
  g_signal_connect (filesel->dir_list, "row_activated",
 
707
                    G_CALLBACK (gtk_file_selection_dir_activate), filesel);
 
708
 
 
709
  /*  gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
 
710
 
 
711
  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
 
712
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);  
 
713
  gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
 
714
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
 
715
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
 
716
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
 
717
  if (GTK_IS_PANED (list_container))
 
718
    gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
 
719
  else
 
720
    gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
 
721
  gtk_widget_show (filesel->dir_list);
 
722
  gtk_widget_show (scrolled_win);
 
723
 
 
724
  /* The files list */
 
725
  model = gtk_list_store_new (1, G_TYPE_STRING);
 
726
  filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
 
727
  g_object_unref (model);
 
728
 
 
729
  column = gtk_tree_view_column_new_with_attributes (_("Files"),
 
730
                                                     gtk_cell_renderer_text_new (),
 
731
                                                     "text", FILE_COLUMN,
 
732
                                                     NULL);
 
733
  label = gtk_label_new_with_mnemonic (_("_Files"));
 
734
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
 
735
  gtk_widget_show (label);
 
736
  gtk_tree_view_column_set_widget (column, label);
 
737
  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 
738
  gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
 
739
 
 
740
  gtk_widget_set_size_request (filesel->file_list,
 
741
                               FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
 
742
  g_signal_connect (filesel->file_list, "row_activated",
 
743
                    G_CALLBACK (gtk_file_selection_file_activate), filesel);
 
744
  g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
 
745
                    G_CALLBACK (gtk_file_selection_file_changed), filesel);
 
746
 
 
747
  /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
 
748
 
 
749
  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
 
750
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
 
751
  gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
 
752
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
 
753
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
 
754
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
 
755
  gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
 
756
  gtk_widget_show (filesel->file_list);
 
757
  gtk_widget_show (scrolled_win);
 
758
 
 
759
  /* action area for packing buttons into. */
 
760
  filesel->action_area = gtk_hbox_new (TRUE, 0);
 
761
  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
 
762
                      FALSE, FALSE, 0);
 
763
  gtk_widget_show (filesel->action_area);
 
764
  
 
765
  /*  The OK/Cancel button area */
 
766
 
 
767
  /*  The Cancel button  */
 
768
  filesel->cancel_button = gtk_dialog_add_button (dialog,
 
769
                                                  GTK_STOCK_CANCEL,
 
770
                                                  GTK_RESPONSE_CANCEL);
 
771
  /*  The OK button  */
 
772
  filesel->ok_button = gtk_dialog_add_button (dialog,
 
773
                                              GTK_STOCK_OK,
 
774
                                              GTK_RESPONSE_OK);
 
775
 
 
776
  gtk_dialog_set_alternative_button_order (dialog,
 
777
                                           GTK_RESPONSE_OK,
 
778
                                           GTK_RESPONSE_CANCEL,
 
779
                                           -1);
 
780
 
 
781
  gtk_widget_grab_default (filesel->ok_button);
 
782
 
 
783
  /*  The selection entry widget  */
 
784
  entry_vbox = gtk_vbox_new (FALSE, 2);
 
785
  gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
 
786
  gtk_widget_show (entry_vbox);
 
787
  
 
788
  eventbox = gtk_event_box_new ();
 
789
  filesel->selection_text = label = gtk_label_new ("");
 
790
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 
791
  gtk_container_add (GTK_CONTAINER (eventbox), label);
 
792
  gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
 
793
  gtk_widget_show (label);
 
794
  gtk_widget_show (eventbox);
 
795
 
 
796
  filesel->selection_entry = gtk_entry_new ();
 
797
  g_signal_connect (filesel->selection_entry, "key_press_event",
 
798
                    G_CALLBACK (gtk_file_selection_key_press), filesel);
 
799
  g_signal_connect (filesel->selection_entry, "insert_text",
 
800
                    G_CALLBACK (gtk_file_selection_insert_text), NULL);
 
801
  g_signal_connect_swapped (filesel->selection_entry, "changed",
 
802
                            G_CALLBACK (gtk_file_selection_update_fileops), filesel);
 
803
  g_signal_connect_swapped (filesel->selection_entry, "focus_in_event",
 
804
                            G_CALLBACK (grab_default),
 
805
                            filesel->ok_button);
 
806
  g_signal_connect_swapped (filesel->selection_entry, "activate",
 
807
                            G_CALLBACK (gtk_button_clicked),
 
808
                            filesel->ok_button);
 
809
  
 
810
  gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
 
811
  gtk_widget_show (filesel->selection_entry);
 
812
 
 
813
  gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
 
814
                                 filesel->selection_entry);
 
815
 
 
816
  if (!cmpl_state_okay (filesel->cmpl_state))
 
817
    {
 
818
      gchar err_buf[256];
 
819
 
 
820
      g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
 
821
 
 
822
      gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
 
823
    }
 
824
  else
 
825
    {
 
826
      gtk_file_selection_populate (filesel, "", FALSE, TRUE);
 
827
    }
 
828
 
 
829
  gtk_widget_grab_focus (filesel->selection_entry);
 
830
 
 
831
  gtk_widget_pop_composite_child ();
 
832
}
 
833
 
 
834
static gchar *
 
835
uri_list_extract_first_uri (const gchar* uri_list)
 
836
{
 
837
  const gchar *p, *q;
 
838
  
 
839
  g_return_val_if_fail (uri_list != NULL, NULL);
 
840
  
 
841
  p = uri_list;
 
842
  /* We don't actually try to validate the URI according to RFC
 
843
   * 2396, or even check for allowed characters - we just ignore
 
844
   * comments and trim whitespace off the ends.  We also
 
845
   * allow LF delimination as well as the specified CRLF.
 
846
   *
 
847
   * We do allow comments like specified in RFC 2483.
 
848
   */
 
849
  while (p)
 
850
    {
 
851
      if (*p != '#')
 
852
        {
 
853
          while (g_ascii_isspace(*p))
 
854
            p++;
 
855
          
 
856
          q = p;
 
857
          while (*q && (*q != '\n') && (*q != '\r'))
 
858
            q++;
 
859
          
 
860
          if (q > p)
 
861
            {
 
862
              q--;
 
863
              while (q > p && g_ascii_isspace (*q))
 
864
                q--;
 
865
 
 
866
              if (q > p)
 
867
                return g_strndup (p, q - p + 1);
 
868
            }
 
869
        }
 
870
      p = strchr (p, '\n');
 
871
      if (p)
 
872
        p++;
 
873
    }
 
874
  return NULL;
 
875
}
 
876
 
 
877
static void
 
878
dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
 
879
{
 
880
  gchar *filename;
 
881
  
 
882
  if (response_id == GTK_RESPONSE_YES)
 
883
    {
 
884
      filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
 
885
 
 
886
      gtk_file_selection_set_filename (fs, filename);
 
887
    }
 
888
  
 
889
  gtk_widget_destroy (dialog);
 
890
}
 
891
 
 
892
 
 
893
static void
 
894
filenames_dropped (GtkWidget        *widget,
 
895
                   GdkDragContext   *context,
 
896
                   gint              x,
 
897
                   gint              y,
 
898
                   GtkSelectionData *selection_data,
 
899
                   guint             info,
 
900
                   guint             time)
 
901
{
 
902
  char *uri = NULL;
 
903
  char *filename = NULL;
 
904
  char *hostname;
 
905
  const char *this_hostname;
 
906
  GError *error = NULL;
 
907
        
 
908
  if (!selection_data->data)
 
909
    return;
 
910
 
 
911
  uri = uri_list_extract_first_uri ((char *)selection_data->data);
 
912
  
 
913
  if (!uri)
 
914
    return;
 
915
 
 
916
  filename = g_filename_from_uri (uri, &hostname, &error);
 
917
  g_free (uri);
 
918
  
 
919
  if (!filename)
 
920
    {
 
921
      g_warning ("Error getting dropped filename: %s\n",
 
922
                 error->message);
 
923
      g_error_free (error);
 
924
      return;
 
925
    }
 
926
 
 
927
  this_hostname = g_get_host_name ();
 
928
  
 
929
  if ((hostname == NULL) ||
 
930
      (strcmp (hostname, this_hostname) == 0) ||
 
931
      (strcmp (hostname, "localhost") == 0))
 
932
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
 
933
                                     filename);
 
934
  else
 
935
    {
 
936
      GtkWidget *dialog;
 
937
      gchar *filename_utf8;
 
938
 
 
939
      /* Conversion back to UTF-8 should always succeed for the result
 
940
       * of g_filename_from_uri()
 
941
       */
 
942
      filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
 
943
      g_assert (filename_utf8);
 
944
      
 
945
      dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
 
946
                                       GTK_DIALOG_DESTROY_WITH_PARENT,
 
947
                                       GTK_MESSAGE_QUESTION,
 
948
                                       GTK_BUTTONS_YES_NO,
 
949
                                       _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
 
950
                                         "Are you sure that you want to select it?"), filename_utf8, hostname);
 
951
      g_free (filename_utf8);
 
952
 
 
953
      g_object_set_data_full (G_OBJECT (dialog), I_("gtk-fs-dnd-filename"), g_strdup (filename), g_free);
 
954
      
 
955
      g_signal_connect_data (dialog, "response",
 
956
                             (GCallback) dnd_really_drop, 
 
957
                             widget, NULL, 0);
 
958
      
 
959
      gtk_widget_show (dialog);
 
960
    }
 
961
 
 
962
  g_free (hostname);
 
963
  g_free (filename);
 
964
}
 
965
 
 
966
enum
 
967
{
 
968
  TARGET_URILIST,
 
969
  TARGET_UTF8_STRING,
 
970
  TARGET_STRING,
 
971
  TARGET_TEXT,
 
972
  TARGET_COMPOUND_TEXT
 
973
};
 
974
 
 
975
 
 
976
static void
 
977
filenames_drag_get (GtkWidget        *widget,
 
978
                    GdkDragContext   *context,
 
979
                    GtkSelectionData *selection_data,
 
980
                    guint             info,
 
981
                    guint             time,
 
982
                    GtkFileSelection *filesel)
 
983
{
 
984
  const gchar *file;
 
985
  gchar *uri_list;
 
986
  const char *hostname;
 
987
  GError *error;
 
988
 
 
989
  file = gtk_file_selection_get_filename (filesel);
 
990
 
 
991
  if (file)
 
992
    {
 
993
      if (info == TARGET_URILIST)
 
994
        {
 
995
          hostname = g_get_host_name ();
 
996
          
 
997
          error = NULL;
 
998
          uri_list = g_filename_to_uri (file, hostname, &error);
 
999
          if (!uri_list)
 
1000
            {
 
1001
              g_warning ("Error getting filename: %s\n",
 
1002
                         error->message);
 
1003
              g_error_free (error);
 
1004
              return;
 
1005
            }
 
1006
          
 
1007
          gtk_selection_data_set (selection_data,
 
1008
                                  selection_data->target, 8,
 
1009
                                  (void *)uri_list, strlen((char *)uri_list));
 
1010
          g_free (uri_list);
 
1011
        }
 
1012
      else
 
1013
        {
 
1014
          gchar *filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
 
1015
          g_assert (filename_utf8);
 
1016
          gtk_selection_data_set_text (selection_data, filename_utf8, -1);
 
1017
          g_free (filename_utf8);
 
1018
        }
 
1019
    }
 
1020
}
 
1021
 
 
1022
static void
 
1023
file_selection_setup_dnd (GtkFileSelection *filesel)
 
1024
{
 
1025
  GtkWidget *eventbox;
 
1026
  static const GtkTargetEntry drop_types[] = {
 
1027
    { "text/uri-list", 0, TARGET_URILIST}
 
1028
  };
 
1029
  static const gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
 
1030
  static const GtkTargetEntry drag_types[] = {
 
1031
    { "text/uri-list", 0, TARGET_URILIST},
 
1032
    { "UTF8_STRING", 0, TARGET_UTF8_STRING },
 
1033
    { "STRING", 0, 0 },
 
1034
    { "TEXT",   0, 0 }, 
 
1035
    { "COMPOUND_TEXT", 0, 0 }
 
1036
  };
 
1037
  static const gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
 
1038
 
 
1039
  gtk_drag_dest_set (GTK_WIDGET (filesel),
 
1040
                     GTK_DEST_DEFAULT_ALL,
 
1041
                     drop_types, n_drop_types,
 
1042
                     GDK_ACTION_COPY);
 
1043
 
 
1044
  g_signal_connect (filesel, "drag_data_received",
 
1045
                    G_CALLBACK (filenames_dropped), NULL);
 
1046
 
 
1047
  eventbox = gtk_widget_get_parent (filesel->selection_text);
 
1048
  gtk_drag_source_set (eventbox,
 
1049
                       GDK_BUTTON1_MASK,
 
1050
                       drag_types, n_drag_types,
 
1051
                       GDK_ACTION_COPY);
 
1052
 
 
1053
  g_signal_connect (eventbox, "drag_data_get",
 
1054
                    G_CALLBACK (filenames_drag_get), filesel);
 
1055
}
 
1056
 
 
1057
GtkWidget*
 
1058
gtk_file_selection_new (const gchar *title)
 
1059
{
 
1060
  GtkFileSelection *filesel;
 
1061
 
 
1062
  filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
 
1063
  gtk_window_set_title (GTK_WINDOW (filesel), title);
 
1064
  gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
 
1065
 
 
1066
  file_selection_setup_dnd (filesel);
 
1067
  
 
1068
  return GTK_WIDGET (filesel);
 
1069
}
 
1070
 
 
1071
void
 
1072
gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
 
1073
{
 
1074
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
 
1075
    
 
1076
  /* delete, create directory, and rename */
 
1077
  if (!filesel->fileop_c_dir) 
 
1078
    {
 
1079
      filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
 
1080
      g_signal_connect (filesel->fileop_c_dir, "clicked",
 
1081
                        G_CALLBACK (gtk_file_selection_create_dir),
 
1082
                        filesel);
 
1083
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
 
1084
                          filesel->fileop_c_dir, TRUE, TRUE, 0);
 
1085
      gtk_widget_show (filesel->fileop_c_dir);
 
1086
    }
 
1087
        
 
1088
  if (!filesel->fileop_del_file) 
 
1089
    {
 
1090
      filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
 
1091
      g_signal_connect (filesel->fileop_del_file, "clicked",
 
1092
                        G_CALLBACK (gtk_file_selection_delete_file),
 
1093
                        filesel);
 
1094
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
 
1095
                          filesel->fileop_del_file, TRUE, TRUE, 0);
 
1096
      gtk_widget_show (filesel->fileop_del_file);
 
1097
    }
 
1098
 
 
1099
  if (!filesel->fileop_ren_file)
 
1100
    {
 
1101
      filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
 
1102
      g_signal_connect (filesel->fileop_ren_file, "clicked",
 
1103
                        G_CALLBACK (gtk_file_selection_rename_file),
 
1104
                        filesel);
 
1105
      gtk_box_pack_start (GTK_BOX (filesel->button_area), 
 
1106
                          filesel->fileop_ren_file, TRUE, TRUE, 0);
 
1107
      gtk_widget_show (filesel->fileop_ren_file);
 
1108
    }
 
1109
  
 
1110
  gtk_file_selection_update_fileops (filesel);
 
1111
  
 
1112
  g_object_notify (G_OBJECT (filesel), "show-fileops");
 
1113
}
 
1114
 
 
1115
void       
 
1116
gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
 
1117
{
 
1118
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
 
1119
    
 
1120
  if (filesel->fileop_ren_file)
 
1121
    {
 
1122
      gtk_widget_destroy (filesel->fileop_ren_file);
 
1123
      filesel->fileop_ren_file = NULL;
 
1124
    }
 
1125
 
 
1126
  if (filesel->fileop_del_file)
 
1127
    {
 
1128
      gtk_widget_destroy (filesel->fileop_del_file);
 
1129
      filesel->fileop_del_file = NULL;
 
1130
    }
 
1131
 
 
1132
  if (filesel->fileop_c_dir)
 
1133
    {
 
1134
      gtk_widget_destroy (filesel->fileop_c_dir);
 
1135
      filesel->fileop_c_dir = NULL;
 
1136
    }
 
1137
  g_object_notify (G_OBJECT (filesel), "show-fileops");
 
1138
}
 
1139
 
 
1140
 
 
1141
 
 
1142
/**
 
1143
 * gtk_file_selection_set_filename:
 
1144
 * @filesel: a #GtkFileSelection.
 
1145
 * @filename:  a string to set as the default file name.
 
1146
 * 
 
1147
 * Sets a default path for the file requestor. If @filename includes a
 
1148
 * directory path, then the requestor will open with that path as its
 
1149
 * current working directory.
 
1150
 *
 
1151
 * This has the consequence that in order to open the requestor with a 
 
1152
 * working directory and an empty filename, @filename must have a trailing
 
1153
 * directory separator.
 
1154
 *
 
1155
 * The encoding of @filename is preferred GLib file name encoding, which
 
1156
 * may not be UTF-8. See g_filename_from_utf8().
 
1157
 **/
 
1158
void
 
1159
gtk_file_selection_set_filename (GtkFileSelection *filesel,
 
1160
                                 const gchar      *filename)
 
1161
{
 
1162
  gchar *buf;
 
1163
  const char *name, *last_slash;
 
1164
  char *filename_utf8;
 
1165
 
 
1166
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
 
1167
  g_return_if_fail (filename != NULL);
 
1168
 
 
1169
  filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
 
1170
  g_return_if_fail (filename_utf8 != NULL);
 
1171
 
 
1172
  last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
 
1173
 
 
1174
  if (!last_slash)
 
1175
    {
 
1176
      buf = g_strdup ("");
 
1177
      name = filename_utf8;
 
1178
    }
 
1179
  else
 
1180
    {
 
1181
      buf = g_strdup (filename_utf8);
 
1182
      buf[last_slash - filename_utf8 + 1] = 0;
 
1183
      name = last_slash + 1;
 
1184
    }
 
1185
 
 
1186
  gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
 
1187
 
 
1188
  if (filesel->selection_entry)
 
1189
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
 
1190
  g_free (buf);
 
1191
  g_object_notify (G_OBJECT (filesel), "filename");
 
1192
 
 
1193
  g_free (filename_utf8);
 
1194
}
 
1195
 
 
1196
/**
 
1197
 * gtk_file_selection_get_filename:
 
1198
 * @filesel: a #GtkFileSelection
 
1199
 * 
 
1200
 * This function returns the selected filename in the GLib file name
 
1201
 * encoding. To convert to UTF-8, call g_filename_to_utf8(). The
 
1202
 * returned string points to a statically allocated buffer and should
 
1203
 * be copied if you plan to keep it around.
 
1204
 *
 
1205
 * If no file is selected then the selected directory path is returned.
 
1206
 * 
 
1207
 * Return value: currently-selected filename in the on-disk encoding.
 
1208
 **/
 
1209
G_CONST_RETURN gchar*
 
1210
gtk_file_selection_get_filename (GtkFileSelection *filesel)
 
1211
{
 
1212
  static const gchar nothing[2] = "";
 
1213
  static GString *something;
 
1214
  char *sys_filename;
 
1215
  const char *text;
 
1216
 
 
1217
  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
 
1218
 
 
1219
#ifdef G_WITH_CYGWIN
 
1220
  translate_win32_path (filesel);
 
1221
#endif
 
1222
  text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
 
1223
  if (text)
 
1224
    {
 
1225
      gchar *fullname = cmpl_completion_fullname (text, filesel->cmpl_state);
 
1226
      sys_filename = g_filename_from_utf8 (fullname, -1, NULL, NULL, NULL);
 
1227
      g_free (fullname);
 
1228
      if (!sys_filename)
 
1229
        return nothing;
 
1230
      if (!something)
 
1231
        something = g_string_new (sys_filename);
 
1232
      else
 
1233
        g_string_assign (something, sys_filename);
 
1234
      g_free (sys_filename);
 
1235
 
 
1236
      return something->str;
 
1237
    }
 
1238
 
 
1239
  return nothing;
 
1240
}
 
1241
 
 
1242
void
 
1243
gtk_file_selection_complete (GtkFileSelection *filesel,
 
1244
                             const gchar      *pattern)
 
1245
{
 
1246
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
 
1247
  g_return_if_fail (pattern != NULL);
 
1248
 
 
1249
  if (filesel->selection_entry)
 
1250
    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
 
1251
  gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
 
1252
}
 
1253
 
 
1254
static void
 
1255
gtk_file_selection_destroy (GtkObject *object)
 
1256
{
 
1257
  GtkFileSelection *filesel;
 
1258
  GList *list;
 
1259
  HistoryCallbackArg *callback_arg;
 
1260
  
 
1261
  g_return_if_fail (GTK_IS_FILE_SELECTION (object));
 
1262
  
 
1263
  filesel = GTK_FILE_SELECTION (object);
 
1264
  
 
1265
  if (filesel->fileop_dialog)
 
1266
    {
 
1267
      gtk_widget_destroy (filesel->fileop_dialog);
 
1268
      filesel->fileop_dialog = NULL;
 
1269
    }
 
1270
  
 
1271
  if (filesel->history_list)
 
1272
    {
 
1273
      list = filesel->history_list;
 
1274
      while (list)
 
1275
        {
 
1276
          callback_arg = list->data;
 
1277
          g_free (callback_arg->directory);
 
1278
          g_free (callback_arg);
 
1279
          list = list->next;
 
1280
        }
 
1281
      g_list_free (filesel->history_list);
 
1282
      filesel->history_list = NULL;
 
1283
    }
 
1284
 
 
1285
  if (filesel->cmpl_state)
 
1286
    {
 
1287
      cmpl_free_state (filesel->cmpl_state);
 
1288
      filesel->cmpl_state = NULL;
 
1289
    }
 
1290
 
 
1291
  if (filesel->selected_names)
 
1292
    {
 
1293
      free_selected_names (filesel->selected_names);
 
1294
      filesel->selected_names = NULL;
 
1295
    } 
 
1296
 
 
1297
  if (filesel->last_selected)
 
1298
    {
 
1299
      g_free (filesel->last_selected);
 
1300
      filesel->last_selected = NULL;
 
1301
    }
 
1302
 
 
1303
  GTK_OBJECT_CLASS (gtk_file_selection_parent_class)->destroy (object);
 
1304
}
 
1305
 
 
1306
static void
 
1307
gtk_file_selection_map (GtkWidget *widget)
 
1308
{
 
1309
  GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
 
1310
 
 
1311
  /* Refresh the contents */
 
1312
  gtk_file_selection_populate (filesel, "", FALSE, FALSE);
 
1313
  
 
1314
  GTK_WIDGET_CLASS (gtk_file_selection_parent_class)->map (widget);
 
1315
}
 
1316
 
 
1317
static void
 
1318
gtk_file_selection_finalize (GObject *object)
 
1319
{
 
1320
  GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
 
1321
 
 
1322
  g_free (filesel->fileop_file);
 
1323
 
 
1324
  G_OBJECT_CLASS (gtk_file_selection_parent_class)->finalize (object);
 
1325
}
 
1326
 
 
1327
/* Begin file operations callbacks */
 
1328
 
 
1329
static void
 
1330
gtk_file_selection_fileop_error (GtkFileSelection *fs,
 
1331
                                 gchar            *error_message)
 
1332
{
 
1333
  GtkWidget *dialog;
 
1334
    
 
1335
  g_return_if_fail (error_message != NULL);
 
1336
 
 
1337
  /* main dialog */
 
1338
  dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
 
1339
                                   GTK_DIALOG_DESTROY_WITH_PARENT,
 
1340
                                   GTK_MESSAGE_ERROR,
 
1341
                                   GTK_BUTTONS_OK,
 
1342
                                   "%s", error_message);
 
1343
 
 
1344
  /* yes, we free it */
 
1345
  g_free (error_message);
 
1346
 
 
1347
  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 
1348
 
 
1349
  g_signal_connect_swapped (dialog, "response",
 
1350
                            G_CALLBACK (gtk_widget_destroy),
 
1351
                            dialog);
 
1352
 
 
1353
  gtk_widget_show (dialog);
 
1354
}
 
1355
 
 
1356
static void
 
1357
gtk_file_selection_fileop_destroy (GtkWidget *widget,
 
1358
                                   gpointer   data)
 
1359
{
 
1360
  GtkFileSelection *fs = data;
 
1361
 
 
1362
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1363
  
 
1364
  fs->fileop_dialog = NULL;
 
1365
}
 
1366
 
 
1367
static gboolean
 
1368
entry_is_empty (GtkEntry *entry)
 
1369
{
 
1370
  const gchar *text = gtk_entry_get_text (entry);
 
1371
  
 
1372
  return *text == '\0';
 
1373
}
 
1374
 
 
1375
static void
 
1376
gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
 
1377
                                         GtkWidget  *button)
 
1378
{
 
1379
  gtk_widget_set_sensitive (button, !entry_is_empty (entry));
 
1380
}
 
1381
 
 
1382
static void
 
1383
gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
 
1384
                                         gpointer   data)
 
1385
{
 
1386
  GtkFileSelection *fs = data;
 
1387
  const gchar *dirname;
 
1388
  gchar *path;
 
1389
  gchar *full_path;
 
1390
  gchar *sys_full_path;
 
1391
  gchar *buf;
 
1392
  GError *error = NULL;
 
1393
  CompletionState *cmpl_state;
 
1394
  
 
1395
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1396
 
 
1397
  dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
 
1398
  cmpl_state = (CompletionState*) fs->cmpl_state;
 
1399
  path = cmpl_reference_position (cmpl_state);
 
1400
  
 
1401
  full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
 
1402
  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
 
1403
  if (error)
 
1404
    {
 
1405
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
 
1406
        buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
 
1407
      else
 
1408
        buf = g_strdup_printf (_("Error creating directory '%s': %s"), 
 
1409
                               dirname, error->message);
 
1410
      gtk_file_selection_fileop_error (fs, buf);
 
1411
      g_error_free (error);
 
1412
      goto out;
 
1413
    }
 
1414
 
 
1415
  if (g_mkdir (sys_full_path, 0777) < 0)
 
1416
    {
 
1417
      buf = g_strdup_printf (_("Error creating directory '%s': %s"), 
 
1418
                             dirname, g_strerror (errno));
 
1419
      gtk_file_selection_fileop_error (fs, buf);
 
1420
    }
 
1421
 
 
1422
 out:
 
1423
  g_free (full_path);
 
1424
  g_free (sys_full_path);
 
1425
  
 
1426
  gtk_widget_destroy (fs->fileop_dialog);
 
1427
  gtk_file_selection_populate (fs, "", FALSE, FALSE);
 
1428
}
 
1429
  
 
1430
static void
 
1431
gtk_file_selection_create_dir (GtkWidget *widget,
 
1432
                               gpointer   data)
 
1433
{
 
1434
  GtkFileSelection *fs = data;
 
1435
  GtkWidget *label;
 
1436
  GtkWidget *dialog;
 
1437
  GtkWidget *vbox;
 
1438
  GtkWidget *button;
 
1439
 
 
1440
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1441
 
 
1442
  if (fs->fileop_dialog)
 
1443
    return;
 
1444
  
 
1445
  /* main dialog */
 
1446
  dialog = gtk_dialog_new ();
 
1447
  fs->fileop_dialog = dialog;
 
1448
  g_signal_connect (dialog, "destroy",
 
1449
                    G_CALLBACK (gtk_file_selection_fileop_destroy),
 
1450
                    fs);
 
1451
  gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
 
1452
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
 
1453
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
 
1454
 
 
1455
  /* If file dialog is grabbed, grab option dialog */
 
1456
  /* When option dialog is closed, file dialog will be grabbed again */
 
1457
  if (GTK_WINDOW (fs)->modal)
 
1458
      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 
1459
 
 
1460
  vbox = gtk_vbox_new (FALSE, 0);
 
1461
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
 
1462
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
 
1463
                     FALSE, FALSE, 0);
 
1464
  gtk_widget_show( vbox);
 
1465
  
 
1466
  label = gtk_label_new_with_mnemonic (_("_Folder name:"));
 
1467
  gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
 
1468
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
 
1469
  gtk_widget_show (label);
 
1470
 
 
1471
  /*  The directory entry widget  */
 
1472
  fs->fileop_entry = gtk_entry_new ();
 
1473
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
 
1474
  gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
 
1475
                      TRUE, TRUE, 5);
 
1476
  GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
 
1477
  gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
 
1478
  gtk_widget_show (fs->fileop_entry);
 
1479
  
 
1480
  /* buttons */
 
1481
  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
 
1482
                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
 
1483
  g_signal_connect_swapped (button, "clicked",
 
1484
                            G_CALLBACK (gtk_widget_destroy),
 
1485
                            dialog);
 
1486
 
 
1487
  gtk_widget_grab_focus (fs->fileop_entry);
 
1488
 
 
1489
  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
 
1490
                                  _("C_reate"), GTK_RESPONSE_OK);
 
1491
  gtk_widget_set_sensitive (button, FALSE);
 
1492
  g_signal_connect (button, "clicked",
 
1493
                    G_CALLBACK (gtk_file_selection_create_dir_confirmed),
 
1494
                    fs);
 
1495
  g_signal_connect (fs->fileop_entry, "changed",
 
1496
                    G_CALLBACK (gtk_file_selection_fileop_entry_changed),
 
1497
                    button);
 
1498
 
 
1499
  gtk_widget_grab_default (button);
 
1500
  
 
1501
  gtk_widget_show (dialog);
 
1502
}
 
1503
 
 
1504
static void
 
1505
gtk_file_selection_delete_file_response (GtkDialog *dialog, 
 
1506
                                         gint       response_id,
 
1507
                                         gpointer   data)
 
1508
{
 
1509
  GtkFileSelection *fs = data;
 
1510
  CompletionState *cmpl_state;
 
1511
  gchar *path;
 
1512
  gchar *full_path;
 
1513
  gchar *sys_full_path;
 
1514
  GError *error = NULL;
 
1515
  gchar *buf;
 
1516
  
 
1517
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1518
 
 
1519
  if (response_id != GTK_RESPONSE_OK)
 
1520
    {
 
1521
      gtk_widget_destroy (GTK_WIDGET (dialog));
 
1522
      return;
 
1523
    }
 
1524
 
 
1525
  cmpl_state = (CompletionState*) fs->cmpl_state;
 
1526
  path = cmpl_reference_position (cmpl_state);
 
1527
  
 
1528
  full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
 
1529
  sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
 
1530
  if (error)
 
1531
    {
 
1532
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
 
1533
        buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
 
1534
                               fs->fileop_file);
 
1535
      else
 
1536
        buf = g_strdup_printf (_("Error deleting file '%s': %s"),
 
1537
                               fs->fileop_file, error->message);
 
1538
      
 
1539
      gtk_file_selection_fileop_error (fs, buf);
 
1540
      g_error_free (error);
 
1541
      goto out;
 
1542
    }
 
1543
 
 
1544
  if (g_unlink (sys_full_path) < 0) 
 
1545
    {
 
1546
      buf = g_strdup_printf (_("Error deleting file '%s': %s"),
 
1547
                             fs->fileop_file, g_strerror (errno));
 
1548
      gtk_file_selection_fileop_error (fs, buf);
 
1549
    }
 
1550
  
 
1551
 out:
 
1552
  g_free (full_path);
 
1553
  g_free (sys_full_path);
 
1554
  
 
1555
  gtk_widget_destroy (fs->fileop_dialog);
 
1556
  gtk_file_selection_populate (fs, "", FALSE, TRUE);
 
1557
}
 
1558
 
 
1559
static void
 
1560
gtk_file_selection_delete_file (GtkWidget *widget,
 
1561
                                gpointer   data)
 
1562
{
 
1563
  GtkFileSelection *fs = data;
 
1564
  GtkWidget *dialog;
 
1565
  const gchar *filename;
 
1566
  
 
1567
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1568
 
 
1569
  if (fs->fileop_dialog)
 
1570
    return;
 
1571
 
 
1572
#ifdef G_WITH_CYGWIN
 
1573
  translate_win32_path (fs);
 
1574
#endif
 
1575
 
 
1576
  filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
 
1577
  if (strlen (filename) < 1)
 
1578
    return;
 
1579
 
 
1580
  g_free (fs->fileop_file);
 
1581
  fs->fileop_file = g_strdup (filename);
 
1582
  
 
1583
  /* main dialog */
 
1584
  fs->fileop_dialog = dialog = 
 
1585
    gtk_message_dialog_new (GTK_WINDOW (fs),
 
1586
                            GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
 
1587
                            GTK_MESSAGE_QUESTION,
 
1588
                            GTK_BUTTONS_NONE,
 
1589
                            _("Really delete file \"%s\"?"), filename);
 
1590
 
 
1591
  g_signal_connect (dialog, "destroy",
 
1592
                    G_CALLBACK (gtk_file_selection_fileop_destroy),
 
1593
                    fs);
 
1594
  gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
 
1595
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
 
1596
  
 
1597
  /* buttons */
 
1598
  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
 
1599
                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
1600
                          GTK_STOCK_DELETE, GTK_RESPONSE_OK,
 
1601
                          NULL);
 
1602
 
 
1603
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
 
1604
 
 
1605
  g_signal_connect (dialog, "response",
 
1606
                    G_CALLBACK (gtk_file_selection_delete_file_response),
 
1607
                    fs);
 
1608
  
 
1609
  gtk_widget_show (dialog);
 
1610
}
 
1611
 
 
1612
static void
 
1613
gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
 
1614
                                          gpointer   data)
 
1615
{
 
1616
  GtkFileSelection *fs = data;
 
1617
  gchar *buf;
 
1618
  const gchar *file;
 
1619
  gchar *path;
 
1620
  gchar *new_filename;
 
1621
  gchar *old_filename;
 
1622
  gchar *sys_new_filename;
 
1623
  gchar *sys_old_filename;
 
1624
  CompletionState *cmpl_state;
 
1625
  GError *error = NULL;
 
1626
  
 
1627
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1628
 
 
1629
  file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
 
1630
  cmpl_state = (CompletionState*) fs->cmpl_state;
 
1631
  path = cmpl_reference_position (cmpl_state);
 
1632
  
 
1633
  new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
 
1634
  old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
 
1635
 
 
1636
  sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
 
1637
  if (error)
 
1638
    {
 
1639
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
 
1640
        buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), new_filename);
 
1641
      else
 
1642
        buf = g_strdup_printf (_("Error renaming file to \"%s\": %s"),
 
1643
                               new_filename, error->message);
 
1644
      gtk_file_selection_fileop_error (fs, buf);
 
1645
      g_error_free (error);
 
1646
      goto out1;
 
1647
    }
 
1648
 
 
1649
  sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
 
1650
  if (error)
 
1651
    {
 
1652
      if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
 
1653
        buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), old_filename);
 
1654
      else
 
1655
        buf = g_strdup_printf (_("Error renaming file \"%s\": %s"),
 
1656
                               old_filename, error->message);
 
1657
      gtk_file_selection_fileop_error (fs, buf);
 
1658
      g_error_free (error);
 
1659
      goto out2;
 
1660
    }
 
1661
  
 
1662
  if (g_rename (sys_old_filename, sys_new_filename) < 0) 
 
1663
    {
 
1664
      buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
 
1665
                             sys_old_filename, sys_new_filename,
 
1666
                             g_strerror (errno));
 
1667
      gtk_file_selection_fileop_error (fs, buf);
 
1668
      goto out2;
 
1669
    }
 
1670
  
 
1671
  gtk_file_selection_populate (fs, "", FALSE, FALSE);
 
1672
  gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
 
1673
  
 
1674
 out2:
 
1675
  g_free (sys_old_filename);
 
1676
 
 
1677
 out1:
 
1678
  g_free (new_filename);
 
1679
  g_free (old_filename);
 
1680
  g_free (sys_new_filename);
 
1681
  
 
1682
  gtk_widget_destroy (fs->fileop_dialog);
 
1683
}
 
1684
  
 
1685
static void
 
1686
gtk_file_selection_rename_file (GtkWidget *widget,
 
1687
                                gpointer   data)
 
1688
{
 
1689
  GtkFileSelection *fs = data;
 
1690
  GtkWidget *label;
 
1691
  GtkWidget *dialog;
 
1692
  GtkWidget *vbox;
 
1693
  GtkWidget *button;
 
1694
  gchar *buf;
 
1695
  
 
1696
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1697
 
 
1698
  if (fs->fileop_dialog)
 
1699
          return;
 
1700
 
 
1701
  g_free (fs->fileop_file);
 
1702
  fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
 
1703
  if (strlen (fs->fileop_file) < 1)
 
1704
    return;
 
1705
  
 
1706
  /* main dialog */
 
1707
  fs->fileop_dialog = dialog = gtk_dialog_new ();
 
1708
  g_signal_connect (dialog, "destroy",
 
1709
                    G_CALLBACK (gtk_file_selection_fileop_destroy),
 
1710
                    fs);
 
1711
  gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
 
1712
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
 
1713
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
 
1714
 
 
1715
  /* If file dialog is grabbed, grab option dialog */
 
1716
  /* When option dialog  closed, file dialog will be grabbed again */
 
1717
  if (GTK_WINDOW (fs)->modal)
 
1718
    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 
1719
  
 
1720
  vbox = gtk_vbox_new (FALSE, 0);
 
1721
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
 
1722
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
 
1723
                      FALSE, FALSE, 0);
 
1724
  gtk_widget_show(vbox);
 
1725
  
 
1726
  buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
 
1727
  label = gtk_label_new (buf);
 
1728
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
 
1729
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
 
1730
  gtk_widget_show (label);
 
1731
  g_free (buf);
 
1732
 
 
1733
  /* New filename entry */
 
1734
  fs->fileop_entry = gtk_entry_new ();
 
1735
  gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
 
1736
                      TRUE, TRUE, 5);
 
1737
  GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
 
1738
  gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
 
1739
  gtk_widget_show (fs->fileop_entry);
 
1740
  
 
1741
  gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
 
1742
  gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
 
1743
                              0, strlen (fs->fileop_file));
 
1744
 
 
1745
  /* buttons */
 
1746
  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
 
1747
                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
 
1748
  g_signal_connect_swapped (button, "clicked",
 
1749
                            G_CALLBACK (gtk_widget_destroy),
 
1750
                            dialog);
 
1751
 
 
1752
  gtk_widget_grab_focus (fs->fileop_entry);
 
1753
 
 
1754
  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
 
1755
                                  _("_Rename"), GTK_RESPONSE_OK);
 
1756
  g_signal_connect (button, "clicked",
 
1757
                    G_CALLBACK (gtk_file_selection_rename_file_confirmed),
 
1758
                    fs);
 
1759
  g_signal_connect (fs->fileop_entry, "changed",
 
1760
                    G_CALLBACK (gtk_file_selection_fileop_entry_changed),
 
1761
                    button);
 
1762
 
 
1763
  gtk_widget_grab_default (button);
 
1764
  
 
1765
  gtk_widget_show (dialog);
 
1766
}
 
1767
 
 
1768
static gint
 
1769
gtk_file_selection_insert_text (GtkWidget   *widget,
 
1770
                                const gchar *new_text,
 
1771
                                gint         new_text_length,
 
1772
                                gint        *position,
 
1773
                                gpointer     user_data)
 
1774
{
 
1775
  gchar *filename;
 
1776
 
 
1777
  filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
 
1778
 
 
1779
  if (!filename)
 
1780
    {
 
1781
      gdk_display_beep (gtk_widget_get_display (widget));
 
1782
      g_signal_stop_emission_by_name (widget, "insert_text");
 
1783
      return FALSE;
 
1784
    }
 
1785
  
 
1786
  g_free (filename);
 
1787
  
 
1788
  return TRUE;
 
1789
}
 
1790
 
 
1791
static void
 
1792
gtk_file_selection_update_fileops (GtkFileSelection *fs)
 
1793
{
 
1794
  gboolean sensitive;
 
1795
 
 
1796
  if (!fs->selection_entry)
 
1797
    return;
 
1798
 
 
1799
  sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
 
1800
 
 
1801
  if (fs->fileop_del_file)
 
1802
    gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
 
1803
  
 
1804
  if (fs->fileop_ren_file)
 
1805
    gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
 
1806
}
 
1807
 
 
1808
static gint
 
1809
gtk_file_selection_key_press (GtkWidget   *widget,
 
1810
                              GdkEventKey *event,
 
1811
                              gpointer     user_data)
 
1812
{
 
1813
  GtkFileSelection *fs;
 
1814
  char *text;
 
1815
 
 
1816
  g_return_val_if_fail (widget != NULL, FALSE);
 
1817
  g_return_val_if_fail (event != NULL, FALSE);
 
1818
 
 
1819
  if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
 
1820
      (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
 
1821
    {
 
1822
      fs = GTK_FILE_SELECTION (user_data);
 
1823
#ifdef G_WITH_CYGWIN
 
1824
      translate_win32_path (fs);
 
1825
#endif
 
1826
      text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
 
1827
 
 
1828
      gtk_file_selection_populate (fs, text, TRUE, TRUE);
 
1829
 
 
1830
      g_free (text);
 
1831
 
 
1832
      return TRUE;
 
1833
    }
 
1834
 
 
1835
  return FALSE;
 
1836
}
 
1837
 
 
1838
static void
 
1839
gtk_file_selection_history_callback (GtkWidget *widget,
 
1840
                                     gpointer   data)
 
1841
{
 
1842
  GtkFileSelection *fs = data;
 
1843
  HistoryCallbackArg *callback_arg;
 
1844
  GList *list;
 
1845
 
 
1846
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1847
 
 
1848
  list = fs->history_list;
 
1849
  
 
1850
  while (list) {
 
1851
    callback_arg = list->data;
 
1852
    
 
1853
    if (callback_arg->menu_item == widget)
 
1854
      {
 
1855
        gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
 
1856
        break;
 
1857
      }
 
1858
    
 
1859
    list = list->next;
 
1860
  }
 
1861
}
 
1862
 
 
1863
static void 
 
1864
gtk_file_selection_update_history_menu (GtkFileSelection *fs,
 
1865
                                        gchar            *current_directory)
 
1866
{
 
1867
  HistoryCallbackArg *callback_arg;
 
1868
  GtkWidget *menu_item;
 
1869
  GList *list;
 
1870
  gchar *current_dir;
 
1871
  gint dir_len;
 
1872
  gint i;
 
1873
  
 
1874
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
1875
  g_return_if_fail (current_directory != NULL);
 
1876
  
 
1877
  list = fs->history_list;
 
1878
 
 
1879
  if (fs->history_menu) 
 
1880
    {
 
1881
      while (list) {
 
1882
        callback_arg = list->data;
 
1883
        g_free (callback_arg->directory);
 
1884
        g_free (callback_arg);
 
1885
        list = list->next;
 
1886
      }
 
1887
      g_list_free (fs->history_list);
 
1888
      fs->history_list = NULL;
 
1889
      
 
1890
      gtk_widget_destroy (fs->history_menu);
 
1891
    }
 
1892
  
 
1893
  fs->history_menu = gtk_menu_new ();
 
1894
 
 
1895
  current_dir = g_strdup (current_directory);
 
1896
 
 
1897
  dir_len = strlen (current_dir);
 
1898
 
 
1899
  for (i = dir_len; i >= 0; i--)
 
1900
    {
 
1901
      /* the i == dir_len is to catch the full path for the first 
 
1902
       * entry. */
 
1903
      if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
 
1904
        {
 
1905
          /* another small hack to catch the full path */
 
1906
          if (i != dir_len) 
 
1907
                  current_dir[i + 1] = '\0';
 
1908
#ifdef G_WITH_CYGWIN
 
1909
          if (!strcmp (current_dir, "//"))
 
1910
            continue;
 
1911
#endif
 
1912
          menu_item = gtk_menu_item_new_with_label (current_dir);
 
1913
          
 
1914
          callback_arg = g_new (HistoryCallbackArg, 1);
 
1915
          callback_arg->menu_item = menu_item;
 
1916
          
 
1917
          /* since the autocompletion gets confused if you don't 
 
1918
           * supply a trailing '/' on a dir entry, set the full
 
1919
           * (current) path to "" which just refreshes the filesel */
 
1920
          if (dir_len == i)
 
1921
            {
 
1922
              callback_arg->directory = g_strdup ("");
 
1923
            }
 
1924
          else
 
1925
            {
 
1926
              callback_arg->directory = g_strdup (current_dir);
 
1927
            }
 
1928
          
 
1929
          fs->history_list = g_list_append (fs->history_list, callback_arg);
 
1930
          
 
1931
          g_signal_connect (menu_item, "activate",
 
1932
                            G_CALLBACK (gtk_file_selection_history_callback),
 
1933
                            fs);
 
1934
          gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
 
1935
          gtk_widget_show (menu_item);
 
1936
        }
 
1937
    }
 
1938
 
 
1939
  gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
 
1940
                            fs->history_menu);
 
1941
  g_free (current_dir);
 
1942
}
 
1943
 
 
1944
static gchar *
 
1945
get_real_filename (gchar    *filename,
 
1946
                   gboolean  free_old)
 
1947
{
 
1948
#ifdef G_WITH_CYGWIN
 
1949
  /* Check to see if the selection was a drive selector */
 
1950
  if (isalpha (filename[0]) && (filename[1] == ':'))
 
1951
    {
 
1952
      gchar temp_filename[MAX_PATH];
 
1953
      int len;
 
1954
 
 
1955
      cygwin_conv_to_posix_path (filename, temp_filename);
 
1956
 
 
1957
      /* we need trailing '/'. */
 
1958
      len = strlen (temp_filename);
 
1959
      if (len > 0 && temp_filename[len-1] != '/')
 
1960
        {
 
1961
          temp_filename[len]   = '/';
 
1962
          temp_filename[len+1] = '\0';
 
1963
        }
 
1964
      
 
1965
      if (free_old)
 
1966
        g_free (filename);
 
1967
 
 
1968
      return g_strdup (temp_filename);
 
1969
    }
 
1970
#endif /* G_WITH_CYGWIN */
 
1971
  return filename;
 
1972
}
 
1973
 
 
1974
static void
 
1975
gtk_file_selection_file_activate (GtkTreeView       *tree_view,
 
1976
                                  GtkTreePath       *path,
 
1977
                                  GtkTreeViewColumn *column,
 
1978
                                  gpointer           user_data)
 
1979
{
 
1980
  GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
 
1981
  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
 
1982
  GtkTreeIter iter;  
 
1983
  gchar *filename;
 
1984
  
 
1985
  gtk_tree_model_get_iter (model, &iter, path);
 
1986
  gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
 
1987
  filename = get_real_filename (filename, TRUE);
 
1988
  gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
 
1989
  gtk_button_clicked (GTK_BUTTON (fs->ok_button));
 
1990
 
 
1991
  g_free (filename);
 
1992
}
 
1993
 
 
1994
static void
 
1995
gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
 
1996
                                 GtkTreePath       *path,
 
1997
                                 GtkTreeViewColumn *column,
 
1998
                                 gpointer           user_data)
 
1999
{
 
2000
  GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
 
2001
  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
 
2002
  GtkTreeIter iter;
 
2003
  gchar *filename;
 
2004
 
 
2005
  gtk_tree_model_get_iter (model, &iter, path);
 
2006
  gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
 
2007
  filename = get_real_filename (filename, TRUE);
 
2008
  gtk_file_selection_populate (fs, filename, FALSE, FALSE);
 
2009
  g_free (filename);
 
2010
}
 
2011
 
 
2012
#ifdef G_PLATFORM_WIN32
 
2013
 
 
2014
static void
 
2015
win32_gtk_add_drives_to_dir_list (GtkListStore *model)
 
2016
{
 
2017
  gchar *textPtr;
 
2018
  gchar buffer[128];
 
2019
  char formatBuffer[128];
 
2020
  GtkTreeIter iter;
 
2021
 
 
2022
  /* Get the drives string */
 
2023
  GetLogicalDriveStrings (sizeof (buffer), buffer);
 
2024
 
 
2025
  /* Add the drives as necessary */
 
2026
  textPtr = buffer;
 
2027
  while (*textPtr != '\0')
 
2028
    {
 
2029
      /* Ignore floppies (?) */
 
2030
      if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
 
2031
        {
 
2032
          /* Build the actual displayable string */
 
2033
          g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
 
2034
 
 
2035
          /* Add to the list */
 
2036
          gtk_list_store_append (model, &iter);
 
2037
          gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
 
2038
        }
 
2039
      textPtr += (strlen (textPtr) + 1);
 
2040
    }
 
2041
}
 
2042
#endif
 
2043
 
 
2044
static gchar *
 
2045
escape_underscores (const gchar *str)
 
2046
{
 
2047
  GString *result = g_string_new (NULL);
 
2048
  while (*str)
 
2049
    {
 
2050
      if (*str == '_')
 
2051
        g_string_append_c (result, '_');
 
2052
 
 
2053
      g_string_append_c (result, *str);
 
2054
      str++;
 
2055
    }
 
2056
 
 
2057
  return g_string_free (result, FALSE);
 
2058
}
 
2059
 
 
2060
static void
 
2061
gtk_file_selection_populate (GtkFileSelection *fs,
 
2062
                             gchar            *rel_path,
 
2063
                             gboolean          try_complete,
 
2064
                             gboolean          reset_entry)
 
2065
{
 
2066
  CompletionState *cmpl_state;
 
2067
  PossibleCompletion* poss;
 
2068
  GtkTreeIter iter;
 
2069
  GtkListStore *dir_model;
 
2070
  GtkListStore *file_model;
 
2071
  gchar* filename;
 
2072
  gchar* rem_path = rel_path;
 
2073
  gchar* sel_text;
 
2074
  gint did_recurse = FALSE;
 
2075
  gint possible_count = 0;
 
2076
  
 
2077
  g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
2078
 
 
2079
  cmpl_state = (CompletionState*) fs->cmpl_state;
 
2080
  poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
 
2081
 
 
2082
  if (!cmpl_state_okay (cmpl_state))
 
2083
    {
 
2084
      /* Something went wrong. */
 
2085
      gtk_file_selection_abort (fs);
 
2086
      return;
 
2087
    }
 
2088
 
 
2089
  g_assert (cmpl_state->reference_dir);
 
2090
 
 
2091
  dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
 
2092
  file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
 
2093
 
 
2094
  gtk_list_store_clear (dir_model);
 
2095
  gtk_list_store_clear (file_model);
 
2096
 
 
2097
  /* Set the dir list to include ./ and ../ */
 
2098
  gtk_list_store_append (dir_model, &iter);
 
2099
  gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
 
2100
  gtk_list_store_append (dir_model, &iter);
 
2101
  gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
 
2102
 
 
2103
  while (poss)
 
2104
    {
 
2105
      if (cmpl_is_a_completion (poss))
 
2106
        {
 
2107
          possible_count += 1;
 
2108
 
 
2109
          filename = cmpl_this_completion (poss);
 
2110
 
 
2111
          if (cmpl_is_directory (poss))
 
2112
            {
 
2113
              if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
 
2114
                  strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
 
2115
                {
 
2116
                  gtk_list_store_append (dir_model, &iter);
 
2117
                  gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
 
2118
                }
 
2119
            }
 
2120
          else
 
2121
            {
 
2122
              gtk_list_store_append (file_model, &iter);
 
2123
              gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
 
2124
            }
 
2125
        }
 
2126
 
 
2127
      poss = cmpl_next_completion (cmpl_state);
 
2128
    }
 
2129
 
 
2130
#ifdef G_PLATFORM_WIN32
 
2131
  /* For Windows, add drives as potential selections */
 
2132
  win32_gtk_add_drives_to_dir_list (dir_model);
 
2133
#endif
 
2134
 
 
2135
  /* File lists are set. */
 
2136
 
 
2137
  g_assert (cmpl_state->reference_dir);
 
2138
 
 
2139
  if (try_complete)
 
2140
    {
 
2141
 
 
2142
      /* User is trying to complete filenames, so advance the user's input
 
2143
       * string to the updated_text, which is the common leading substring
 
2144
       * of all possible completions, and if its a directory attempt
 
2145
       * attempt completions in it. */
 
2146
 
 
2147
      if (cmpl_updated_text (cmpl_state)[0])
 
2148
        {
 
2149
 
 
2150
          if (cmpl_updated_dir (cmpl_state))
 
2151
            {
 
2152
              gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
 
2153
 
 
2154
              did_recurse = TRUE;
 
2155
 
 
2156
              gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
 
2157
 
 
2158
              g_free (dir_name);
 
2159
            }
 
2160
          else
 
2161
            {
 
2162
              if (fs->selection_entry)
 
2163
                      gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
 
2164
                                          cmpl_updated_text (cmpl_state));
 
2165
            }
 
2166
        }
 
2167
      else
 
2168
        {
 
2169
          if (fs->selection_entry)
 
2170
            gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
 
2171
        }
 
2172
    }
 
2173
  else if (reset_entry)
 
2174
    {
 
2175
      if (fs->selection_entry)
 
2176
        gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
 
2177
    }
 
2178
 
 
2179
  if (!did_recurse)
 
2180
    {
 
2181
      if (fs->selection_entry && try_complete)
 
2182
        gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1);
 
2183
 
 
2184
      if (fs->selection_entry)
 
2185
        {
 
2186
          char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
 
2187
          sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
 
2188
          g_free (escaped);
 
2189
 
 
2190
          gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
 
2191
          g_free (sel_text);
 
2192
        }
 
2193
 
 
2194
      if (fs->history_pulldown) 
 
2195
        {
 
2196
          gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
 
2197
        }
 
2198
      
 
2199
    }
 
2200
}
 
2201
 
 
2202
static void
 
2203
gtk_file_selection_abort (GtkFileSelection *fs)
 
2204
{
 
2205
  gchar err_buf[256];
 
2206
 
 
2207
  g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
 
2208
 
 
2209
  /*  BEEP gdk_beep();  */
 
2210
 
 
2211
  if (fs->selection_entry)
 
2212
    gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
 
2213
}
 
2214
 
 
2215
/**
 
2216
 * gtk_file_selection_set_select_multiple:
 
2217
 * @filesel: a #GtkFileSelection
 
2218
 * @select_multiple: whether or not the user is allowed to select multiple
 
2219
 * files in the file list.
 
2220
 *
 
2221
 * Sets whether the user is allowed to select multiple files in the file list.
 
2222
 * Use gtk_file_selection_get_selections () to get the list of selected files.
 
2223
 **/
 
2224
void
 
2225
gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
 
2226
                                        gboolean          select_multiple)
 
2227
{
 
2228
  GtkTreeSelection *sel;
 
2229
  GtkSelectionMode mode;
 
2230
 
 
2231
  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
 
2232
 
 
2233
  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
 
2234
 
 
2235
  mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
 
2236
 
 
2237
  if (mode != gtk_tree_selection_get_mode (sel))
 
2238
    {
 
2239
      gtk_tree_selection_set_mode (sel, mode);
 
2240
 
 
2241
      g_object_notify (G_OBJECT (filesel), "select-multiple");
 
2242
    }
 
2243
}
 
2244
 
 
2245
/**
 
2246
 * gtk_file_selection_get_select_multiple:
 
2247
 * @filesel: a #GtkFileSelection
 
2248
 *
 
2249
 * Determines whether or not the user is allowed to select multiple files in
 
2250
 * the file list. See gtk_file_selection_set_select_multiple().
 
2251
 *
 
2252
 * Return value: %TRUE if the user is allowed to select multiple files in the
 
2253
 * file list
 
2254
 **/
 
2255
gboolean
 
2256
gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
 
2257
{
 
2258
  GtkTreeSelection *sel;
 
2259
 
 
2260
  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
 
2261
 
 
2262
  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
 
2263
  return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
 
2264
}
 
2265
 
 
2266
static void
 
2267
multiple_changed_foreach (GtkTreeModel *model,
 
2268
                          GtkTreePath  *path,
 
2269
                          GtkTreeIter  *iter,
 
2270
                          gpointer      data)
 
2271
{
 
2272
  GPtrArray *names = data;
 
2273
  gchar *filename;
 
2274
 
 
2275
  gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
 
2276
 
 
2277
  g_ptr_array_add (names, filename);
 
2278
}
 
2279
 
 
2280
static void
 
2281
free_selected_names (GPtrArray *names)
 
2282
{
 
2283
  gint i;
 
2284
 
 
2285
  for (i = 0; i < names->len; i++)
 
2286
    g_free (g_ptr_array_index (names, i));
 
2287
 
 
2288
  g_ptr_array_free (names, TRUE);
 
2289
}
 
2290
 
 
2291
static void
 
2292
gtk_file_selection_file_changed (GtkTreeSelection *selection,
 
2293
                                 gpointer          user_data)
 
2294
{
 
2295
  GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
 
2296
  GPtrArray *new_names;
 
2297
  gchar *filename;
 
2298
  const gchar *entry;
 
2299
  gint index = -1;
 
2300
 
 
2301
  new_names = g_ptr_array_sized_new (8);
 
2302
 
 
2303
  gtk_tree_selection_selected_foreach (selection,
 
2304
                                       multiple_changed_foreach,
 
2305
                                       new_names);
 
2306
 
 
2307
  /* nothing selected */
 
2308
  if (new_names->len == 0)
 
2309
    {
 
2310
      g_ptr_array_free (new_names, TRUE);
 
2311
 
 
2312
      if (fs->selected_names != NULL)
 
2313
        {
 
2314
          free_selected_names (fs->selected_names);
 
2315
          fs->selected_names = NULL;
 
2316
        }
 
2317
 
 
2318
      goto maybe_clear_entry;
 
2319
    }
 
2320
 
 
2321
  if (new_names->len != 1)
 
2322
    {
 
2323
      GPtrArray *old_names = fs->selected_names;
 
2324
 
 
2325
      if (old_names != NULL)
 
2326
        {
 
2327
          /* A common case is selecting a range of files from top to bottom,
 
2328
           * so quickly check for that to avoid looping over the entire list
 
2329
           */
 
2330
          if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
 
2331
                                      g_ptr_array_index (new_names, new_names->len - 1)) != 0)
 
2332
            index = new_names->len - 1;
 
2333
          else
 
2334
            {
 
2335
              gint i = 0, j = 0, cmp;
 
2336
 
 
2337
              /* do a quick diff, stopping at the first file not in the
 
2338
               * old list
 
2339
               */
 
2340
              while (i < old_names->len && j < new_names->len)
 
2341
                {
 
2342
                  cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
 
2343
                                                g_ptr_array_index (new_names, j));
 
2344
                  if (cmp < 0)
 
2345
                    {
 
2346
                      i++;
 
2347
                    }
 
2348
                  else if (cmp == 0)
 
2349
                    {
 
2350
                      i++;
 
2351
                      j++;
 
2352
                    }
 
2353
                  else if (cmp > 0)
 
2354
                    {
 
2355
                      index = j;
 
2356
                      break;
 
2357
                    }
 
2358
                }
 
2359
 
 
2360
              /* we ran off the end of the old list */
 
2361
              if (index == -1 && i < new_names->len)
 
2362
                index = j;
 
2363
            }
 
2364
        }
 
2365
      else
 
2366
        {
 
2367
          /* A phantom anchor still exists at the point where the last item
 
2368
           * was selected, which is used for subsequent range selections.
 
2369
           * So search up from there.
 
2370
           */
 
2371
          if (fs->last_selected &&
 
2372
              compare_utf8_filenames (fs->last_selected,
 
2373
                                      g_ptr_array_index (new_names, 0)) == 0)
 
2374
            index = new_names->len - 1;
 
2375
          else
 
2376
            index = 0;
 
2377
        }
 
2378
    }
 
2379
  else
 
2380
    index = 0;
 
2381
 
 
2382
  if (fs->selected_names != NULL)
 
2383
    free_selected_names (fs->selected_names);
 
2384
 
 
2385
  fs->selected_names = new_names;
 
2386
 
 
2387
  if (index != -1)
 
2388
    {
 
2389
      if (fs->last_selected != NULL)
 
2390
        g_free (fs->last_selected);
 
2391
 
 
2392
      fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
 
2393
      filename = get_real_filename (fs->last_selected, FALSE);
 
2394
 
 
2395
      gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
 
2396
 
 
2397
      if (filename != fs->last_selected)
 
2398
        g_free (filename);
 
2399
      
 
2400
      return;
 
2401
    }
 
2402
  
 
2403
maybe_clear_entry:
 
2404
 
 
2405
  entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
 
2406
  if ((entry != NULL) && (fs->last_selected != NULL) &&
 
2407
      (compare_utf8_filenames (entry, fs->last_selected) == 0))
 
2408
    gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
 
2409
}
 
2410
 
 
2411
/**
 
2412
 * gtk_file_selection_get_selections:
 
2413
 * @filesel: a #GtkFileSelection
 
2414
 *
 
2415
 * Retrieves the list of file selections the user has made in the dialog box.
 
2416
 * This function is intended for use when the user can select multiple files
 
2417
 * in the file list. 
 
2418
 *
 
2419
 * The filenames are in the GLib file name encoding. To convert to
 
2420
 * UTF-8, call g_filename_to_utf8() on each string.
 
2421
 *
 
2422
 * Return value: a newly-allocated %NULL-terminated array of strings. Use
 
2423
 * g_strfreev() to free it.
 
2424
 **/
 
2425
gchar **
 
2426
gtk_file_selection_get_selections (GtkFileSelection *filesel)
 
2427
{
 
2428
  GPtrArray *names;
 
2429
  gchar **selections;
 
2430
  gchar *filename, *dirname;
 
2431
  gchar *current, *buf;
 
2432
  gint i, count;
 
2433
  gboolean unselected_entry;
 
2434
 
 
2435
  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
 
2436
 
 
2437
  filename = g_strdup (gtk_file_selection_get_filename (filesel));
 
2438
 
 
2439
  if (strlen (filename) == 0)
 
2440
    {
 
2441
      g_free (filename);
 
2442
      return NULL;
 
2443
    }
 
2444
 
 
2445
  names = filesel->selected_names;
 
2446
 
 
2447
  if (names != NULL)
 
2448
    selections = g_new (gchar *, names->len + 2);
 
2449
  else
 
2450
    selections = g_new (gchar *, 2);
 
2451
 
 
2452
  count = 0;
 
2453
  unselected_entry = TRUE;
 
2454
 
 
2455
  if (names != NULL)
 
2456
    {
 
2457
      dirname = g_path_get_dirname (filename);
 
2458
 
 
2459
      if ((names->len >= 1) && 
 
2460
          (strcmp (gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)), "") == 0))
 
2461
        { /* multiple files are selected and last selection was removed via ctrl click */
 
2462
          g_free (dirname);
 
2463
          dirname = g_strdup (filename); /* as gtk_file_selection_get_filename returns dir 
 
2464
                                            if no file is selected */
 
2465
          unselected_entry = FALSE;
 
2466
        }
 
2467
 
 
2468
      for (i = 0; i < names->len; i++)
 
2469
        {
 
2470
          buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
 
2471
                                      NULL, NULL, NULL);
 
2472
          current = g_build_filename (dirname, buf, NULL);
 
2473
          g_free (buf);
 
2474
 
 
2475
          selections[count++] = current;
 
2476
 
 
2477
          if (unselected_entry && compare_sys_filenames (current, filename) == 0)
 
2478
            unselected_entry = FALSE;
 
2479
        }
 
2480
 
 
2481
      g_free (dirname);
 
2482
    }
 
2483
 
 
2484
  if (unselected_entry)
 
2485
    selections[count++] = filename;
 
2486
  else
 
2487
    g_free (filename);
 
2488
 
 
2489
  selections[count] = NULL;
 
2490
 
 
2491
  return selections;
 
2492
}
 
2493
 
 
2494
/**********************************************************************/
 
2495
/*                        External Interface                          */
 
2496
/**********************************************************************/
 
2497
 
 
2498
/* The four completion state selectors
 
2499
 */
 
2500
static gchar*
 
2501
cmpl_updated_text (CompletionState *cmpl_state)
 
2502
{
 
2503
  return cmpl_state->updated_text;
 
2504
}
 
2505
 
 
2506
static gboolean
 
2507
cmpl_updated_dir (CompletionState *cmpl_state)
 
2508
{
 
2509
  return cmpl_state->re_complete;
 
2510
}
 
2511
 
 
2512
static gchar*
 
2513
cmpl_reference_position (CompletionState *cmpl_state)
 
2514
{
 
2515
  return cmpl_state->reference_dir->fullname;
 
2516
}
 
2517
 
 
2518
#if 0
 
2519
/* This doesn't work currently and would require changes
 
2520
 * to fnmatch.c to get working.
 
2521
 */
 
2522
static gint
 
2523
cmpl_last_valid_char (CompletionState *cmpl_state)
 
2524
{
 
2525
  return cmpl_state->last_valid_char;
 
2526
}
 
2527
#endif
 
2528
 
 
2529
static gchar*
 
2530
cmpl_completion_fullname (const gchar     *text,
 
2531
                          CompletionState *cmpl_state)
 
2532
{
 
2533
  if (!cmpl_state_okay (cmpl_state))
 
2534
    {
 
2535
      return g_strdup ("");
 
2536
    }
 
2537
  else if (g_path_is_absolute (text))
 
2538
    {
 
2539
      return g_strdup (text);
 
2540
    }
 
2541
#ifdef HAVE_PWD_H
 
2542
  else if (text[0] == '~')
 
2543
    {
 
2544
      CompletionDir* dir;
 
2545
      char* slash;
 
2546
 
 
2547
      dir = open_user_dir (text, cmpl_state);
 
2548
 
 
2549
      if (dir)
 
2550
        {
 
2551
          slash = strchr (text, G_DIR_SEPARATOR);
 
2552
          
 
2553
          /* slash may be NULL, that works too */
 
2554
          return g_strconcat (dir->fullname, slash, NULL);
 
2555
        }
 
2556
    }
 
2557
#endif
 
2558
  
 
2559
  return g_build_filename (cmpl_state->reference_dir->fullname,
 
2560
                           text,
 
2561
                           NULL);
 
2562
}
 
2563
 
 
2564
/* The three completion selectors
 
2565
 */
 
2566
static gchar*
 
2567
cmpl_this_completion (PossibleCompletion* pc)
 
2568
{
 
2569
  return pc->text;
 
2570
}
 
2571
 
 
2572
static gboolean
 
2573
cmpl_is_directory (PossibleCompletion* pc)
 
2574
{
 
2575
  return pc->is_directory;
 
2576
}
 
2577
 
 
2578
static gint
 
2579
cmpl_is_a_completion (PossibleCompletion* pc)
 
2580
{
 
2581
  return pc->is_a_completion;
 
2582
}
 
2583
 
 
2584
/**********************************************************************/
 
2585
/*                       Construction, deletion                       */
 
2586
/**********************************************************************/
 
2587
 
 
2588
/* Get the nearest parent of the current directory for which
 
2589
 * we can convert the filename into UTF-8. With paranoia.
 
2590
 * Returns "." when all goes wrong.
 
2591
 */
 
2592
static gchar *
 
2593
get_current_dir_utf8 (void)
 
2594
{
 
2595
  gchar *dir = g_get_current_dir ();
 
2596
  gchar *dir_utf8 = NULL;
 
2597
 
 
2598
  while (TRUE)
 
2599
    {
 
2600
      gchar *last_slash;
 
2601
 
 
2602
      dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL);
 
2603
      if (dir_utf8)
 
2604
        break;
 
2605
 
 
2606
      last_slash = strrchr (dir, G_DIR_SEPARATOR);
 
2607
      if (!last_slash)          /* g_get_current_dir() wasn't absolute! */
 
2608
        break;
 
2609
 
 
2610
      if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */
 
2611
        {
 
2612
          if (last_slash[1] == '\0') /* Root misencoded! */
 
2613
            break;
 
2614
          else
 
2615
            last_slash[1] = '\0';
 
2616
        }
 
2617
      else
 
2618
        last_slash[0] = '\0';
 
2619
      
 
2620
      g_assert (last_slash);
 
2621
    }
 
2622
 
 
2623
  g_free (dir);
 
2624
  
 
2625
  return dir_utf8 ? dir_utf8 : g_strdup (".");
 
2626
}
 
2627
 
 
2628
static CompletionState*
 
2629
cmpl_init_state (void)
 
2630
{
 
2631
  gchar *utf8_cwd;
 
2632
  CompletionState *new_state;
 
2633
  gint tries = 0;
 
2634
 
 
2635
  new_state = g_new (CompletionState, 1);
 
2636
 
 
2637
  utf8_cwd = get_current_dir_utf8 ();
 
2638
 
 
2639
tryagain:
 
2640
  tries++;
 
2641
  new_state->reference_dir = NULL;
 
2642
  new_state->completion_dir = NULL;
 
2643
  new_state->active_completion_dir = NULL;
 
2644
  new_state->directory_storage = NULL;
 
2645
  new_state->directory_sent_storage = NULL;
 
2646
  new_state->last_valid_char = 0;
 
2647
  new_state->updated_text = g_new (gchar, MAXPATHLEN);
 
2648
  new_state->updated_text_alloc = MAXPATHLEN;
 
2649
  new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
 
2650
  new_state->the_completion.text_alloc = MAXPATHLEN;
 
2651
  new_state->user_dir_name_buffer = NULL;
 
2652
  new_state->user_directories = NULL;
 
2653
 
 
2654
  new_state->reference_dir = open_dir (utf8_cwd, new_state);
 
2655
 
 
2656
  if (!new_state->reference_dir)
 
2657
    {
 
2658
      /* Directories changing from underneath us, grumble */
 
2659
      strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
 
2660
      if (tries < 2)
 
2661
        goto tryagain;
 
2662
    }
 
2663
 
 
2664
  g_free (utf8_cwd);
 
2665
  return new_state;
 
2666
}
 
2667
 
 
2668
static void
 
2669
cmpl_free_dir_list (GList* dp0)
 
2670
{
 
2671
  GList *dp = dp0;
 
2672
 
 
2673
  while (dp)
 
2674
    {
 
2675
      free_dir (dp->data);
 
2676
      dp = dp->next;
 
2677
    }
 
2678
 
 
2679
  g_list_free (dp0);
 
2680
}
 
2681
 
 
2682
static void
 
2683
cmpl_free_dir_sent_list (GList* dp0)
 
2684
{
 
2685
  GList *dp = dp0;
 
2686
 
 
2687
  while (dp)
 
2688
    {
 
2689
      free_dir_sent (dp->data);
 
2690
      dp = dp->next;
 
2691
    }
 
2692
 
 
2693
  g_list_free (dp0);
 
2694
}
 
2695
 
 
2696
static void
 
2697
cmpl_free_state (CompletionState* cmpl_state)
 
2698
{
 
2699
  g_return_if_fail (cmpl_state != NULL);
 
2700
 
 
2701
  cmpl_free_dir_list (cmpl_state->directory_storage);
 
2702
  cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
 
2703
 
 
2704
  if (cmpl_state->user_dir_name_buffer)
 
2705
    g_free (cmpl_state->user_dir_name_buffer);
 
2706
  if (cmpl_state->user_directories)
 
2707
    g_free (cmpl_state->user_directories);
 
2708
  if (cmpl_state->the_completion.text)
 
2709
    g_free (cmpl_state->the_completion.text);
 
2710
  if (cmpl_state->updated_text)
 
2711
    g_free (cmpl_state->updated_text);
 
2712
 
 
2713
  g_free (cmpl_state);
 
2714
}
 
2715
 
 
2716
static void
 
2717
free_dir (CompletionDir* dir)
 
2718
{
 
2719
  g_free (dir->cmpl_text);
 
2720
  g_free (dir->fullname);
 
2721
  g_free (dir);
 
2722
}
 
2723
 
 
2724
static void
 
2725
free_dir_sent (CompletionDirSent* sent)
 
2726
{
 
2727
  gint i;
 
2728
  for (i = 0; i < sent->entry_count; i++)
 
2729
    {
 
2730
      g_free (sent->entries[i].entry_name);
 
2731
      g_free (sent->entries[i].sort_key);
 
2732
    }
 
2733
  g_free (sent->entries);
 
2734
  g_free (sent);
 
2735
}
 
2736
 
 
2737
static void
 
2738
prune_memory_usage (CompletionState *cmpl_state)
 
2739
{
 
2740
  GList* cdsl = cmpl_state->directory_sent_storage;
 
2741
  GList* cdl = cmpl_state->directory_storage;
 
2742
  GList* cdl0 = cdl;
 
2743
  gint len = 0;
 
2744
 
 
2745
  for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
 
2746
    cdsl = cdsl->next;
 
2747
 
 
2748
  if (cdsl)
 
2749
    {
 
2750
      cmpl_free_dir_sent_list (cdsl->next);
 
2751
      cdsl->next = NULL;
 
2752
    }
 
2753
 
 
2754
  cmpl_state->directory_storage = NULL;
 
2755
  while (cdl)
 
2756
    {
 
2757
      if (cdl->data == cmpl_state->reference_dir)
 
2758
        cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
 
2759
      else
 
2760
        free_dir (cdl->data);
 
2761
      cdl = cdl->next;
 
2762
    }
 
2763
 
 
2764
  g_list_free (cdl0);
 
2765
}
 
2766
 
 
2767
/**********************************************************************/
 
2768
/*                        The main entrances.                         */
 
2769
/**********************************************************************/
 
2770
 
 
2771
static PossibleCompletion*
 
2772
cmpl_completion_matches (gchar           *text_to_complete,
 
2773
                         gchar          **remaining_text,
 
2774
                         CompletionState *cmpl_state)
 
2775
{
 
2776
#ifdef HAVE_PWD_H
 
2777
  gchar* first_slash;
 
2778
#endif
 
2779
  PossibleCompletion *poss;
 
2780
 
 
2781
  prune_memory_usage (cmpl_state);
 
2782
 
 
2783
  g_assert (text_to_complete != NULL);
 
2784
 
 
2785
  cmpl_state->user_completion_index = -1;
 
2786
  cmpl_state->last_completion_text = text_to_complete;
 
2787
  cmpl_state->the_completion.text[0] = 0;
 
2788
  cmpl_state->last_valid_char = 0;
 
2789
  cmpl_state->updated_text_len = -1;
 
2790
  cmpl_state->updated_text[0] = 0;
 
2791
  cmpl_state->re_complete = FALSE;
 
2792
 
 
2793
#ifdef HAVE_PWD_H
 
2794
  first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
 
2795
 
 
2796
  if (text_to_complete[0] == '~' && !first_slash)
 
2797
    {
 
2798
      /* Text starts with ~ and there is no slash, show all the
 
2799
       * home directory completions.
 
2800
       */
 
2801
      poss = attempt_homedir_completion (text_to_complete, cmpl_state);
 
2802
 
 
2803
      update_cmpl (poss, cmpl_state);
 
2804
 
 
2805
      return poss;
 
2806
    }
 
2807
#endif
 
2808
  cmpl_state->reference_dir =
 
2809
    open_ref_dir (text_to_complete, remaining_text, cmpl_state);
 
2810
 
 
2811
  if (!cmpl_state->reference_dir)
 
2812
    return NULL;
 
2813
 
 
2814
  cmpl_state->completion_dir =
 
2815
    find_completion_dir (*remaining_text, remaining_text, cmpl_state);
 
2816
 
 
2817
  cmpl_state->last_valid_char = *remaining_text - text_to_complete;
 
2818
 
 
2819
  if (!cmpl_state->completion_dir)
 
2820
    return NULL;
 
2821
 
 
2822
  cmpl_state->completion_dir->cmpl_index = -1;
 
2823
  cmpl_state->completion_dir->cmpl_parent = NULL;
 
2824
  cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
 
2825
 
 
2826
  cmpl_state->active_completion_dir = cmpl_state->completion_dir;
 
2827
 
 
2828
  cmpl_state->reference_dir = cmpl_state->completion_dir;
 
2829
 
 
2830
  poss = attempt_file_completion (cmpl_state);
 
2831
 
 
2832
  update_cmpl (poss, cmpl_state);
 
2833
 
 
2834
  return poss;
 
2835
}
 
2836
 
 
2837
static PossibleCompletion*
 
2838
cmpl_next_completion (CompletionState* cmpl_state)
 
2839
{
 
2840
  PossibleCompletion* poss = NULL;
 
2841
 
 
2842
  cmpl_state->the_completion.text[0] = 0;
 
2843
 
 
2844
#ifdef HAVE_PWD_H
 
2845
  if (cmpl_state->user_completion_index >= 0)
 
2846
    poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
 
2847
  else
 
2848
    poss = attempt_file_completion (cmpl_state);
 
2849
#else
 
2850
  poss = attempt_file_completion (cmpl_state);
 
2851
#endif
 
2852
 
 
2853
  update_cmpl (poss, cmpl_state);
 
2854
 
 
2855
  return poss;
 
2856
}
 
2857
 
 
2858
/**********************************************************************/
 
2859
/*                       Directory Operations                         */
 
2860
/**********************************************************************/
 
2861
 
 
2862
/* Open the directory where completion will begin from, if possible. */
 
2863
static CompletionDir*
 
2864
open_ref_dir (gchar           *text_to_complete,
 
2865
              gchar          **remaining_text,
 
2866
              CompletionState *cmpl_state)
 
2867
{
 
2868
  gchar* first_slash;
 
2869
  CompletionDir *new_dir;
 
2870
 
 
2871
  first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
 
2872
 
 
2873
#ifdef G_WITH_CYGWIN
 
2874
  if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
 
2875
    {
 
2876
      char root_dir[5];
 
2877
      g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
 
2878
 
 
2879
      new_dir = open_dir (root_dir, cmpl_state);
 
2880
 
 
2881
      if (new_dir) {
 
2882
        *remaining_text = text_to_complete + 4;
 
2883
      }
 
2884
    }
 
2885
#else
 
2886
  if (FALSE)
 
2887
    ;
 
2888
#endif
 
2889
#ifdef HAVE_PWD_H
 
2890
  else if (text_to_complete[0] == '~')
 
2891
    {
 
2892
      new_dir = open_user_dir (text_to_complete, cmpl_state);
 
2893
 
 
2894
      if (new_dir)
 
2895
        {
 
2896
          if (first_slash)
 
2897
            *remaining_text = first_slash + 1;
 
2898
          else
 
2899
            *remaining_text = text_to_complete + strlen (text_to_complete);
 
2900
        }
 
2901
      else
 
2902
        {
 
2903
          return NULL;
 
2904
        }
 
2905
    }
 
2906
#endif
 
2907
  else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
 
2908
    {
 
2909
      gchar *tmp = g_strdup (text_to_complete);
 
2910
      gchar *p;
 
2911
 
 
2912
      p = tmp;
 
2913
      while (*p && *p != '*' && *p != '?')
 
2914
        p++;
 
2915
 
 
2916
      *p = '\0';
 
2917
      p = strrchr (tmp, G_DIR_SEPARATOR);
 
2918
      if (p)
 
2919
        {
 
2920
          if (p + 1 == g_path_skip_root (tmp))
 
2921
            p++;
 
2922
      
 
2923
          *p = '\0';
 
2924
          new_dir = open_dir (tmp, cmpl_state);
 
2925
 
 
2926
          if (new_dir)
 
2927
            *remaining_text = text_to_complete + 
 
2928
              ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
 
2929
        }
 
2930
      else
 
2931
        {
 
2932
          /* If no possible candidates, use the cwd */
 
2933
          gchar *utf8_curdir = get_current_dir_utf8 ();
 
2934
 
 
2935
          new_dir = open_dir (utf8_curdir, cmpl_state);
 
2936
 
 
2937
          if (new_dir)
 
2938
            *remaining_text = text_to_complete;
 
2939
 
 
2940
          g_free (utf8_curdir);
 
2941
        }
 
2942
 
 
2943
      g_free (tmp);
 
2944
    }
 
2945
  else
 
2946
    {
 
2947
      *remaining_text = text_to_complete;
 
2948
 
 
2949
      new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
 
2950
    }
 
2951
 
 
2952
  if (new_dir)
 
2953
    {
 
2954
      new_dir->cmpl_index = -1;
 
2955
      new_dir->cmpl_parent = NULL;
 
2956
    }
 
2957
 
 
2958
  return new_dir;
 
2959
}
 
2960
 
 
2961
#ifdef HAVE_PWD_H
 
2962
 
 
2963
/* open a directory by user name */
 
2964
static CompletionDir*
 
2965
open_user_dir (const gchar     *text_to_complete,
 
2966
               CompletionState *cmpl_state)
 
2967
{
 
2968
  CompletionDir *result;
 
2969
  gchar *first_slash;
 
2970
  gint cmp_len;
 
2971
 
 
2972
  g_assert (text_to_complete && text_to_complete[0] == '~');
 
2973
 
 
2974
  first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
 
2975
 
 
2976
  if (first_slash)
 
2977
    cmp_len = first_slash - text_to_complete - 1;
 
2978
  else
 
2979
    cmp_len = strlen (text_to_complete + 1);
 
2980
 
 
2981
  if (!cmp_len)
 
2982
    {
 
2983
      /* ~/ */
 
2984
      const gchar *homedir = g_get_home_dir ();
 
2985
      gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
 
2986
 
 
2987
      if (utf8_homedir)
 
2988
        result = open_dir (utf8_homedir, cmpl_state);
 
2989
      else
 
2990
        result = NULL;
 
2991
      
 
2992
      g_free (utf8_homedir);
 
2993
    }
 
2994
  else
 
2995
    {
 
2996
      /* ~user/ */
 
2997
      gchar* copy = g_new (char, cmp_len + 1);
 
2998
      gchar *utf8_dir;
 
2999
      struct passwd *pwd;
 
3000
 
 
3001
      strncpy (copy, text_to_complete + 1, cmp_len);
 
3002
      copy[cmp_len] = 0;
 
3003
      pwd = getpwnam (copy);
 
3004
      g_free (copy);
 
3005
      if (!pwd)
 
3006
        {
 
3007
          cmpl_errno = errno;
 
3008
          return NULL;
 
3009
        }
 
3010
      utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
 
3011
      result = open_dir (utf8_dir, cmpl_state);
 
3012
      g_free (utf8_dir);
 
3013
    }
 
3014
  return result;
 
3015
}
 
3016
 
 
3017
#endif
 
3018
 
 
3019
/* open a directory relative to the current relative directory */
 
3020
static CompletionDir*
 
3021
open_relative_dir (gchar           *dir_name,
 
3022
                   CompletionDir   *dir,
 
3023
                   CompletionState *cmpl_state)
 
3024
{
 
3025
  CompletionDir *result;
 
3026
  GString *path;
 
3027
 
 
3028
  path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
 
3029
  g_string_assign (path, dir->fullname);
 
3030
 
 
3031
  if (dir->fullname_len > 1
 
3032
      && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
 
3033
    g_string_append_c (path, G_DIR_SEPARATOR);
 
3034
  g_string_append (path, dir_name);
 
3035
 
 
3036
  result = open_dir (path->str, cmpl_state);
 
3037
 
 
3038
  g_string_free (path, TRUE);
 
3039
 
 
3040
  return result;
 
3041
}
 
3042
 
 
3043
/* after the cache lookup fails, really open a new directory */
 
3044
static CompletionDirSent*
 
3045
open_new_dir (gchar       *dir_name,
 
3046
              struct stat *sbuf,
 
3047
              gboolean     stat_subdirs)
 
3048
{
 
3049
  CompletionDirSent *sent;
 
3050
  GDir *directory;
 
3051
  const char *dirent;
 
3052
  GError *error = NULL;
 
3053
  gint entry_count = 0;
 
3054
  gint n_entries = 0;
 
3055
  gint i;
 
3056
  struct stat ent_sbuf;
 
3057
  GString *path;
 
3058
  gchar *sys_dir_name;
 
3059
 
 
3060
  sent = g_new (CompletionDirSent, 1);
 
3061
#ifndef G_PLATFORM_WIN32
 
3062
  sent->mtime = sbuf->st_mtime;
 
3063
  sent->inode = sbuf->st_ino;
 
3064
  sent->device = sbuf->st_dev;
 
3065
#endif
 
3066
  path = g_string_sized_new (2*MAXPATHLEN + 10);
 
3067
 
 
3068
  sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
 
3069
  if (!sys_dir_name)
 
3070
    {
 
3071
      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3072
      return NULL;
 
3073
    }
 
3074
  
 
3075
  directory = g_dir_open (sys_dir_name, 0, &error);
 
3076
  if (!directory)
 
3077
    {
 
3078
      cmpl_errno = error->code; /* ??? */
 
3079
      g_free (sys_dir_name);
 
3080
      return NULL;
 
3081
    }
 
3082
 
 
3083
  while ((dirent = g_dir_read_name (directory)) != NULL)
 
3084
    entry_count++;
 
3085
  entry_count += 2;             /* For ".",".." */
 
3086
 
 
3087
  sent->entries = g_new (CompletionDirEntry, entry_count);
 
3088
  sent->entry_count = entry_count;
 
3089
 
 
3090
  g_dir_rewind (directory);
 
3091
 
 
3092
  for (i = 0; i < entry_count; i += 1)
 
3093
    {
 
3094
      GError *error = NULL;
 
3095
 
 
3096
      if (i == 0)
 
3097
        dirent = ".";
 
3098
      else if (i == 1)
 
3099
        dirent = "..";
 
3100
      else
 
3101
        {
 
3102
          dirent = g_dir_read_name (directory);
 
3103
          if (!dirent)          /* Directory changed */
 
3104
            break;
 
3105
        }
 
3106
 
 
3107
      sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
 
3108
      if (sent->entries[n_entries].entry_name == NULL
 
3109
          || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
 
3110
        {
 
3111
          gchar *escaped_str = g_strescape (dirent, NULL);
 
3112
          g_message (_("The filename \"%s\" couldn't be converted to UTF-8. "
 
3113
                       "(try setting the environment variable G_FILENAME_ENCODING): %s"),
 
3114
                     escaped_str,
 
3115
                     error->message ? error->message : _("Invalid UTF-8"));
 
3116
          g_free (escaped_str);
 
3117
          g_clear_error (&error);
 
3118
          continue;
 
3119
        }
 
3120
      g_clear_error (&error);
 
3121
      
 
3122
      sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
 
3123
      
 
3124
      g_string_assign (path, sys_dir_name);
 
3125
      if (path->str[path->len-1] != G_DIR_SEPARATOR)
 
3126
        {
 
3127
          g_string_append_c (path, G_DIR_SEPARATOR);
 
3128
        }
 
3129
      g_string_append (path, dirent);
 
3130
 
 
3131
      if (stat_subdirs)
 
3132
        {
 
3133
          /* Here we know path->str is a "system charset" string */
 
3134
          if (g_stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
 
3135
            sent->entries[n_entries].is_dir = TRUE;
 
3136
          else
 
3137
            /* stat may fail, and we don't mind, since it could be a
 
3138
             * dangling symlink. */
 
3139
            sent->entries[n_entries].is_dir = FALSE;
 
3140
        }
 
3141
      else
 
3142
        sent->entries[n_entries].is_dir = 1;
 
3143
 
 
3144
      n_entries++;
 
3145
    }
 
3146
  sent->entry_count = n_entries;
 
3147
  
 
3148
  g_free (sys_dir_name);
 
3149
  g_string_free (path, TRUE);
 
3150
  qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
 
3151
 
 
3152
  g_dir_close (directory);
 
3153
 
 
3154
  return sent;
 
3155
}
 
3156
 
 
3157
#ifndef G_PLATFORM_WIN32
 
3158
 
 
3159
static gboolean
 
3160
check_dir (gchar       *dir_name,
 
3161
           struct stat *result,
 
3162
           gboolean    *stat_subdirs)
 
3163
{
 
3164
  /* A list of directories that we know only contain other directories.
 
3165
   * Trying to stat every file in these directories would be very
 
3166
   * expensive.
 
3167
   */
 
3168
 
 
3169
  static struct {
 
3170
    const gchar name[5];
 
3171
    gboolean present;
 
3172
    struct stat statbuf;
 
3173
  } no_stat_dirs[] = {
 
3174
    { "/afs", FALSE, { 0 } },
 
3175
    { "/net", FALSE, { 0 } }
 
3176
  };
 
3177
 
 
3178
  static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
 
3179
  static gboolean initialized = FALSE;
 
3180
  gchar *sys_dir_name;
 
3181
  gint i;
 
3182
 
 
3183
  if (!initialized)
 
3184
    {
 
3185
      initialized = TRUE;
 
3186
      for (i = 0; i < n_no_stat_dirs; i++)
 
3187
        {
 
3188
          if (g_stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
 
3189
            no_stat_dirs[i].present = TRUE;
 
3190
        }
 
3191
    }
 
3192
 
 
3193
  sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
 
3194
  if (!sys_dir_name)
 
3195
    {
 
3196
      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3197
      return FALSE;
 
3198
    }
 
3199
  
 
3200
  if (g_stat (sys_dir_name, result) < 0)
 
3201
    {
 
3202
      g_free (sys_dir_name);
 
3203
      cmpl_errno = errno;
 
3204
      return FALSE;
 
3205
    }
 
3206
  g_free (sys_dir_name);
 
3207
 
 
3208
  *stat_subdirs = TRUE;
 
3209
  for (i = 0; i < n_no_stat_dirs; i++)
 
3210
    {
 
3211
      if (no_stat_dirs[i].present &&
 
3212
          (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
 
3213
          (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
 
3214
        {
 
3215
          *stat_subdirs = FALSE;
 
3216
          break;
 
3217
        }
 
3218
    }
 
3219
 
 
3220
  return TRUE;
 
3221
}
 
3222
 
 
3223
#endif
 
3224
 
 
3225
/* open a directory by absolute pathname */
 
3226
static CompletionDir*
 
3227
open_dir (gchar           *dir_name,
 
3228
          CompletionState *cmpl_state)
 
3229
{
 
3230
#ifndef G_PLATFORM_WIN32
 
3231
  struct stat sbuf;
 
3232
  gboolean stat_subdirs;
 
3233
  GList* cdsl;
 
3234
#endif
 
3235
  CompletionDirSent *sent;
 
3236
 
 
3237
#ifndef G_PLATFORM_WIN32
 
3238
  if (!check_dir (dir_name, &sbuf, &stat_subdirs))
 
3239
    return NULL;
 
3240
 
 
3241
  cdsl = cmpl_state->directory_sent_storage;
 
3242
 
 
3243
  while (cdsl)
 
3244
    {
 
3245
      sent = cdsl->data;
 
3246
 
 
3247
      if (sent->inode == sbuf.st_ino &&
 
3248
          sent->mtime == sbuf.st_mtime &&
 
3249
          sent->device == sbuf.st_dev)
 
3250
        return attach_dir (sent, dir_name, cmpl_state);
 
3251
 
 
3252
      cdsl = cdsl->next;
 
3253
    }
 
3254
 
 
3255
  sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
 
3256
#else
 
3257
  sent = open_new_dir (dir_name, NULL, TRUE);
 
3258
#endif
 
3259
 
 
3260
  if (sent)
 
3261
    {
 
3262
      cmpl_state->directory_sent_storage =
 
3263
        g_list_prepend (cmpl_state->directory_sent_storage, sent);
 
3264
 
 
3265
      return attach_dir (sent, dir_name, cmpl_state);
 
3266
    }
 
3267
 
 
3268
  return NULL;
 
3269
}
 
3270
 
 
3271
static CompletionDir*
 
3272
attach_dir (CompletionDirSent *sent,
 
3273
            gchar             *dir_name,
 
3274
            CompletionState   *cmpl_state)
 
3275
{
 
3276
  CompletionDir* new_dir;
 
3277
 
 
3278
  new_dir = g_new (CompletionDir, 1);
 
3279
 
 
3280
  cmpl_state->directory_storage =
 
3281
    g_list_prepend (cmpl_state->directory_storage, new_dir);
 
3282
 
 
3283
  new_dir->sent = sent;
 
3284
  new_dir->fullname = g_strdup (dir_name);
 
3285
  new_dir->fullname_len = strlen (dir_name);
 
3286
  new_dir->cmpl_text = NULL;
 
3287
 
 
3288
  return new_dir;
 
3289
}
 
3290
 
 
3291
static gint
 
3292
correct_dir_fullname (CompletionDir* cmpl_dir)
 
3293
{
 
3294
  gint length = strlen (cmpl_dir->fullname);
 
3295
  gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
 
3296
  gchar *sys_filename;
 
3297
  struct stat sbuf;
 
3298
 
 
3299
  /* Does it end with /. (\.) ? */
 
3300
  if (length >= 2 &&
 
3301
      strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
 
3302
    {
 
3303
      /* Is it just the root directory (on a drive) ? */
 
3304
      if (cmpl_dir->fullname + length - 2 == first_slash)
 
3305
        {
 
3306
          cmpl_dir->fullname[length - 1] = 0;
 
3307
          cmpl_dir->fullname_len = length - 1;
 
3308
          return TRUE;
 
3309
        }
 
3310
      else
 
3311
        {
 
3312
          cmpl_dir->fullname[length - 2] = 0;
 
3313
        }
 
3314
    }
 
3315
 
 
3316
  /* Ends with /./ (\.\)? */
 
3317
  else if (length >= 3 &&
 
3318
           strcmp (cmpl_dir->fullname + length - 3,
 
3319
                   G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
 
3320
    cmpl_dir->fullname[length - 2] = 0;
 
3321
 
 
3322
  /* Ends with /.. (\..) ? */
 
3323
  else if (length >= 3 &&
 
3324
           strcmp (cmpl_dir->fullname + length - 3,
 
3325
                   G_DIR_SEPARATOR_S "..") == 0)
 
3326
    {
 
3327
      /* Is it just /.. (X:\..)? */
 
3328
      if (cmpl_dir->fullname + length - 3 == first_slash)
 
3329
        {
 
3330
          cmpl_dir->fullname[length - 2] = 0;
 
3331
          cmpl_dir->fullname_len = length - 2;
 
3332
          return TRUE;
 
3333
        }
 
3334
 
 
3335
      sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
 
3336
      if (!sys_filename)
 
3337
        {
 
3338
          cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3339
          return FALSE;
 
3340
        }
 
3341
      
 
3342
      if (g_stat (sys_filename, &sbuf) < 0)
 
3343
        {
 
3344
          g_free (sys_filename);
 
3345
          cmpl_errno = errno;
 
3346
          return FALSE;
 
3347
        }
 
3348
      g_free (sys_filename);
 
3349
 
 
3350
      cmpl_dir->fullname[length - 3] = 0;
 
3351
 
 
3352
      if (!correct_parent (cmpl_dir, &sbuf))
 
3353
        return FALSE;
 
3354
    }
 
3355
 
 
3356
  /* Ends with /../ (\..\)? */
 
3357
  else if (length >= 4 &&
 
3358
           strcmp (cmpl_dir->fullname + length - 4,
 
3359
                   G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
 
3360
    {
 
3361
      /* Is it just /../ (X:\..\)? */
 
3362
      if (cmpl_dir->fullname + length - 4 == first_slash)
 
3363
        {
 
3364
          cmpl_dir->fullname[length - 3] = 0;
 
3365
          cmpl_dir->fullname_len = length - 3;
 
3366
          return TRUE;
 
3367
        }
 
3368
 
 
3369
      sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
 
3370
      if (!sys_filename)
 
3371
        {
 
3372
          cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3373
          return FALSE;
 
3374
        }
 
3375
      
 
3376
      if (g_stat (sys_filename, &sbuf) < 0)
 
3377
        {
 
3378
          g_free (sys_filename);
 
3379
          cmpl_errno = errno;
 
3380
          return FALSE;
 
3381
        }
 
3382
      g_free (sys_filename);
 
3383
 
 
3384
      cmpl_dir->fullname[length - 4] = 0;
 
3385
 
 
3386
      if (!correct_parent (cmpl_dir, &sbuf))
 
3387
        return FALSE;
 
3388
    }
 
3389
 
 
3390
  cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
 
3391
 
 
3392
  return TRUE;
 
3393
}
 
3394
 
 
3395
static gint
 
3396
correct_parent (CompletionDir *cmpl_dir,
 
3397
                struct stat   *sbuf)
 
3398
{
 
3399
  struct stat parbuf;
 
3400
  gchar *last_slash;
 
3401
  gchar *first_slash;
 
3402
#ifndef G_PLATFORM_WIN32
 
3403
  gchar *new_name;
 
3404
#endif
 
3405
  gchar *sys_filename;
 
3406
  gchar c = 0;
 
3407
 
 
3408
  last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
 
3409
  g_assert (last_slash);
 
3410
  first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
 
3411
 
 
3412
  /* Clever (?) way to check for top-level directory that works also on
 
3413
   * Win32, where there is a drive letter and colon prefixed...
 
3414
   */
 
3415
  if (last_slash != first_slash)
 
3416
    {
 
3417
      last_slash[0] = 0;
 
3418
    }
 
3419
  else
 
3420
    {
 
3421
      c = last_slash[1];
 
3422
      last_slash[1] = 0;
 
3423
    }
 
3424
 
 
3425
  sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
 
3426
  if (!sys_filename)
 
3427
    {
 
3428
      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3429
      if (!c)
 
3430
        last_slash[0] = G_DIR_SEPARATOR;
 
3431
      return FALSE;
 
3432
    }
 
3433
  
 
3434
  if (g_stat (sys_filename, &parbuf) < 0)
 
3435
    {
 
3436
      g_free (sys_filename);
 
3437
      cmpl_errno = errno;
 
3438
      if (!c)
 
3439
        last_slash[0] = G_DIR_SEPARATOR;
 
3440
      return FALSE;
 
3441
    }
 
3442
  g_free (sys_filename);
 
3443
 
 
3444
#ifndef G_PLATFORM_WIN32        /* No inode numbers on Win32 */
 
3445
  if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
 
3446
    /* it wasn't a link */
 
3447
    return TRUE;
 
3448
 
 
3449
  if (c)
 
3450
    last_slash[1] = c;
 
3451
  else
 
3452
    last_slash[0] = G_DIR_SEPARATOR;
 
3453
 
 
3454
  /* it was a link, have to figure it out the hard way */
 
3455
 
 
3456
  new_name = find_parent_dir_fullname (cmpl_dir->fullname);
 
3457
 
 
3458
  if (!new_name)
 
3459
    return FALSE;
 
3460
 
 
3461
  g_free (cmpl_dir->fullname);
 
3462
 
 
3463
  cmpl_dir->fullname = new_name;
 
3464
#endif
 
3465
 
 
3466
  return TRUE;
 
3467
}
 
3468
 
 
3469
#ifndef G_PLATFORM_WIN32
 
3470
 
 
3471
static gchar*
 
3472
find_parent_dir_fullname (gchar* dirname)
 
3473
{
 
3474
  gchar *sys_orig_dir;
 
3475
  gchar *result;
 
3476
  gchar *sys_cwd;
 
3477
  gchar *sys_dirname;
 
3478
 
 
3479
  sys_orig_dir = g_get_current_dir ();
 
3480
  sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
 
3481
  if (!sys_dirname)
 
3482
    {
 
3483
      g_free (sys_orig_dir);
 
3484
      cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
 
3485
      return NULL;
 
3486
    }
 
3487
  
 
3488
  if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
 
3489
    {
 
3490
      cmpl_errno = errno;
 
3491
      chdir (sys_orig_dir);
 
3492
      g_free (sys_dirname);
 
3493
      g_free (sys_orig_dir);
 
3494
      return NULL;
 
3495
    }
 
3496
  g_free (sys_dirname);
 
3497
 
 
3498
  sys_cwd = g_get_current_dir ();
 
3499
  result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
 
3500
  g_free (sys_cwd);
 
3501
 
 
3502
  if (chdir (sys_orig_dir) != 0)
 
3503
    {
 
3504
      cmpl_errno = errno;
 
3505
      g_free (sys_orig_dir);
 
3506
      return NULL;
 
3507
    }
 
3508
 
 
3509
  g_free (sys_orig_dir);
 
3510
  return result;
 
3511
}
 
3512
 
 
3513
#endif
 
3514
 
 
3515
/**********************************************************************/
 
3516
/*                        Completion Operations                       */
 
3517
/**********************************************************************/
 
3518
 
 
3519
#ifdef HAVE_PWD_H
 
3520
 
 
3521
static PossibleCompletion*
 
3522
attempt_homedir_completion (gchar           *text_to_complete,
 
3523
                            CompletionState *cmpl_state)
 
3524
{
 
3525
  gint index;
 
3526
 
 
3527
  if (!cmpl_state->user_dir_name_buffer &&
 
3528
      !get_pwdb (cmpl_state))
 
3529
    return NULL;
 
3530
 
 
3531
  cmpl_state->user_completion_index += 1;
 
3532
 
 
3533
  while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
 
3534
    {
 
3535
      index = first_diff_index (text_to_complete + 1,
 
3536
                                cmpl_state->user_directories
 
3537
                                [cmpl_state->user_completion_index].login);
 
3538
 
 
3539
      switch (index)
 
3540
        {
 
3541
        case PATTERN_MATCH:
 
3542
          break;
 
3543
        default:
 
3544
          if (cmpl_state->last_valid_char < (index + 1))
 
3545
            cmpl_state->last_valid_char = index + 1;
 
3546
          cmpl_state->user_completion_index += 1;
 
3547
          continue;
 
3548
        }
 
3549
 
 
3550
      cmpl_state->the_completion.is_a_completion = 1;
 
3551
      cmpl_state->the_completion.is_directory = TRUE;
 
3552
 
 
3553
      append_completion_text ("~", cmpl_state);
 
3554
 
 
3555
      append_completion_text (cmpl_state->
 
3556
                              user_directories[cmpl_state->user_completion_index].login,
 
3557
                              cmpl_state);
 
3558
 
 
3559
      return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
 
3560
    }
 
3561
 
 
3562
  if (text_to_complete[1]
 
3563
      || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
 
3564
    {
 
3565
      cmpl_state->user_completion_index = -1;
 
3566
      return NULL;
 
3567
    }
 
3568
  else
 
3569
    {
 
3570
      cmpl_state->user_completion_index += 1;
 
3571
      cmpl_state->the_completion.is_a_completion = 1;
 
3572
      cmpl_state->the_completion.is_directory = TRUE;
 
3573
 
 
3574
      return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
 
3575
    }
 
3576
}
 
3577
 
 
3578
#endif
 
3579
 
 
3580
#ifdef G_PLATFORM_WIN32
 
3581
/* FIXME: determine whether we should casefold all Unicode letters
 
3582
 * here, too (and in in first_diff_index() walk through the strings with
 
3583
 * g_utf8_next_char()), or if this folding isn't actually needed at
 
3584
 * all.
 
3585
 */
 
3586
#define FOLD(c) (tolower(c))
 
3587
#else
 
3588
#define FOLD(c) (c)
 
3589
#endif
 
3590
 
 
3591
/* returns the index (>= 0) of the first differing character,
 
3592
 * PATTERN_MATCH if the completion matches */
 
3593
static gint
 
3594
first_diff_index (gchar *pat,
 
3595
                  gchar *text)
 
3596
{
 
3597
  gint diff = 0;
 
3598
 
 
3599
  while (*pat && *text && FOLD (*text) == FOLD (*pat))
 
3600
    {
 
3601
      pat += 1;
 
3602
      text += 1;
 
3603
      diff += 1;
 
3604
    }
 
3605
 
 
3606
  if (*pat)
 
3607
    return diff;
 
3608
 
 
3609
  return PATTERN_MATCH;
 
3610
}
 
3611
 
 
3612
static PossibleCompletion*
 
3613
append_completion_text (gchar           *text,
 
3614
                        CompletionState *cmpl_state)
 
3615
{
 
3616
  gint len, i = 1;
 
3617
 
 
3618
  if (!cmpl_state->the_completion.text)
 
3619
    return NULL;
 
3620
 
 
3621
  len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
 
3622
 
 
3623
  if (cmpl_state->the_completion.text_alloc > len)
 
3624
    {
 
3625
      strcat (cmpl_state->the_completion.text, text);
 
3626
      return &cmpl_state->the_completion;
 
3627
    }
 
3628
 
 
3629
  while (i < len)
 
3630
    i <<= 1;
 
3631
 
 
3632
  cmpl_state->the_completion.text_alloc = i;
 
3633
 
 
3634
  cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
 
3635
 
 
3636
  if (!cmpl_state->the_completion.text)
 
3637
    return NULL;
 
3638
  else
 
3639
    {
 
3640
      strcat (cmpl_state->the_completion.text, text);
 
3641
      return &cmpl_state->the_completion;
 
3642
    }
 
3643
}
 
3644
 
 
3645
static CompletionDir*
 
3646
find_completion_dir (gchar          *text_to_complete,
 
3647
                    gchar          **remaining_text,
 
3648
                    CompletionState *cmpl_state)
 
3649
{
 
3650
  gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
 
3651
  CompletionDir* dir = cmpl_state->reference_dir;
 
3652
  CompletionDir* next;
 
3653
  *remaining_text = text_to_complete;
 
3654
 
 
3655
  while (first_slash)
 
3656
    {
 
3657
      gint len = first_slash - *remaining_text;
 
3658
      gint found = 0;
 
3659
      gchar *found_name = NULL;         /* Quiet gcc */
 
3660
      gint i;
 
3661
      gchar* pat_buf = g_new (gchar, len + 1);
 
3662
 
 
3663
      strncpy (pat_buf, *remaining_text, len);
 
3664
      pat_buf[len] = 0;
 
3665
 
 
3666
      for (i = 0; i < dir->sent->entry_count; i += 1)
 
3667
        {
 
3668
          if (dir->sent->entries[i].is_dir &&
 
3669
              _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE))
 
3670
            {
 
3671
              if (found)
 
3672
                {
 
3673
                  g_free (pat_buf);
 
3674
                  return dir;
 
3675
                }
 
3676
              else
 
3677
                {
 
3678
                  found = 1;
 
3679
                  found_name = dir->sent->entries[i].entry_name;
 
3680
                }
 
3681
            }
 
3682
        }
 
3683
 
 
3684
      if (!found)
 
3685
        {
 
3686
          /* Perhaps we are trying to open an automount directory */
 
3687
          found_name = pat_buf;
 
3688
        }
 
3689
 
 
3690
      next = open_relative_dir (found_name, dir, cmpl_state);
 
3691
      
 
3692
      if (!next)
 
3693
        {
 
3694
          g_free (pat_buf);
 
3695
          return NULL;
 
3696
}
 
3697
      
 
3698
      next->cmpl_parent = dir;
 
3699
      
 
3700
      dir = next;
 
3701
      
 
3702
      if (!correct_dir_fullname (dir))
 
3703
        {
 
3704
          g_free (pat_buf);
 
3705
          return NULL;
 
3706
        }
 
3707
      
 
3708
      *remaining_text = first_slash + 1;
 
3709
      first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
 
3710
 
 
3711
      g_free (pat_buf);
 
3712
    }
 
3713
 
 
3714
  return dir;
 
3715
}
 
3716
 
 
3717
static void
 
3718
update_cmpl (PossibleCompletion *poss,
 
3719
             CompletionState    *cmpl_state)
 
3720
{
 
3721
  gint cmpl_len;
 
3722
 
 
3723
  if (!poss || !cmpl_is_a_completion (poss))
 
3724
    return;
 
3725
 
 
3726
  cmpl_len = strlen (cmpl_this_completion (poss));
 
3727
 
 
3728
  if (cmpl_state->updated_text_alloc < cmpl_len + 1)
 
3729
    {
 
3730
      cmpl_state->updated_text_alloc = 2*cmpl_len;
 
3731
      cmpl_state->updated_text =
 
3732
        (gchar*)g_realloc (cmpl_state->updated_text,
 
3733
                           cmpl_state->updated_text_alloc);
 
3734
    }
 
3735
 
 
3736
  if (cmpl_state->updated_text_len < 0)
 
3737
    {
 
3738
      strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
 
3739
      cmpl_state->updated_text_len = cmpl_len;
 
3740
      cmpl_state->re_complete = cmpl_is_directory (poss);
 
3741
    }
 
3742
  else if (cmpl_state->updated_text_len == 0)
 
3743
    {
 
3744
      cmpl_state->re_complete = FALSE;
 
3745
    }
 
3746
  else
 
3747
    {
 
3748
      gint first_diff =
 
3749
        first_diff_index (cmpl_state->updated_text,
 
3750
                          cmpl_this_completion (poss));
 
3751
 
 
3752
      cmpl_state->re_complete = FALSE;
 
3753
 
 
3754
      if (first_diff == PATTERN_MATCH)
 
3755
        return;
 
3756
 
 
3757
      if (first_diff > cmpl_state->updated_text_len)
 
3758
        strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
 
3759
 
 
3760
      cmpl_state->updated_text_len = first_diff;
 
3761
      cmpl_state->updated_text[first_diff] = 0;
 
3762
    }
 
3763
}
 
3764
 
 
3765
static PossibleCompletion*
 
3766
attempt_file_completion (CompletionState *cmpl_state)
 
3767
{
 
3768
  gchar *pat_buf, *first_slash;
 
3769
  CompletionDir *dir = cmpl_state->active_completion_dir;
 
3770
 
 
3771
  dir->cmpl_index += 1;
 
3772
 
 
3773
  if (dir->cmpl_index == dir->sent->entry_count)
 
3774
    {
 
3775
      if (dir->cmpl_parent == NULL)
 
3776
        {
 
3777
          cmpl_state->active_completion_dir = NULL;
 
3778
 
 
3779
          return NULL;
 
3780
        }
 
3781
      else
 
3782
        {
 
3783
          cmpl_state->active_completion_dir = dir->cmpl_parent;
 
3784
 
 
3785
          return attempt_file_completion (cmpl_state);
 
3786
        }
 
3787
    }
 
3788
 
 
3789
  g_assert (dir->cmpl_text);
 
3790
 
 
3791
  first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
 
3792
 
 
3793
  if (first_slash)
 
3794
    {
 
3795
      gint len = first_slash - dir->cmpl_text;
 
3796
 
 
3797
      pat_buf = g_new (gchar, len + 1);
 
3798
      strncpy (pat_buf, dir->cmpl_text, len);
 
3799
      pat_buf[len] = 0;
 
3800
    }
 
3801
  else
 
3802
    {
 
3803
      gint len = strlen (dir->cmpl_text);
 
3804
 
 
3805
      pat_buf = g_new (gchar, len + 2);
 
3806
      strcpy (pat_buf, dir->cmpl_text);
 
3807
      /* Don't append a * if the user entered one herself.
 
3808
       * This way one can complete *.h and don't get matches
 
3809
       * on any .help files, for instance.
 
3810
       */
 
3811
      if (strchr (pat_buf, '*') == NULL)
 
3812
        strcpy (pat_buf + len, "*");
 
3813
    }
 
3814
 
 
3815
  if (first_slash)
 
3816
    {
 
3817
      if (dir->sent->entries[dir->cmpl_index].is_dir)
 
3818
        {
 
3819
          if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE))
 
3820
            {
 
3821
              CompletionDir* new_dir;
 
3822
 
 
3823
              new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
 
3824
                                           dir, cmpl_state);
 
3825
 
 
3826
              if (!new_dir)
 
3827
                {
 
3828
                  g_free (pat_buf);
 
3829
                  return NULL;
 
3830
                }
 
3831
 
 
3832
              new_dir->cmpl_parent = dir;
 
3833
 
 
3834
              new_dir->cmpl_index = -1;
 
3835
              new_dir->cmpl_text = g_strdup (first_slash + 1);
 
3836
 
 
3837
              cmpl_state->active_completion_dir = new_dir;
 
3838
 
 
3839
              g_free (pat_buf);
 
3840
              return attempt_file_completion (cmpl_state);
 
3841
            }
 
3842
          else
 
3843
            {
 
3844
              g_free (pat_buf);
 
3845
              return attempt_file_completion (cmpl_state);
 
3846
            }
 
3847
        }
 
3848
      else
 
3849
        {
 
3850
          g_free (pat_buf);
 
3851
          return attempt_file_completion (cmpl_state);
 
3852
        }
 
3853
    }
 
3854
  else
 
3855
    {
 
3856
      if (dir->cmpl_parent != NULL)
 
3857
        {
 
3858
          append_completion_text (dir->fullname +
 
3859
                                  strlen (cmpl_state->completion_dir->fullname) + 1,
 
3860
                                  cmpl_state);
 
3861
          append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
 
3862
        }
 
3863
 
 
3864
      append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
 
3865
 
 
3866
      cmpl_state->the_completion.is_a_completion =
 
3867
        _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE);
 
3868
 
 
3869
      cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
 
3870
      if (dir->sent->entries[dir->cmpl_index].is_dir)
 
3871
        append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
 
3872
 
 
3873
      g_free (pat_buf);
 
3874
      return &cmpl_state->the_completion;
 
3875
    }
 
3876
}
 
3877
 
 
3878
#ifdef HAVE_PWD_H
 
3879
 
 
3880
static gint
 
3881
get_pwdb (CompletionState* cmpl_state)
 
3882
{
 
3883
  struct passwd *pwd_ptr;
 
3884
  gchar* buf_ptr;
 
3885
  gchar *utf8;
 
3886
  gint len = 0, i, count = 0;
 
3887
 
 
3888
  if (cmpl_state->user_dir_name_buffer)
 
3889
    return TRUE;
 
3890
  setpwent ();
 
3891
 
 
3892
  while ((pwd_ptr = getpwent ()) != NULL)
 
3893
    {
 
3894
      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
 
3895
      len += strlen (utf8);
 
3896
      g_free (utf8);
 
3897
      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
 
3898
      len += strlen (utf8);
 
3899
      g_free (utf8);
 
3900
      len += 2;
 
3901
      count += 1;
 
3902
    }
 
3903
 
 
3904
  setpwent ();
 
3905
 
 
3906
  cmpl_state->user_dir_name_buffer = g_new (gchar, len);
 
3907
  cmpl_state->user_directories = g_new (CompletionUserDir, count);
 
3908
  cmpl_state->user_directories_len = count;
 
3909
 
 
3910
  buf_ptr = cmpl_state->user_dir_name_buffer;
 
3911
 
 
3912
  for (i = 0; i < count; i += 1)
 
3913
    {
 
3914
      pwd_ptr = getpwent ();
 
3915
      if (!pwd_ptr)
 
3916
        {
 
3917
          cmpl_errno = errno;
 
3918
          goto error;
 
3919
        }
 
3920
 
 
3921
      utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
 
3922
      strcpy (buf_ptr, utf8);
 
3923
      g_free (utf8);
 
3924
 
 
3925
      cmpl_state->user_directories[i].login = buf_ptr;
 
3926
 
 
3927
      buf_ptr += strlen (buf_ptr);
 
3928
      buf_ptr += 1;
 
3929
 
 
3930
      utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
 
3931
      strcpy (buf_ptr, utf8);
 
3932
      g_free (utf8);
 
3933
 
 
3934
      cmpl_state->user_directories[i].homedir = buf_ptr;
 
3935
 
 
3936
      buf_ptr += strlen (buf_ptr);
 
3937
      buf_ptr += 1;
 
3938
    }
 
3939
 
 
3940
  qsort (cmpl_state->user_directories,
 
3941
         cmpl_state->user_directories_len,
 
3942
         sizeof (CompletionUserDir),
 
3943
         compare_user_dir);
 
3944
 
 
3945
  endpwent ();
 
3946
 
 
3947
  return TRUE;
 
3948
 
 
3949
error:
 
3950
 
 
3951
  if (cmpl_state->user_dir_name_buffer)
 
3952
    g_free (cmpl_state->user_dir_name_buffer);
 
3953
  if (cmpl_state->user_directories)
 
3954
    g_free (cmpl_state->user_directories);
 
3955
 
 
3956
  cmpl_state->user_dir_name_buffer = NULL;
 
3957
  cmpl_state->user_directories = NULL;
 
3958
 
 
3959
  return FALSE;
 
3960
}
 
3961
 
 
3962
static gint
 
3963
compare_user_dir (const void *a,
 
3964
                  const void *b)
 
3965
{
 
3966
  return strcmp ((((CompletionUserDir*)a))->login,
 
3967
                 (((CompletionUserDir*)b))->login);
 
3968
}
 
3969
 
 
3970
#endif
 
3971
 
 
3972
static gint
 
3973
compare_cmpl_dir (const void *a,
 
3974
                  const void *b)
 
3975
{
 
3976
  
 
3977
  return strcmp (((CompletionDirEntry*)a)->sort_key,
 
3978
                 (((CompletionDirEntry*)b))->sort_key);
 
3979
}
 
3980
 
 
3981
static gint
 
3982
cmpl_state_okay (CompletionState* cmpl_state)
 
3983
{
 
3984
  return  cmpl_state && cmpl_state->reference_dir;
 
3985
}
 
3986
 
 
3987
static const gchar*
 
3988
cmpl_strerror (gint err)
 
3989
{
 
3990
  if (err == CMPL_ERRNO_TOO_LONG)
 
3991
    return _("Name too long");
 
3992
  else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
 
3993
    return _("Couldn't convert filename");
 
3994
  else
 
3995
    return g_strerror (err);
 
3996
}
 
3997
 
 
3998
#ifdef G_OS_WIN32
 
3999
 
 
4000
/* DLL ABI stability backward compatibility versions */
 
4001
 
 
4002
#undef gtk_file_selection_get_filename
 
4003
 
 
4004
G_CONST_RETURN gchar*
 
4005
gtk_file_selection_get_filename (GtkFileSelection *filesel)
 
4006
{
 
4007
  static gchar retval[MAXPATHLEN*2+1];
 
4008
  gchar *tem;
 
4009
 
 
4010
  tem = g_locale_from_utf8 (gtk_file_selection_get_filename_utf8 (filesel),
 
4011
                            -1, NULL, NULL, NULL);
 
4012
 
 
4013
  strncpy (retval, tem, sizeof (retval) - 1);
 
4014
  retval[sizeof (retval) - 1] = '\0';
 
4015
  g_free (tem);
 
4016
 
 
4017
  return retval;
 
4018
}
 
4019
 
 
4020
#undef gtk_file_selection_set_filename
 
4021
 
 
4022
void
 
4023
gtk_file_selection_set_filename (GtkFileSelection *filesel,
 
4024
                                 const gchar      *filename)
 
4025
{
 
4026
  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
 
4027
  gtk_file_selection_set_filename_utf8 (filesel, utf8_filename);
 
4028
  g_free (utf8_filename);
 
4029
}
 
4030
 
 
4031
#undef gtk_file_selection_get_selections
 
4032
 
 
4033
gchar **
 
4034
gtk_file_selection_get_selections (GtkFileSelection *filesel)
 
4035
{
 
4036
  int i = 0;
 
4037
  gchar **selections = gtk_file_selection_get_selections_utf8 (filesel);
 
4038
 
 
4039
  if (selections != NULL)
 
4040
    while (selections[i] != NULL)
 
4041
      {
 
4042
        gchar *tem = selections[i];
 
4043
        selections[i] = g_locale_from_utf8 (selections[i],
 
4044
                                            -1, NULL, NULL, NULL);
 
4045
        g_free (tem);
 
4046
        i++;
 
4047
      }
 
4048
 
 
4049
  return selections;
 
4050
}
 
4051
 
 
4052
#endif /* G_OS_WIN32 */
 
4053
 
 
4054
#define __GTK_FILESEL_C__
 
4055
#include "gtkaliasdef.c"