gimppatheditor.c 26.3 KB
Newer Older
Michael Natterer's avatar
Michael Natterer committed
1
/* LIBGIMP - The GIMP Library
2
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 4
 *
 * gimppatheditor.c
5
 * Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
6
 *
7
 * This library is free software: you can redistribute it and/or
Marc Lehmann's avatar
Marc Lehmann committed
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 3 of the License, or (at your option) any later version.
Michael Natterer's avatar
Michael Natterer committed
11 12 13 14
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 16
 * Library General Public License for more details.
 *
Marc Lehmann's avatar
Marc Lehmann committed
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
19
 * <https://www.gnu.org/licenses/>.
20
 */
21

22 23
#include "config.h"

24 25
#include <string.h>

26 27
#include <gtk/gtk.h>

28 29
#include "libgimpbase/gimpbase.h"

30
#include "gimpwidgetstypes.h"
31

32
#undef GIMP_DISABLE_DEPRECATED
Michael Natterer's avatar
Michael Natterer committed
33
#include "gimpfileentry.h"
34

35
#include "gimphelpui.h"
36
#include "gimpicons.h"
37 38
#include "gimppatheditor.h"

39 40
#include "libgimp/libgimp-intl.h"

41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/**
 * SECTION: gimppatheditor
 * @title: GimpPathEditor
 * @short_description: Widget for editing a file search path.
 * @see_also: #GimpFileEntry, #G_SEARCHPATH_SEPARATOR
 *
 * This widget is used to edit file search paths.
 *
 * It shows a list of all directories which are in the search
 * path. You can click a directory to select it. The widget provides a
 * #GimpFileEntry to change the currently selected directory.
 *
 * There are buttons to add or delete directories as well as "up" and
 * "down" buttons to change the order in which the directories will be
 * searched.
 *
 * Whenever the user adds, deletes, changes or reorders a directory of
 * the search path, the "path_changed" signal will be emitted.
 **/


63 64
enum
{
65
  PATH_CHANGED,
66
  WRITABLE_CHANGED,
67 68 69
  LAST_SIGNAL
};

70 71 72 73 74 75 76 77
enum
{
  COLUMN_UTF8,
  COLUMN_DIRECTORY,
  COLUMN_WRITABLE,
  NUM_COLUMNS
};

78

79 80 81 82 83 84 85 86 87 88 89 90 91
static void   gimp_path_editor_new_clicked        (GtkWidget           *widget,
                                                   GimpPathEditor      *editor);
static void   gimp_path_editor_move_clicked       (GtkWidget           *widget,
                                                   GimpPathEditor      *editor);
static void   gimp_path_editor_delete_clicked     (GtkWidget           *widget,
                                                   GimpPathEditor      *editor);
static void   gimp_path_editor_file_entry_changed (GtkWidget           *widget,
                                                   GimpPathEditor      *editor);
static void   gimp_path_editor_selection_changed  (GtkTreeSelection    *sel,
                                                   GimpPathEditor      *editor);
static void   gimp_path_editor_writable_toggled   (GtkCellRendererToggle *toggle,
                                                   gchar               *path_str,
                                                   GimpPathEditor      *editor);
92 93


94
G_DEFINE_TYPE (GimpPathEditor, gimp_path_editor, GTK_TYPE_BOX)
95

96
#define parent_class gimp_path_editor_parent_class
97

98
static guint gimp_path_editor_signals[LAST_SIGNAL] = { 0 };
99 100


