View | Details | Raw Unified | Return to bug 459744
Collapse All | Expand All

(-)a/gtk/gtkfilechooser.h (-1 / +2 lines)
Lines 60-66 GType gtk_file_chooser_get_type (void) G_GNUC_CONST; Link Here
60
typedef enum {
60
typedef enum {
61
  GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
61
  GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
62
  GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
62
  GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
63
  GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS
63
  GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
64
  GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME
64
} GtkFileChooserError;
65
} GtkFileChooserError;
65
66
66
GQuark gtk_file_chooser_error_quark (void);
67
GQuark gtk_file_chooser_error_quark (void);
(-)a/gtk/gtkfilechooserdefault.c (+4 lines)
Lines 4865-4870 save_widgets_create (GtkFileChooserDefault *impl) Link Here
4865
  impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4865
  impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4866
  _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4866
  _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4867
					   impl->file_system);
4867
					   impl->file_system);
4868
  _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4868
  gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4869
  gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4869
  gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4870
  gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4870
  gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4871
  gtk_table_attach (GTK_TABLE (table), impl->location_entry,
Lines 5304-5309 set_local_only (GtkFileChooserDefault *impl, Link Here
5304
    {
5305
    {
5305
      impl->local_only = local_only;
5306
      impl->local_only = local_only;
5306
5307
5308
      if (impl->location_entry)
5309
	_gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
5310
5307
      if (impl->shortcuts_model && impl->file_system)
5311
      if (impl->shortcuts_model && impl->file_system)
5308
	{
5312
	{
5309
	  shortcuts_add_volumes (impl);
5313
	  shortcuts_add_volumes (impl);
(-)a/gtk/gtkfilechooserentry.c (-103 / +275 lines)
Lines 50-55 typedef enum { Link Here
50
  LOAD_COMPLETE_EXPLICIT_COMPLETION
50
  LOAD_COMPLETE_EXPLICIT_COMPLETION
51
} LoadCompleteAction;
51
} LoadCompleteAction;
52
52
53
typedef enum
54
{
55
  REFRESH_OK,
56
  REFRESH_INVALID_INPUT,
57
  REFRESH_INCOMPLETE_HOSTNAME,
58
  REFRESH_NONEXISTENT,
59
  REFRESH_NOT_LOCAL
60
} RefreshStatus;
61
53
struct _GtkFileChooserEntry
62
struct _GtkFileChooserEntry
54
{
63
{
55
  GtkEntry parent_instance;
64
  GtkEntry parent_instance;
Lines 79-84 struct _GtkFileChooserEntry Link Here
79
  guint has_completion : 1;
88
  guint has_completion : 1;
80
  guint in_change      : 1;
89
  guint in_change      : 1;
81
  guint eat_tabs       : 1;
90
  guint eat_tabs       : 1;
91
  guint local_only     : 1;
82
};
92
};
83
93
84
enum
94
enum
Lines 136-152 static gboolean completion_match_func (GtkEntryCompletion *comp, Link Here
136
					   gpointer             data);
146
					   gpointer             data);
137
static char    *maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
147
static char    *maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
138
						GFile               *file,
148
						GFile               *file,
139
						gchar               *display_name);
149
						gchar               *display_name,
150
						gboolean            *appended);
140
151
141
typedef enum {
152
typedef enum {
142
  REFRESH_UP_TO_CURSOR_POSITION,
153
  REFRESH_UP_TO_CURSOR_POSITION,
143
  REFRESH_WHOLE_TEXT
154
  REFRESH_WHOLE_TEXT
144
} RefreshMode;
155
} RefreshMode;
145
156
146
static void refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
157
static RefreshStatus refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
147
						  RefreshMode refresh_mode);
158
						  RefreshMode refresh_mode);
148
static void finished_loading_cb (GFile         *file,
159
static void finished_loading_cb (GtkFolder *folder,
149
				 gpointer       data);
160
				 gpointer   data);
150
static void autocomplete (GtkFileChooserEntry *chooser_entry);
161
static void autocomplete (GtkFileChooserEntry *chooser_entry);
151
static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry);
162
static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry);
152
static void remove_completion_feedback (GtkFileChooserEntry *chooser_entry);
163
static void remove_completion_feedback (GtkFileChooserEntry *chooser_entry);
Lines 194-199 _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry) Link Here
194
  GtkEntryCompletion *comp;
205
  GtkEntryCompletion *comp;
195
  GtkCellRenderer *cell;
206
  GtkCellRenderer *cell;
196
207
208
  chooser_entry->local_only = TRUE;
209
197
  g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
210
  g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
198
211
199
  comp = gtk_entry_completion_new ();
212
  comp = gtk_entry_completion_new ();