101
static void
102
gimp_path_editor_class_init (GimpPathEditorClass *klass)
103
{
104 105 106 107 108 109
  /**
   * GimpPathEditor::path-changed:
   *
   * This signal is emitted whenever the user adds, deletes, modifies
   * or reorders an element of the search path.
   **/
Michael Natterer's avatar
Michael Natterer committed
110
  gimp_path_editor_signals[PATH_CHANGED] =
111
    g_signal_new ("path-changed",
Sven Neumann's avatar
Sven Neumann committed
112 113 114 115 116 117
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpPathEditorClass, path_changed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
118

119 120 121 122 123 124 125 126
  /**
   * GimpPathEditor::writable-changed:
   *
   * This signal is emitted whenever the "writable" column of a directory
   * is changed, either by the user clicking on it or by calling
   * gimp_path_editor_set_dir_writable().
   **/
  gimp_path_editor_signals[WRITABLE_CHANGED] =
127
    g_signal_new ("writable-changed",
Sven Neumann's avatar
Sven Neumann committed
128 129 130 131 132 133
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpPathEditorClass, writable_changed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
134 135 136

  klass->path_changed     = NULL;
  klass->writable_changed = NULL;
137 138 139
}

static void
140
gimp_path_editor_init (GimpPathEditor *editor)
141
{
142 143 144 145 146 147 148
  GtkWidget         *button_box;
  GtkWidget         *button;
  GtkWidget         *image;
  GtkWidget         *scrolled_window;
  GtkWidget         *tv;
  GtkTreeViewColumn *col;
  GtkCellRenderer   *renderer;
149

150 151 152
  editor->file_entry = NULL;
  editor->sel_path   = NULL;
  editor->num_items  = 0;
153

154 155 156
  gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
                                  GTK_ORIENTATION_VERTICAL);

157
  editor->upper_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
158 159
  gtk_box_pack_start (GTK_BOX (editor), editor->upper_hbox, FALSE, TRUE, 0);
  gtk_widget_show (editor->upper_hbox);
160

161 162
  button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_set_homogeneous (GTK_BOX (button_box), TRUE);
163
  gtk_box_pack_start (GTK_BOX (editor->upper_hbox), button_box, FALSE, TRUE, 0);
164 165
  gtk_widget_show (button_box);

166
  editor->new_button = button = gtk_button_new ();
167
  gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
168 169
  gtk_widget_show (button);

170 171
  image = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_NEW,
                                        GTK_ICON_SIZE_BUTTON);
172 173 174
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show (image);

175
  g_signal_connect (button, "clicked",
Michael Natterer's avatar
Michael Natterer committed
176
                    G_CALLBACK (gimp_path_editor_new_clicked),
Sven Neumann's avatar
Sven Neumann committed
177
                    editor);
178

179 180 181 182
  gimp_help_set_help_data (editor->new_button,
                           _("Add a new folder"),
                           NULL);

183
  editor->up_button = button = gtk_button_new ();
184 185
  gtk_widget_set_sensitive (button, FALSE);
  gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
186 187
  gtk_widget_show (button);

188 189
  image = gtk_image_new_from_icon_name (GIMP_ICON_GO_UP,
                                        GTK_ICON_SIZE_BUTTON);
190 191 192
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show (image);

193
  g_signal_connect (button, "clicked",
Michael Natterer's avatar
Michael Natterer committed
194
                    G_CALLBACK (gimp_path_editor_move_clicked),
Sven Neumann's avatar
Sven Neumann committed
195
                    editor);
196

197 198 199 200
  gimp_help_set_help_data (editor->up_button,
                           _("Move the selected folder up"),
                           NULL);

201
  editor->down_button = button = gtk_button_new ();
202 203
  gtk_widget_set_sensitive (button, FALSE);
  gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
204 205
  gtk_widget_show (button);

206 207
  image = gtk_image_new_from_icon_name (GIMP_ICON_GO_DOWN,
                                        GTK_ICON_SIZE_BUTTON);
208 209 210
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show (image);

211
  g_signal_connect (button, "clicked",
Michael Natterer's avatar
Michael Natterer committed
212
                    G_CALLBACK (gimp_path_editor_move_clicked),
Sven Neumann's avatar
Sven Neumann committed
213
                    editor);
214

215 216 217 218
  gimp_help_set_help_data (editor->down_button,
                           _("Move the selected folder down"),
                           NULL);

219
  editor->delete_button = button = gtk_button_new ();
220 221
  gtk_widget_set_sensitive (button, FALSE);
  gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
222 223
  gtk_widget_show (button);

224 225
  image = gtk_image_new_from_icon_name (GIMP_ICON_EDIT_DELETE,
                                        GTK_ICON_SIZE_BUTTON);
226 227 228
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show (image);

229
  g_signal_connect (button, "clicked",
Michael Natterer's avatar
Michael Natterer committed
230
                    G_CALLBACK (gimp_path_editor_delete_clicked),
Sven Neumann's avatar
Sven Neumann committed
231
                    editor);
232

233 234 235 236
  gimp_help_set_help_data (editor->delete_button,
                           _("Remove the selected folder from the list"),
                           NULL);

237
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
238
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
Sven Neumann's avatar
Sven Neumann committed
239
                                       GTK_SHADOW_IN);