Lines 245-255 gtk_file_chooser_entry_finalize (GObject *object) Link Here
245
}
258
}
246
259
247
static void
260
static void
261
discard_current_folder (GtkFileChooserEntry *chooser_entry)
262
{
263
  if (chooser_entry->current_folder)
264
    {
265
      g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
266
					    G_CALLBACK (finished_loading_cb), chooser_entry);
267
      g_object_unref (chooser_entry->current_folder);
268
      chooser_entry->current_folder = NULL;
269
    }
270
}
271
272
static void
273
discard_loading_and_current_folder_file (GtkFileChooserEntry *chooser_entry)
274
{
275
  if (chooser_entry->load_folder_cancellable)
276
    {
277
      g_cancellable_cancel (chooser_entry->load_folder_cancellable);
278
      chooser_entry->load_folder_cancellable = NULL;
279
    }
280
281
  if (chooser_entry->current_folder_file)
282
    {
283
      g_object_unref (chooser_entry->current_folder_file);
284
      chooser_entry->current_folder_file = NULL;
285
    }
286
}
287
288
static void
248
gtk_file_chooser_entry_dispose (GObject *object)
289
gtk_file_chooser_entry_dispose (GObject *object)
249
{
290
{
250
  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
291
  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
251
292
252
  remove_completion_feedback (chooser_entry);
293
  remove_completion_feedback (chooser_entry);
294
  discard_current_folder (chooser_entry);
295
  discard_loading_and_current_folder_file (chooser_entry);
253
296
254
  if (chooser_entry->start_autocompletion_idle_id != 0)
297
  if (chooser_entry->start_autocompletion_idle_id != 0)
255
    {
298
    {
Lines 263-282 gtk_file_chooser_entry_dispose (GObject *object) Link Here
263
      chooser_entry->completion_store = NULL;
306
      chooser_entry->completion_store = NULL;
264
    }
307
    }
265
308
266
  if (chooser_entry->load_folder_cancellable)
267
    {
268
      g_cancellable_cancel (chooser_entry->load_folder_cancellable);
269
      chooser_entry->load_folder_cancellable = NULL;
270
    }
271
272
  if (chooser_entry->current_folder)
273
    {
274
      g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
275
					    G_CALLBACK (finished_loading_cb), chooser_entry);
276
      g_object_unref (chooser_entry->current_folder);
277
      chooser_entry->current_folder = NULL;
278
    }
279
280
  if (chooser_entry->file_system)
309
  if (chooser_entry->file_system)
281
    {
310
    {
282
      g_object_unref (chooser_entry->file_system);
311
      g_object_unref (chooser_entry->file_system);
Lines 296-301 match_selected_callback (GtkEntryCompletion *completion, Link Here
296
  char *display_name;
325
  char *display_name;
297
  GFile *file;
326
  GFile *file;
298
  gint pos;
327
  gint pos;
328
  gboolean dummy;
299
  
329
  
300
  gtk_tree_model_get (model, iter,
330
  gtk_tree_model_get (model, iter,
301
		      DISPLAY_NAME_COLUMN, &display_name,
331
		      DISPLAY_NAME_COLUMN, &display_name,
Lines 310-316 match_selected_callback (GtkEntryCompletion *completion, Link Here
310
      return FALSE;
340
      return FALSE;
311
    }
341
    }
312
342
313
  display_name = maybe_append_separator_to_file (chooser_entry, file, display_name);
343
  display_name = maybe_append_separator_to_file (chooser_entry, file, display_name, &dummy);
314
344
315
  pos = chooser_entry->file_part_pos;
345
  pos = chooser_entry->file_part_pos;
316
346
Lines 422-436 beep (GtkFileChooserEntry *chooser_entry) Link Here
422
 * return a new one if needed.  Otherwise, it will return the old one.
452
 * return a new one if needed.  Otherwise, it will return the old one.
423
 * You should be safe calling
453
 * You should be safe calling
424
 *
454
 *
425
 * display_name = maybe_append_separator_to_file (entry, file, display_name);
455
 * display_name = maybe_append_separator_to_file (entry, file, display_name, &appended);
426
 * ...
456
 * ...
427
 * g_free (display_name);
457
 * g_free (display_name);
428
 */
458
 */
429
static char *
459
static char *
430
maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
460
maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
431
				GFile               *file,
461
				GFile               *file,
432
				gchar               *display_name)
462
				gchar               *display_name,
463
				gboolean            *appended)
433
{
464
{
465
  *appended = FALSE;
466
434
  if (!g_str_has_suffix (display_name, G_DIR_SEPARATOR_S) && file)
467
  if (!g_str_has_suffix (display_name, G_DIR_SEPARATOR_S) && file)
435
    {
468
    {
436
      GFileInfo *info;
469
      GFileInfo *info;
Lines 443-448 maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry, Link Here
443
	    {
476
	    {
444
	      gchar *tmp = display_name;
477
	      gchar *tmp = display_name;
445
	      display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL);
478
	      display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL);
479
	      *appended = TRUE;
446
	      g_free (tmp);
480
	      g_free (tmp);
447
	    }
481
	    }
448
482
Lines 581-587 find_common_prefix (GtkFileChooserEntry *chooser_entry, Link Here
581
	  if (G_IS_DIR_SEPARATOR (display_name[len - 1]))
615
	  if (G_IS_DIR_SEPARATOR (display_name[len - 1]))
582
	    len--;
616
	    len--;
583
617
584
	  if (strncmp (*common_prefix_ret, display_name, len) == 0)
618
	  if (*unique_file_ret == NULL && strncmp (*common_prefix_ret, display_name, len) == 0)
585
	    *is_complete_not_unique_ret = TRUE;
619
	    *is_complete_not_unique_ret = TRUE;
586
620
587
	  g_free (display_name);
621
	  g_free (display_name);
Lines 653-659 append_common_prefix (GtkFileChooserEntry *chooser_entry, Link Here
653
  error = NULL;
687
  error = NULL;
654
  if (!find_common_prefix (chooser_entry, &common_prefix, &unique_file, &is_complete_not_unique, &prefix_expands_the_file_part, &error))
688
  if (!find_common_prefix (chooser_entry, &common_prefix, &unique_file, &is_complete_not_unique, &prefix_expands_the_file_part, &error))
655
    {
689
    {
656
      if (show_errors)
690
      /* If the user types an incomplete hostname ("http://foo" without a slash
691
       * after that), it's not an error.  We just don't want to pop up a
692
       * meaningless completion window in that state.
693
       */
694
      if (!g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME)
695
	  && show_errors)
657
	{
696
	{
658
	  beep (chooser_entry);
697
	  beep (chooser_entry);
659
	  pop_up_completion_feedback (chooser_entry, _("Invalid path"));
698
	  pop_up_completion_feedback (chooser_entry, _("Invalid path"));
Lines 669-689 append_common_prefix (GtkFileChooserEntry *chooser_entry, Link Here
669
  if (unique_file)
708
  if (unique_file)
670
    {
709
    {
671
      if (!char_after_cursor_is_directory_separator (chooser_entry))
710
      if (!char_after_cursor_is_directory_separator (chooser_entry))
672
	common_prefix = maybe_append_separator_to_file (chooser_entry,
711
	{
673
							unique_file,
712
	  gboolean appended;
674
							common_prefix);
713
714
	  common_prefix = maybe_append_separator_to_file (chooser_entry,
715
							  unique_file,
716
							  common_prefix,
717
							  &appended);
718
	  if (appended)
719
	    prefix_expands_the_file_part = TRUE;
720
	}
675
721
676
      g_object_unref (unique_file);
722
      g_object_unref (unique_file);
677
723
678
      if (common_prefix)
724
      if (prefix_expands_the_file_part)
679
	{
725
	result = COMPLETED_UNIQUE;
680
	  if (prefix_expands_the_file_part)
681
	    result = COMPLETED_UNIQUE;
682
	  else
683
	    result = NOTHING_INSERTED_UNIQUE;
684
	}
685
      else
726
      else
686
	result = INVALID_INPUT;
727
	result = NOTHING_INSERTED_UNIQUE;
687
728
688
      have_result = TRUE;
729
      have_result = TRUE;
689
    }
730
    }
Lines 1105-1136 explicitly_complete (GtkFileChooserEntry *chooser_entry) Link Here
1105
static void
1146
static void
1106
start_explicit_completion (GtkFileChooserEntry *chooser_entry)
1147
start_explicit_completion (GtkFileChooserEntry *chooser_entry)
1107
{
1148
{
1108
  refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1149
  RefreshStatus status;
1150
  gboolean is_error;
1151
  char *feedback_msg;
1109
1152
1110
  if (!chooser_entry->current_folder_file)
1153
  status = refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1154
1155
  is_error = FALSE;
1156
1157
  switch (status)
1111
    {
1158
    {
1112
      /* Here, no folder path means we couldn't parse what the user typed. */
1159
    case REFRESH_OK:
1160
      g_assert (chooser_entry->current_folder_file != NULL);
1113
1161
1114
      beep (chooser_entry);
1162
      if (chooser_entry->current_folder && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
1115
      pop_up_completion_feedback (chooser_entry, _("Invalid path"));
1163
	explicitly_complete (chooser_entry);
1164
      else
1165
	{
1166
	  chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION;
1116
1167
1117
      chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1168
	  /* Translators: this text is shown while the system is searching
1169
	   * for possible completions for filenames in a file chooser entry. */
1170
	  pop_up_completion_feedback (chooser_entry, _("Completing..."));
1171
	}
1172
1173
      break;
1174
1175
    case REFRESH_INVALID_INPUT:
1176
      is_error = TRUE;
1177
      /* Translators: this is shown in the feedback for Tab-completion in a file
1178
       * chooser's text entry, when the user enters an invalid path. */
1179
      feedback_msg = _("Invalid path");
1180
      break;
1181
1182
    case REFRESH_INCOMPLETE_HOSTNAME:
1183
      is_error = TRUE;
1184
1185
      if (chooser_entry->local_only)
1186
	{
1187
	  /* hostnames in a local_only file chooser?  user error */
1188
1189
	  /* Translators: this is shown in the feedback for Tab-completion in a
1190
	   * file chooser's text entry when the user enters something like
1191
	   * "sftp://blahblah" in an app that only supports local filenames. */
1192
	  feedback_msg = _("Only local files may be selected");
1193
	}
1194
      else
1195
	{
1196
	  /* Another option is to complete the hostname based on the remote volumes that are mounted */
1197
1198
	  /* Translators: this is shown in the feedback for Tab-completion in a
1199
	   * file chooser's text entry when the user hasn't entered the first '/'
1200
	   * after a hostname and yet hits Tab (such as "sftp://blahblah[Tab]") */
1201
	  feedback_msg = _("Incomplete hostname; end it with '/'");
1202
	}
1203
1204
      break;
1205
1206
    case REFRESH_NONEXISTENT:
1207
      is_error = TRUE;
1208
1209
      /* Translators: this is shown in the feedback for Tab-completion in a file
1210
       * chooser's text entry when the user enters a path that does not exist
1211
       * and then hits Tab */
1212
      feedback_msg = _("Path does not exist");
1213
      break;
1214
1215
    case REFRESH_NOT_LOCAL:
1216
      is_error = TRUE;
1217
      feedback_msg = _("Only local files may be selected");
1218
      break;
1219
1220
    default:
1221
      g_assert_not_reached ();
1118
      return;
1222
      return;
1119
    }
1223
    }
1120
1224
1121
  if (chooser_entry->current_folder
1225
  if (is_error)
1122
      && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
1123
    {
1124
      explicitly_complete (chooser_entry);
1125
    }
1126
  else
1127
    {
1226
    {
1128
      chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION;
1227
      g_assert (chooser_entry->current_folder_file == NULL);
1129
1228
1130
      /* translators: this text is shown while the system is searching
1229
      beep (chooser_entry);
1131
       * for possible completions for text in a file chooser entry 
1230
      pop_up_completion_feedback (chooser_entry, feedback_msg);
1132
       */
1231
      chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1133
      pop_up_completion_feedback (chooser_entry, _("Completing..."));
1134
    }
1232
    }
1135
}
1233
}
1136
1234
Lines 1196-1201 commit_completion_and_refresh (GtkFileChooserEntry *chooser_entry) Link Here
1196
				 GTK_ENTRY (chooser_entry)->text_length);
1294
				 GTK_ENTRY (chooser_entry)->text_length);
1197
    }
1295
    }
1198
1296
1297
  /* Here we ignore the result of refresh_current_folder_and_file_part(); there is nothing we can do with it */
1199
  refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
1298
  refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
1200
}
1299
}
1201
1300
Lines 1247-1254 populate_completion_store (GtkFileChooserEntry *chooser_entry) Link Here
1247
	{
1346
	{
1248
	  gchar *display_name = g_strdup (g_file_info_get_display_name (info));
1347
	  gchar *display_name = g_strdup (g_file_info_get_display_name (info));
1249
	  GtkTreeIter iter;
1348
	  GtkTreeIter iter;
1349
	  gboolean dummy;
1250
1350
1251
          display_name = maybe_append_separator_to_file (chooser_entry, file, display_name);
1351
          display_name = maybe_append_separator_to_file (chooser_entry, file, display_name, &dummy);
1252
1352
1253
	  gtk_list_store_append (chooser_entry->completion_store, &iter);
1353
	  gtk_list_store_append (chooser_entry->completion_store, &iter);
1254
	  gtk_list_store_set (chooser_entry->completion_store, &iter,
1354
	  gtk_list_store_set (chooser_entry->completion_store, &iter,
Lines 1308-1315 finish_folder_load (GtkFileChooserEntry *chooser_entry) Link Here
1308
1408
1309
/* Callback when the current folder finishes loading */
1409
/* Callback when the current folder finishes loading */
1310
static void
1410
static void
1311
finished_loading_cb (GFile    *file,
1411
finished_loading_cb (GtkFolder *folder,
1312
		     gpointer  data)
1412
		     gpointer   data)
1313
{
1413
{
1314
  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1414
  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1315
1415
Lines 1337-1342 load_directory_get_folder_callback (GCancellable *cancellable, Link Here
1337
1437
1338
      old_load_complete_action = chooser_entry->load_complete_action;
1438
      old_load_complete_action = chooser_entry->load_complete_action;
1339
1439
1440
      discard_completion_store (chooser_entry);
1340
      clear_completions (chooser_entry);
1441
      clear_completions (chooser_entry);
1341
1442
1342
      if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION)
1443
      if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION)
Lines 1347-1357 load_directory_get_folder_callback (GCancellable *cancellable, Link Here
1347
	  pop_up_completion_feedback (chooser_entry, error->message);
1448
	  pop_up_completion_feedback (chooser_entry, error->message);
1348
	}
1449
	}
1349
1450
1350
      if (chooser_entry->current_folder)
1451
      discard_current_folder (chooser_entry);
1351
	{
1352
	  g_object_unref (chooser_entry->current_folder);
1353
	  chooser_entry->current_folder = NULL;
1354
	}
1355
    }
1452
    }
1356
1453
1357
  if (cancelled || error)
1454
  if (cancelled || error)
Lines 1373-1440 out: Link Here
1373
  g_object_unref (cancellable);
1470
  g_object_unref (cancellable);
1374
}
1471
}
1375
1472
1376
static void
1473
static RefreshStatus
1377
start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
1474
start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
1378
{
1475
{
1379
  if (chooser_entry->current_folder_file == NULL ||
1476
  if (chooser_entry->file_system == NULL)
1380
      chooser_entry->file_system == NULL)
1477
    return REFRESH_OK;
1381
    return;
1382
1478
1479
  g_assert (chooser_entry->current_folder_file != NULL);
1383
  g_assert (chooser_entry->current_folder == NULL);
1480
  g_assert (chooser_entry->current_folder == NULL);
1384
  g_assert (chooser_entry->load_folder_cancellable == NULL);
1481
  g_assert (chooser_entry->load_folder_cancellable == NULL);
1385
1482
1483
  if (chooser_entry->local_only
1484
      && !g_file_is_native (chooser_entry->current_folder_file))
1485
    {
1486
      g_object_unref (chooser_entry->current_folder_file);
1487
      chooser_entry->current_folder_file = NULL;
1488
1489
      return REFRESH_NOT_LOCAL;
1490
    }
1491
1386
  chooser_entry->load_folder_cancellable =
1492
  chooser_entry->load_folder_cancellable =
1387
    _gtk_file_system_get_folder (chooser_entry->file_system,
1493
    _gtk_file_system_get_folder (chooser_entry->file_system,
1388
			         chooser_entry->current_folder_file,
1494
			         chooser_entry->current_folder_file,
1389
			 	"standard::name,standard::display-name,standard::type",
1495
			 	"standard::name,standard::display-name,standard::type",
1390
			         load_directory_get_folder_callback,
1496
			         load_directory_get_folder_callback,
1391
			         g_object_ref (chooser_entry));
1497
			         g_object_ref (chooser_entry));
1498
1499
  return REFRESH_OK;
1392
}
1500
}
1393
1501
1394
static void
1502
static RefreshStatus
1395
reload_current_folder (GtkFileChooserEntry *chooser_entry,
1503
reload_current_folder (GtkFileChooserEntry *chooser_entry,
1396
		       GFile               *folder_file,
1504
		       GFile               *folder_file,
1397
		       gboolean             force_reload)
1505
		       gboolean             force_reload)
1398
{
1506
{
1399
  gboolean reload = FALSE;
1507
  gboolean reload = FALSE;
1400
1508
1509
  g_assert (folder_file != NULL);
1510
1401
  if (chooser_entry->current_folder_file)
1511
  if (chooser_entry->current_folder_file)
1402
    {
1512
    {
1403
      if ((folder_file && !g_file_equal (folder_file, chooser_entry->current_folder_file))
1513
      if ((!(g_file_equal (folder_file, chooser_entry->current_folder_file)
1514
	     && chooser_entry->load_folder_cancellable))
1404
	  || force_reload)
1515
	  || force_reload)
1405
	{
1516
	{
1406
	  reload = TRUE;
1517
	  reload = TRUE;
1407
1518
1408
	  /* We changed our current directory.  We need to clear out the old
1519
          discard_current_folder (chooser_entry);
1409
	   * directory information.
1520
	  discard_loading_and_current_folder_file (chooser_entry);
1410
	   */
1411
	  if (chooser_entry->current_folder)
1412
	    {
1413
	      if (chooser_entry->load_folder_cancellable)
1414
		{
1415
		  g_cancellable_cancel (chooser_entry->load_folder_cancellable);
1416
		  chooser_entry->load_folder_cancellable = NULL;
1417
		}
1418
1419
	      g_object_unref (chooser_entry->current_folder);
1420
	      chooser_entry->current_folder = NULL;
1421
	    }
1422
1521
1423
	  g_object_unref (chooser_entry->current_folder_file);
1424
	  chooser_entry->current_folder_file = g_object_ref (folder_file);
1522
	  chooser_entry->current_folder_file = g_object_ref (folder_file);
1425
	}
1523
	}
1426
    }
1524
    }
1427
  else
1525
  else
1428
    {
1526
    {
1429
      chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
1527
      chooser_entry->current_folder_file = g_object_ref (folder_file);
1430
      reload = TRUE;
1528
      reload = TRUE;
1431
    }
1529
    }
1432
1530
1433
  if (reload)
1531
  if (reload)
1434
    start_loading_current_folder (chooser_entry);
1532
    return start_loading_current_folder (chooser_entry);
1533
  else
1534
    return REFRESH_OK;
1435
}
1535
}
1436
1536
1437
static void
1537
static RefreshStatus
1438
refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
1538
refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
1439
				      RefreshMode          refresh_mode)
1539
				      RefreshMode          refresh_mode)
1440
{
1540
{
Lines 1445-1450 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry, Link Here
1445
  gchar *file_part;
1545
  gchar *file_part;
1446
  gsize total_len, file_part_len;
1546
  gsize total_len, file_part_len;
1447
  gint file_part_pos;
1547
  gint file_part_pos;
1548
  GError *error;
1549
  RefreshStatus result;
1448
1550
1449
  editable = GTK_EDITABLE (chooser_entry);
1551
  editable = GTK_EDITABLE (chooser_entry);
1450
1552
Lines 1460-1488 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry, Link Here
1460
1562
1461
    default:
1563
    default:
1462
      g_assert_not_reached ();
1564
      g_assert_not_reached ();
1463
      return;
1565
      return REFRESH_INVALID_INPUT;
1464
    }
1566
    }
1465
1567
1466
  text = gtk_editable_get_chars (editable, 0, end_pos);
1568
  text = gtk_editable_get_chars (editable, 0, end_pos);
1467
  
1569
1570
  error = NULL;
1468
  if (!chooser_entry->file_system ||
1571
  if (!chooser_entry->file_system ||
1469
      !chooser_entry->base_folder ||
1572
      !chooser_entry->base_folder ||
1470
      !_gtk_file_system_parse (chooser_entry->file_system,
1573
      !_gtk_file_system_parse (chooser_entry->file_system,
1471
			       chooser_entry->base_folder, text,
1574
			       chooser_entry->base_folder, text,
1472
			       &folder_file, &file_part, NULL)) /* NULL-GError */
1575
			       &folder_file, &file_part, &error))
1473
    {
1576
    {
1474
      folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
1577
      if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME))
1578
	{
1579
	  folder_file = NULL;
1580
	  result = REFRESH_INCOMPLETE_HOSTNAME;
1581
	}
1582
      else
1583
	{
1584
	  folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
1585
1586
	  if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_NONEXISTENT))
1587
	    result = REFRESH_NONEXISTENT;
1588
	  else
1589
	    result = REFRESH_INVALID_INPUT;
1590
	}
1591
1592
      if (error)
1593
	g_error_free (error);
1594
1475
      file_part = g_strdup ("");
1595
      file_part = g_strdup ("");
1476
      file_part_pos = -1;
1596
      file_part_pos = -1;
1477
    }
1597
    }
1478
  else
1598
  else
1479
    {
1599
    {
1600
      g_assert (folder_file != NULL);
1601
1480
      file_part_len = strlen (file_part);
1602
      file_part_len = strlen (file_part);
1481
      total_len = strlen (text);
1603
      total_len = strlen (text);
1482
      if (total_len > file_part_len)
1604
      if (total_len > file_part_len)
1483
	file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
1605
	file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
1484
      else
1606
      else
1485
	file_part_pos = 0;
1607
	file_part_pos = 0;
1608
1609
      result = REFRESH_OK;
1486
    }
1610
    }
1487
1611
1488
  g_free (text);
1612
  g_free (text);
Lines 1492-1509 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry, Link Here
1492
  chooser_entry->file_part = file_part;
1616
  chooser_entry->file_part = file_part;
1493
  chooser_entry->file_part_pos = file_part_pos;
1617
  chooser_entry->file_part_pos = file_part_pos;
1494
1618
1495
  reload_current_folder (chooser_entry, folder_file, file_part_pos == -1);
1619
  if (result == REFRESH_OK)
1620
    {
1621
      result = reload_current_folder (chooser_entry, folder_file, file_part_pos == -1);
1622
    }
1623
  else
1624
    {
1625
      discard_current_folder (chooser_entry);
1626
      discard_loading_and_current_folder_file (chooser_entry);
1627
    }
1496
1628
1497
  if (folder_file)
1629
  if (folder_file)
1498
    g_object_unref (folder_file);
1630
    g_object_unref (folder_file);
1631
1632
  g_assert (/* we are OK and we have a current folder file and (loading process or folder handle)... */
1633
	    ((result == REFRESH_OK)
1634
	     && (chooser_entry->current_folder_file != NULL)
1635
	     && (((chooser_entry->load_folder_cancellable != NULL) && (chooser_entry->current_folder == NULL))
1636
		 || ((chooser_entry->load_folder_cancellable == NULL) && (chooser_entry->current_folder != NULL))))
1637
	    /* ... OR we have an error, and we don't have a current folder file nor a loading process nor a folder handle */
1638
	    || ((result != REFRESH_OK)
1639
		&& (chooser_entry->current_folder_file == NULL)
1640
		&& (chooser_entry->load_folder_cancellable == NULL)
1641
		&& (chooser_entry->current_folder == NULL)));
1642
1643
  return result;
1499
}
1644
}
1500
1645
1501
static void
1646
static void
1502
autocomplete (GtkFileChooserEntry *chooser_entry)
1647
autocomplete (GtkFileChooserEntry *chooser_entry)
1503
{
1648
{
1504
  g_assert (chooser_entry->current_folder != NULL);
1649
  if (!(chooser_entry->current_folder != NULL
1505
  g_assert (_gtk_folder_is_finished_loading (chooser_entry->current_folder));
1650
	&& _gtk_folder_is_finished_loading (chooser_entry->current_folder)
1506
  g_assert (gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == GTK_ENTRY (chooser_entry)->text_length);
1651
	&& gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == GTK_ENTRY (chooser_entry)->text_length))
1652
    return;
1507
1653
1508
  append_common_prefix (chooser_entry, TRUE, FALSE);
1654
  append_common_prefix (chooser_entry, TRUE, FALSE);
1509
}
1655
}
Lines 1511-1530 autocomplete (GtkFileChooserEntry *chooser_entry) Link Here
1511
static void
1657
static void
1512
start_autocompletion (GtkFileChooserEntry *chooser_entry)
1658
start_autocompletion (GtkFileChooserEntry *chooser_entry)
1513
{
1659
{
1514
  refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1660
  RefreshStatus status;
1661
1662
  status = refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1515
1663
1516
  if (!chooser_entry->current_folder)
1664
  switch (status)
1517
    {
1665
    {
1666
    case REFRESH_OK:
1667
      g_assert (chooser_entry->current_folder_file != NULL);
1668
1669
      if (chooser_entry->current_folder && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
1670
	autocomplete (chooser_entry);
1671
      else
1672
	chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE;
1673
1674
      break;
1675
1676
    case REFRESH_INVALID_INPUT:
1677
    case REFRESH_INCOMPLETE_HOSTNAME:
1678
    case REFRESH_NONEXISTENT:
1679
    case REFRESH_NOT_LOCAL:
1518
      /* We don't beep or anything, since this is autocompletion - the user
1680
      /* We don't beep or anything, since this is autocompletion - the user
1519
       * didn't request any action explicitly.
1681
       * didn't request any action explicitly.
1520
       */
1682
       */
1521
      return;
1683
      break;
1522
    }
1523
1684
1524
  if (_gtk_folder_is_finished_loading (chooser_entry->current_folder))
1685
    default:
1525
    autocomplete (chooser_entry);
1686
      g_assert_not_reached ();
1526
  else
1687
    }
1527
    chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE;
1528
}
1688
}
1529
1689
1530
static gboolean
1690
static gboolean
Lines 1685-1696 _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry, Link Here
1685
 * @chooser_entry: a #GtkFileChooserEntry
1845
 * @chooser_entry: a #GtkFileChooserEntry
1686
 *
1846
 *
1687
 * Gets the current folder for the #GtkFileChooserEntry. If the
1847
 * Gets the current folder for the #GtkFileChooserEntry. If the
1688
 * user has only entered a filename, this will be the base folder
1848
 * user has only entered a filename, this will be in the base folder
1689
 * (see _gtk_file_chooser_entry_set_base_folder()), but if the
1849
 * (see _gtk_file_chooser_entry_set_base_folder()), but if the
1690
 * user has entered a relative or absolute path, then it will
1850
 * user has entered a relative or absolute path, then it will
1691
 * be different. If the user has entered a relative or absolute
1851
 * be different.  If the user has entered unparsable text, or text which
1692
 * path that doesn't point to a folder in the file system, it will
1852
 * the entry cannot handle, this will return %NULL.
1693
 * be %NULL.
1694
 *
1853
 *
1695
 * Return value: the file for the current folder - this value is owned by the
1854
 * Return value: the file for the current folder - this value is owned by the
1696
 *  chooser entry and must not be modified or freed.
1855
 *  chooser entry and must not be modified or freed.
Lines 1845-1847 _gtk_file_chooser_entry_select_filename (GtkFileChooserEntry *chooser_entry) Link Here
1845
  gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
2004
  gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
1846
}
2005
}
1847
2006
2007
void
2008
_gtk_file_chooser_entry_set_local_only (GtkFileChooserEntry *chooser_entry,
2009
                                        gboolean             local_only)