240 241 242
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_ALWAYS);
243
  gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 2);
244
  gtk_widget_show (scrolled_window);
Michael Natterer's avatar
Michael Natterer committed
245

246 247 248 249
  editor->dir_list = gtk_list_store_new (NUM_COLUMNS,
                                         G_TYPE_STRING,
                                         G_TYPE_STRING,
                                         G_TYPE_BOOLEAN);
250 251
  tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (editor->dir_list));
  g_object_unref (editor->dir_list);
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
  renderer = gtk_cell_renderer_toggle_new ();

  g_signal_connect (renderer, "toggled",
                    G_CALLBACK (gimp_path_editor_writable_toggled),
                    editor);

  editor->writable_column = col = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (col, _("Writable"));
  gtk_tree_view_column_pack_start (col, renderer, FALSE);
  gtk_tree_view_column_add_attribute (col, renderer, "active", COLUMN_WRITABLE);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col);

  gtk_tree_view_column_set_visible (col, FALSE);

268
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
Sven Neumann's avatar
Sven Neumann committed
269 270 271 272
                                               -1, _("Folder"),
                                               gtk_cell_renderer_text_new (),
                                               "text", COLUMN_UTF8,
                                               NULL);
273

274
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), TRUE);
275 276 277 278

  gtk_container_add (GTK_CONTAINER (scrolled_window), tv);
  gtk_widget_show (tv);

279 280
  editor->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
  g_signal_connect (editor->sel, "changed",
Sven Neumann's avatar
Sven Neumann committed
281 282
                    G_CALLBACK (gimp_path_editor_selection_changed),
                    editor);
283 284
}

285 286
/**
 * gimp_path_editor_new:
287 288
 * @title: The title of the #GtkFileChooser dialog which can be popped up.
 * @path:  The initial search path.
289 290 291 292 293 294 295
 *
 * Creates a new #GimpPathEditor widget.
 *
 * The elements of the initial search path must be separated with the
 * #G_SEARCHPATH_SEPARATOR character.
 *
 * Returns: A pointer to the new #GimpPathEditor widget.
296
 **/
297
GtkWidget *
298
gimp_path_editor_new (const gchar *title,
Sven Neumann's avatar
Sven Neumann committed
299
                      const gchar *path)
300
{
301
  GimpPathEditor *editor;
302

303
  g_return_val_if_fail (title != NULL, NULL);
304

305
  editor = g_object_new (GIMP_TYPE_PATH_EDITOR, NULL);
306

307
  editor->file_entry = gimp_file_entry_new (title, "", TRUE, TRUE);
308 309
  gtk_widget_set_sensitive (editor->file_entry, FALSE);
  gtk_box_pack_start (GTK_BOX (editor->upper_hbox), editor->file_entry,
Michael Natterer's avatar
Michael Natterer committed
310
                      TRUE, TRUE, 0);
311
  gtk_widget_show (editor->file_entry);
312

313
  g_signal_connect (editor->file_entry, "filename-changed",
Michael Natterer's avatar
Michael Natterer committed
314
                    G_CALLBACK (gimp_path_editor_file_entry_changed),
Sven Neumann's avatar
Sven Neumann committed
315
                    editor);
316

317
  if (path)
318
    gimp_path_editor_set_path (editor, path);
319

320
  return GTK_WIDGET (editor);
321 322
}

323 324
/**
 * gimp_path_editor_get_path:
325
 * @editor: The path editor you want to get the search path from.
326 327 328 329 330 331 332
 *
 * The elements of the returned search path string are separated with the
 * #G_SEARCHPATH_SEPARATOR character.
 *
 * Note that you have to g_free() the returned string.
 *
 * Returns: The search path the user has selected in the path editor.
333
 **/
334
gchar *
335
gimp_path_editor_get_path (GimpPathEditor *editor)
336
{
337 338 339 340
  GtkTreeModel *model;
  GString      *path;
  GtkTreeIter   iter;
  gboolean      iter_valid;
341

342
  g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));
343

344 345
  model = GTK_TREE_MODEL (editor->dir_list);

346
  path = g_string_new ("");
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      gchar *dir;

      gtk_tree_model_get (model, &iter,
                          COLUMN_DIRECTORY, &dir,
                          -1);

      if (path->len > 0)
        g_string_append_c (path, G_SEARCHPATH_SEPARATOR);

      g_string_append (path, dir);

      g_free (dir);
    }
365 366

  return g_string_free (path, FALSE);
367 368
}

369 370
/**
 * gimp_path_editor_set_path:
371 372
 * @editor: The path editor you want to set the search path from.
 * @path:   The new path to set.
373 374 375 376 377
 *
 * The elements of the initial search path must be separated with the
 * #G_SEARCHPATH_SEPARATOR character.
 **/
void
378
gimp_path_editor_set_path (GimpPathEditor *editor,
379 380
                           const gchar    *path)
{
381 382 383
  gchar *old_path;
  GList *path_list;
  GList *list;
384

385
  g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
386

387 388
  old_path = gimp_path_editor_get_path (editor);

389
  if (old_path && path && strcmp (old_path, path) == 0)
390 391 392 393 394
    {
      g_free (old_path);
      return;
    }

395 396
  g_free (old_path);

397
  path_list = gimp_path_parse (path, 256, FALSE, NULL);
398

399
  gtk_list_store_clear (editor->dir_list);
400

401
  for (list = path_list; list; list = g_list_next (list))
402
    {
403 404 405
      gchar       *directory = list->data;
      gchar       *utf8;
      GtkTreeIter  iter;
406

407
      utf8 = g_filename_to_utf8 (directory, -1, NULL, NULL, NULL);
408

409
      gtk_list_store_append (editor->dir_list, &iter);
410 411 412 413 414
      gtk_list_store_set (editor->dir_list, &iter,
                          COLUMN_UTF8,      utf8,
                          COLUMN_DIRECTORY, directory,
                          COLUMN_WRITABLE,  FALSE,
                          -1);
415

416 417
      g_free (utf8);

418
      editor->num_items++;
419 420
    }

421
  gimp_path_free (path_list);
422 423

  g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
424 425
}

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
gchar *
gimp_path_editor_get_writable_path (GimpPathEditor *editor)
{
  GtkTreeModel *model;
  GString      *path;
  GtkTreeIter   iter;
  gboolean      iter_valid;

  g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));

  model = GTK_TREE_MODEL (editor->dir_list);

  path = g_string_new ("");

  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      gchar    *dir;
      gboolean  dir_writable;

      gtk_tree_model_get (model, &iter,
                          COLUMN_DIRECTORY, &dir,
                          COLUMN_WRITABLE,  &dir_writable,
                          -1);

      if (dir_writable)
        {
          if (path->len > 0)
            g_string_append_c (path, G_SEARCHPATH_SEPARATOR);

          g_string_append (path, dir);
        }

      g_free (dir);
    }

  return g_string_free (path, FALSE);
}

void
gimp_path_editor_set_writable_path (GimpPathEditor *editor,
                                    const gchar    *path)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;
  gboolean      iter_valid;
  GList        *path_list;
  gboolean      writable_changed = FALSE;

  g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));

  gtk_tree_view_column_set_visible (editor->writable_column, TRUE);

480
  path_list = gimp_path_parse (path, 256, FALSE, NULL);
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597

  model = GTK_TREE_MODEL (editor->dir_list);

  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      gchar    *dir;
      gboolean  dir_writable;
      gboolean  new_writable = FALSE;

      gtk_tree_model_get (model, &iter,
                          COLUMN_DIRECTORY, &dir,
                          COLUMN_WRITABLE,  &dir_writable,
                          -1);

      if (g_list_find_custom (path_list, dir, (GCompareFunc) strcmp))
        new_writable = TRUE;

      g_free (dir);

      if (dir_writable != new_writable)
        {
          gtk_list_store_set (editor->dir_list, &iter,
                              COLUMN_WRITABLE, new_writable,
                              -1);

          writable_changed = TRUE;
        }
    }

  gimp_path_free (path_list);

  if (writable_changed)
    g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
}