2010
{
2011
  chooser_entry->local_only = local_only;
2012
  clear_completions (chooser_entry);
2013
}
2014
2015
gboolean
2016
_gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
2017
{
2018
  return chooser_entry->local_only;
2019
}
(-)a/gtk/gtkfilechooserentry.h (+3 lines)
Lines 48-53 const gchar * _gtk_file_chooser_entry_get_file_part (GtkFileChooserEnt Link Here
48
gboolean           _gtk_file_chooser_entry_get_is_folder      (GtkFileChooserEntry *chooser_entry,
48
gboolean           _gtk_file_chooser_entry_get_is_folder      (GtkFileChooserEntry *chooser_entry,
49
							       GFile               *file);
49
							       GFile               *file);
50
void               _gtk_file_chooser_entry_select_filename    (GtkFileChooserEntry *chooser_entry);
50
void               _gtk_file_chooser_entry_select_filename    (GtkFileChooserEntry *chooser_entry);
51
void               _gtk_file_chooser_entry_set_local_only     (GtkFileChooserEntry *chooser_entry,
52
                                                               gboolean             local_only);
53
gboolean           _gtk_file_chooser_entry_get_local_only     (GtkFileChooserEntry *chooser_entry);
51
54
52
G_END_DECLS
55
G_END_DECLS
53
56
(-)a/gtk/gtkfilesystem.c (-1 / +55 lines)
Lines 596-601 _gtk_file_system_list_bookmarks (GtkFileSystem *file_system) Link Here
596
  return g_slist_reverse (files);
596
  return g_slist_reverse (files);
597
}
597
}
598
598
599
static gboolean
600
is_valid_scheme_character (char c)
601
{
602
  return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
603
}
604
605
static gboolean
606
has_uri_scheme (const char *str)
607
{
608
  const char *p;
609
610
  p = str;
611
612
  if (!is_valid_scheme_character (*p))
613
    return FALSE;
614
615
  do
616
    p++;
617
  while (is_valid_scheme_character (*p));
618
619
  return (strncmp (p, "://", 3) == 0);
620
}
621
599
gboolean
622
gboolean
600
_gtk_file_system_parse (GtkFileSystem     *file_system,
623
_gtk_file_system_parse (GtkFileSystem     *file_system,
601
		        GFile             *base_file,
624
		        GFile             *base_file,
Lines 608-613 _gtk_file_system_parse (GtkFileSystem *file_system, Link Here
608
  gboolean result = FALSE;
631
  gboolean result = FALSE;
609
  gboolean is_dir = FALSE;
632
  gboolean is_dir = FALSE;
610
  gchar *last_slash = NULL;
633
  gchar *last_slash = NULL;
634
  gboolean is_uri;
611
635
612
  DEBUG ("parse");
636
  DEBUG ("parse");
613
637
Lines 616-622 _gtk_file_system_parse (GtkFileSystem *file_system, Link Here
616
640
617
  last_slash = strrchr (str, G_DIR_SEPARATOR);
641
  last_slash = strrchr (str, G_DIR_SEPARATOR);
618
642
619
  if (str[0] == '~')
643
  is_uri = has_uri_scheme (str);
644
645
  if (is_uri)
646
    {
647
      const char *colon;
648
      const char *slash_after_hostname;
649
650
      colon = strchr (str, ':');
651
      g_assert (colon != NULL);
652
      g_assert (strncmp (colon, "://", 3) == 0);
653
654
      slash_after_hostname = strchr (colon + 3, '/');
655
656
      if (slash_after_hostname == NULL)
657
	{
658
	  /* We don't have a full hostname yet.  So, don't switch the folder
659
	   * until we have seen a full hostname.  Otherwise, completion will
660
	   * happen for every character the user types for the hostname.
661
	   */
662
663
	  *folder = NULL;
664
	  *file_part = NULL;
665
	  g_set_error (error,
666
		       GTK_FILE_CHOOSER_ERROR,
667
		       GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME,
668
		       "Incomplete hostname");
669
	  return FALSE;
670
	}
671
    }
672
673
  if (str[0] == '~' || g_path_is_absolute (str) || is_uri)
620
    file = g_file_parse_name (str);
674
    file = g_file_parse_name (str);
621
  else
675
  else
622
    file = g_file_resolve_relative_path (base_file, str);
676
    file = g_file_resolve_relative_path (base_file, str);

Return to bug 459744