gboolean
gimp_path_editor_get_dir_writable (GimpPathEditor *editor,
                                   const gchar    *directory)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;
  gboolean      iter_valid;

  g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), FALSE);
  g_return_val_if_fail (directory != NULL, FALSE);

  model = GTK_TREE_MODEL (editor->dir_list);

  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      gchar    *dir;
      gboolean  dir_writable;

      gtk_tree_model_get (model, &iter,
                          COLUMN_DIRECTORY, &dir,
                          COLUMN_WRITABLE,  &dir_writable,
                          -1);

      if (! strcmp (dir, directory))
        {
          g_free (dir);

          return dir_writable;
        }

      g_free (dir);
    }

  return FALSE;
}

void
gimp_path_editor_set_dir_writable (GimpPathEditor *editor,
                                   const gchar    *directory,
                                   gboolean        writable)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;
  gboolean      iter_valid;

  g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
  g_return_if_fail (directory != NULL);

  model = GTK_TREE_MODEL (editor->dir_list);

  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      gchar    *dir;
      gboolean  dir_writable;

      gtk_tree_model_get (model, &iter,
                          COLUMN_DIRECTORY, &dir,
                          COLUMN_WRITABLE,  &dir_writable,
                          -1);

      if (! strcmp (dir, directory) && dir_writable != writable)
        {
          gtk_list_store_set (editor->dir_list, &iter,
                              COLUMN_WRITABLE, writable ? TRUE : FALSE,
                              -1);

          g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);

          g_free (dir);
          break;
        }

      g_free (dir);
    }
}

598

Michael Natterer's avatar
Michael Natterer committed
599
/*  private functions  */
600 601

static void
Michael Natterer's avatar
Michael Natterer committed
602
gimp_path_editor_new_clicked (GtkWidget      *widget,
603
                              GimpPathEditor *editor)
604
{
605
  if (editor->sel_path)
606
    {
607
      g_signal_handlers_block_by_func (editor->sel,
Sven Neumann's avatar
Sven Neumann committed
608
                                       gimp_path_editor_selection_changed,
609
                                       editor);
Michael Natterer's avatar
Michael Natterer committed
610

611
      gtk_tree_selection_unselect_path (editor->sel, editor->sel_path);
Michael Natterer's avatar
Michael Natterer committed
612

613
      g_signal_handlers_unblock_by_func (editor->sel,
Sven Neumann's avatar
Sven Neumann committed
614
                                         gimp_path_editor_selection_changed,
615
                                         editor);
616

617 618
      gtk_tree_path_free (editor->sel_path);
      editor->sel_path = NULL;
619 620
    }

621 622 623 624
  gtk_widget_set_sensitive (editor->delete_button, FALSE);
  gtk_widget_set_sensitive (editor->up_button, FALSE);
  gtk_widget_set_sensitive (editor->down_button, FALSE);
  gtk_widget_set_sensitive (editor->file_entry, TRUE);
625

626
  gtk_editable_set_position
627
    (GTK_EDITABLE (GIMP_FILE_ENTRY (editor->file_entry)->entry), -1);
628
  gtk_widget_grab_focus
629
    (GTK_WIDGET (GIMP_FILE_ENTRY (editor->file_entry)->entry));
630 631
}

632
static void
Michael Natterer's avatar
Michael Natterer committed
633
gimp_path_editor_move_clicked (GtkWidget      *widget,
634
                               GimpPathEditor *editor)
635
{
Michael Natterer's avatar
Michael Natterer committed
636 637 638
  GtkTreePath  *path;
  GtkTreeModel *model;
  GtkTreeIter   iter1, iter2;
639
  gchar        *utf81, *utf82;
Michael Natterer's avatar
Michael Natterer committed
640
  gchar        *dir1, *dir2;
641
  gboolean      writable1, writable2;
642

643
  if (editor->sel_path == NULL)
644 645
    return;

646
  path = gtk_tree_path_copy (editor->sel_path);
647

648
  if (widget == editor->up_button)
649 650 651 652
    gtk_tree_path_prev (path);
  else
    gtk_tree_path_next (path);

653
  model = GTK_TREE_MODEL (editor->dir_list);
654

655
  gtk_tree_model_get_iter (model, &iter1, editor->sel_path);
656 657
  gtk_tree_model_get_iter (model, &iter2, path);

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
  gtk_tree_model_get (model, &iter1,
                      COLUMN_UTF8,      &utf81,
                      COLUMN_DIRECTORY, &dir1,
                      COLUMN_WRITABLE,  &writable1,
                      -1);
  gtk_tree_model_get (model, &iter2,
                      COLUMN_UTF8,      &utf82,
                      COLUMN_DIRECTORY, &dir2,
                      COLUMN_WRITABLE,  &writable2,
                      -1);

  gtk_list_store_set (editor->dir_list, &iter1,
                      COLUMN_UTF8,      utf82,
                      COLUMN_DIRECTORY, dir2,
                      COLUMN_WRITABLE,  writable2,
                      -1);
  gtk_list_store_set (editor->dir_list, &iter2,
                      COLUMN_UTF8,      utf81,
                      COLUMN_DIRECTORY, dir1,
                      COLUMN_WRITABLE,  writable1,
                      -1);

  g_free (utf81);
  g_free (utf82);
682 683 684 685

  g_free (dir2);
  g_free (dir1);

686
  gtk_tree_selection_select_iter (editor->sel, &iter2);
687

688
  g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
689 690
}

691
static void
Michael Natterer's avatar
Michael Natterer committed
692
gimp_path_editor_delete_clicked (GtkWidget      *widget,
693
                                 GimpPathEditor *editor)
694
{
695 696
  GtkTreeIter iter;
  gboolean    dir_writable;
697

698
  if (editor->sel_path == NULL)
699 700
    return;

701
  gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
Sven Neumann's avatar
Sven Neumann committed
702
                           editor->sel_path);
703 704 705 706 707

  gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
                      COLUMN_WRITABLE, &dir_writable,
                      -1);

708
  gtk_list_store_remove (editor->dir_list, &iter);
709

710
  editor->num_items--;
711

712
  if (editor->num_items == 0)
713
    {
714 715
      gtk_tree_path_free (editor->sel_path);
      editor->sel_path = NULL;
716

717
      g_signal_handlers_block_by_func (editor->file_entry,
Michael Natterer's avatar
Michael Natterer committed
718
                                       gimp_path_editor_file_entry_changed,
719
                                       editor);
Michael Natterer's avatar
Michael Natterer committed
720

721
      gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry), "");
Michael Natterer's avatar
Michael Natterer committed
722

723
      g_signal_handlers_unblock_by_func (editor->file_entry,
Michael Natterer's avatar
Michael Natterer committed
724
                                         gimp_path_editor_file_entry_changed,
725
                                         editor);
726

727 728 729 730
      gtk_widget_set_sensitive (editor->delete_button, FALSE);
      gtk_widget_set_sensitive (editor->up_button,     FALSE);
      gtk_widget_set_sensitive (editor->down_button,   FALSE);
      gtk_widget_set_sensitive (editor->file_entry,    FALSE);
731
    }
732 733 734
  else
    {
      gint *indices;
735

736 737 738
      indices = gtk_tree_path_get_indices (editor->sel_path);
      if ((indices[0] == editor->num_items) && (indices[0] > 0))
        gtk_tree_path_prev (editor->sel_path);
739

740 741
      gtk_tree_selection_select_path (editor->sel, editor->sel_path);
    }
742

743
  g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
744 745 746

  if (dir_writable)
    g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
747 748 749
}

static void
Michael Natterer's avatar
Michael Natterer committed
750
gimp_path_editor_file_entry_changed (GtkWidget      *widget,
751
                                     GimpPathEditor *editor)
752
{
753 754
  gchar       *dir;
  gchar       *utf8;
Michael Natterer's avatar
Michael Natterer committed
755
  GtkTreeIter  iter;
756

757 758
  dir = gimp_file_entry_get_filename (GIMP_FILE_ENTRY (widget));
  if (strcmp (dir, "") == 0)
759
    {
760
      g_free (dir);
761 762
      return;
    }
763

764
  utf8 = g_filename_display_name (dir);
765

766
  if (editor->sel_path == NULL)
767
    {
768
      gtk_list_store_append (editor->dir_list, &iter);
769 770 771 772 773
      gtk_list_store_set (editor->dir_list, &iter,
                          COLUMN_UTF8,      utf8,
                          COLUMN_DIRECTORY, dir,
                          COLUMN_WRITABLE,  FALSE,
                          -1);
774
      editor->num_items++;
775

776
      gtk_tree_selection_select_iter (editor->sel, &iter);
777 778 779
    }
  else
    {
780
      gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
Sven Neumann's avatar
Sven Neumann committed
781
                               editor->sel_path);
782 783 784 785
      gtk_list_store_set (editor->dir_list, &iter,
                          COLUMN_UTF8,      utf8,
                          COLUMN_DIRECTORY, dir,
                          -1);
786 787
    }

788 789
  g_free (dir);
  g_free (utf8);
790

791
  g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
792
}
Michael Natterer's avatar
Michael Natterer committed
793 794 795

static void
gimp_path_editor_selection_changed (GtkTreeSelection *sel,
796
                                    GimpPathEditor   *editor)
Michael Natterer's avatar
Michael Natterer committed
797 798 799 800 801 802 803
{
  GtkTreeIter  iter;
  gchar       *directory;
  gint        *indices;

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
    {
804
      gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
805
                          COLUMN_DIRECTORY, &directory,
806
                          -1);
Michael Natterer's avatar
Michael Natterer committed
807

808
      g_signal_handlers_block_by_func (editor->file_entry,
809 810
                                       gimp_path_editor_file_entry_changed,
                                       editor);
Michael Natterer's avatar
Michael Natterer committed
811

812
      gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry),
Michael Natterer's avatar
Michael Natterer committed
813 814
                                    directory);

815
      g_signal_handlers_unblock_by_func (editor->file_entry,
816 817
                                         gimp_path_editor_file_entry_changed,
                                         editor);
Michael Natterer's avatar
Michael Natterer committed
818 819 820

      g_free (directory);

821
      if (editor->sel_path)
Sven Neumann's avatar
Sven Neumann committed
822
        gtk_tree_path_free (editor->sel_path);
Michael Natterer's avatar
Michael Natterer committed
823

824 825
      editor->sel_path =
        gtk_tree_model_get_path (GTK_TREE_MODEL (editor->dir_list), &iter);
Michael Natterer's avatar
Michael Natterer committed
826

827
      indices = gtk_tree_path_get_indices (editor->sel_path);
Michael Natterer's avatar
Michael Natterer committed
828

829 830 831
      gtk_widget_set_sensitive (editor->delete_button, TRUE);
      gtk_widget_set_sensitive (editor->up_button, (indices[0] > 0));
      gtk_widget_set_sensitive (editor->down_button,
832
                                (indices[0] < (editor->num_items - 1)));
833
      gtk_widget_set_sensitive (editor->file_entry, TRUE);
Michael Natterer's avatar
Michael Natterer committed
834 835 836 837
    }
  else
    {
      g_signal_handlers_block_by_func (sel,
838 839
                                       gimp_path_editor_selection_changed,
                                       editor);
Michael Natterer's avatar
Michael Natterer committed
840

841
      gtk_tree_selection_select_path (editor->sel, editor->sel_path);
Michael Natterer's avatar
Michael Natterer committed
842 843

      g_signal_handlers_unblock_by_func (sel,
844 845
                                         gimp_path_editor_selection_changed,
                                         editor);
Michael Natterer's avatar
Michael Natterer committed
846 847 848
    }
}

849 850 851 852
static void
gimp_path_editor_writable_toggled (GtkCellRendererToggle *toggle,
                                   gchar                 *path_str,
                                   GimpPathEditor        *editor)
Michael Natterer's avatar
Michael Natterer committed
853
{
854 855
  GtkTreePath *path;
  GtkTreeIter  iter;
Michael Natterer's avatar
Michael Natterer committed
856

857
  path = gtk_tree_path_new_from_string (path_str);
Michael Natterer's avatar
Michael Natterer committed
858

859 860 861
  if (gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter, path))
    {
      gboolean dir_writable;
Michael Natterer's avatar
Michael Natterer committed
862

863 864 865
      gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
                          COLUMN_WRITABLE,  &dir_writable,
                          -1);
Michael Natterer's avatar
Michael Natterer committed
866

867 868 869
      gtk_list_store_set (editor->dir_list, &iter,
                          COLUMN_WRITABLE, ! dir_writable,
                          -1);
Michael Natterer's avatar
Michael Natterer committed
870

871 872 873 874
      g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
    }

  gtk_tree_path_free (path);
Michael Natterer's avatar
Michael Natterer committed
875
}