~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to src/project-tree/gb-project-tree-actions.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* gb-project-tree-actions.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
5
 * This program is free software: you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation, either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "gb-project-tree-actions"
 
20
 
 
21
#include <glib/gi18n.h>
 
22
#include <gio/gdesktopappinfo.h>
 
23
 
 
24
#include "gb-editor-workspace.h"
 
25
#include "gb-file-manager.h"
 
26
#include "gb-new-file-popover.h"
 
27
#include "gb-project-tree.h"
 
28
#include "gb-project-tree-actions.h"
 
29
#include "gb-project-tree-private.h"
 
30
#include "gb-rename-file-popover.h"
 
31
#include "gb-view-stack.h"
 
32
#include "gb-widget.h"
 
33
#include "gb-workbench.h"
 
34
 
 
35
static void
 
36
action_set (GActionGroup *group,
 
37
            const gchar  *action_name,
 
38
            const gchar  *first_param,
 
39
            ...)
 
40
{
 
41
  GAction *action;
 
42
  va_list args;
 
43
 
 
44
  g_assert (G_IS_ACTION_GROUP (group));
 
45
  g_assert (G_IS_ACTION_MAP (group));
 
46
 
 
47
  action = g_action_map_lookup_action (G_ACTION_MAP (group), action_name);
 
48
  g_assert (G_IS_SIMPLE_ACTION (action));
 
49
 
 
50
  va_start (args, first_param);
 
51
  g_object_set_valist (G_OBJECT (action), first_param, args);
 
52
  va_end (args);
 
53
}
 
54
 
 
55
static gboolean
 
56
project_file_is_directory (GObject *object)
 
57
{
 
58
  g_assert (!object || G_IS_OBJECT (object));
 
59
 
 
60
  return (IDE_IS_PROJECT_FILE (object) &&
 
61
          ide_project_file_get_is_directory (IDE_PROJECT_FILE (object)));
 
62
}
 
63
 
 
64
static void
 
65
gb_project_tree_actions_refresh (GSimpleAction *action,
 
66
                                 GVariant      *variant,
 
67
                                 gpointer       user_data)
 
68
{
 
69
  GbProjectTree *self = user_data;
 
70
  GbTreeNode *selected;
 
71
  GObject *item = NULL;
 
72
 
 
73
  g_assert (GB_IS_PROJECT_TREE (self));
 
74
 
 
75
  if ((selected = gb_tree_get_selected (GB_TREE (self))))
 
76
    {
 
77
      item = gb_tree_node_get_item (selected);
 
78
      if (item != NULL)
 
79
        g_object_ref (item);
 
80
    }
 
81
 
 
82
  gb_tree_rebuild (GB_TREE (self));
 
83
 
 
84
  if (item != NULL)
 
85
    {
 
86
      selected = gb_tree_find_item (GB_TREE (self), item);
 
87
      if (selected != NULL)
 
88
        {
 
89
          gb_tree_node_expand (selected, TRUE);
 
90
          gb_tree_node_select (selected);
 
91
          gb_tree_scroll_to_node (GB_TREE (self), selected);
 
92
        }
 
93
      g_object_unref (item);
 
94
    }
 
95
}
 
96
 
 
97
static void
 
98
gb_project_tree_actions_collapse_all_nodes (GSimpleAction *action,
 
99
                                            GVariant      *variant,
 
100
                                            gpointer       user_data)
 
101
{
 
102
  GbProjectTree *self = user_data;
 
103
 
 
104
  g_assert (GB_IS_PROJECT_TREE (self));
 
105
 
 
106
  gtk_tree_view_collapse_all (GTK_TREE_VIEW (self));
 
107
}
 
108
 
 
109
static void
 
110
gb_project_tree_actions_open (GSimpleAction *action,
 
111
                              GVariant      *variant,
 
112
                              gpointer       user_data)
 
113
{
 
114
  GbProjectTree *self = user_data;
 
115
  GbWorkbench *workbench;
 
116
  GbTreeNode *selected;
 
117
  GObject *item;
 
118
 
 
119
  g_assert (GB_IS_PROJECT_TREE (self));
 
120
 
 
121
  workbench = gb_widget_get_workbench (GTK_WIDGET (self));
 
122
  g_assert (GB_IS_WORKBENCH (workbench));
 
123
 
 
124
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
125
      !(item = gb_tree_node_get_item (selected)))
 
126
    return;
 
127
 
 
128
  item = gb_tree_node_get_item (selected);
 
129
 
 
130
  if (IDE_IS_PROJECT_FILE (item))
 
131
    {
 
132
      GFileInfo *file_info;
 
133
      GFile *file;
 
134
 
 
135
      file_info = ide_project_file_get_file_info (IDE_PROJECT_FILE (item));
 
136
      if (!file_info)
 
137
        return;
 
138
 
 
139
      if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
 
140
        return;
 
141
 
 
142
      file = ide_project_file_get_file (IDE_PROJECT_FILE (item));
 
143
      if (!file)
 
144
        return;
 
145
 
 
146
      gb_workbench_open (workbench, file);
 
147
    }
 
148
}
 
149
 
 
150
static void
 
151
gb_project_tree_actions_open_with (GSimpleAction *action,
 
152
                                   GVariant      *variant,
 
153
                                   gpointer       user_data)
 
154
{
 
155
  g_autoptr(GDesktopAppInfo) app_info = NULL;
 
156
  g_autoptr(GdkAppLaunchContext) launch_context = NULL;
 
157
  GbProjectTree *self = user_data;
 
158
  GbTreeNode *selected;
 
159
  GbWorkbench *workbench;
 
160
  GdkDisplay *display;
 
161
  GFileInfo *file_info;
 
162
  GFile *file;
 
163
  const gchar *app_id;
 
164
  GObject *item;
 
165
  GList *files;
 
166
 
 
167
  g_assert (GB_IS_PROJECT_TREE (self));
 
168
  g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
 
169
 
 
170
  if (!(workbench = gb_widget_get_workbench (GTK_WIDGET (self))) ||
 
171
      !(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
172
      !(item = gb_tree_node_get_item (selected)) ||
 
173
      !IDE_IS_PROJECT_FILE (item) ||
 
174
      !(app_id = g_variant_get_string (variant, NULL)) ||
 
175
      !(file_info = ide_project_file_get_file_info (IDE_PROJECT_FILE (item))) ||
 
176
      !(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))) ||
 
177
      !(app_info = g_desktop_app_info_new (app_id)))
 
178
    return;
 
179
 
 
180
  display = gtk_widget_get_display (GTK_WIDGET (self));
 
181
  launch_context = gdk_display_get_app_launch_context (display);
 
182
 
 
183
  files = g_list_append (NULL, file);
 
184
  g_app_info_launch (G_APP_INFO (app_info), files, G_APP_LAUNCH_CONTEXT (launch_context), NULL);
 
185
  g_list_free (files);
 
186
}
 
187
 
 
188
static void
 
189
gb_project_tree_actions_open_with_editor (GSimpleAction *action,
 
190
                                          GVariant      *variant,
 
191
                                          gpointer       user_data)
 
192
{
 
193
  GbWorkbench *workbench;
 
194
  GbProjectTree *self = user_data;
 
195
  GFileInfo *file_info;
 
196
  GFile *file;
 
197
  GbTreeNode *selected;
 
198
  GObject *item;
 
199
 
 
200
  g_assert (GB_IS_PROJECT_TREE (self));
 
201
 
 
202
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
203
      !(item = gb_tree_node_get_item (selected)) ||
 
204
      !IDE_IS_PROJECT_FILE (item) ||
 
205
      !(file_info = ide_project_file_get_file_info (IDE_PROJECT_FILE (item))) ||
 
206
      (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) ||
 
207
      !(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))) ||
 
208
      !(workbench = gb_widget_get_workbench (GTK_WIDGET (self))))
 
209
    return;
 
210
 
 
211
  gb_workbench_open_with_editor (workbench, file);
 
212
}
 
213
 
 
214
static void
 
215
gb_project_tree_actions_open_containing_folder (GSimpleAction *action,
 
216
                                                GVariant      *variant,
 
217
                                                gpointer       user_data)
 
218
{
 
219
  GbEditorWorkspace *self = user_data;
 
220
  GbTreeNode *selected;
 
221
  GObject *item;
 
222
  GFile *file;
 
223
 
 
224
  g_assert (GB_IS_PROJECT_TREE (self));
 
225
 
 
226
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
227
      !(item = gb_tree_node_get_item (selected)) ||
 
228
      !(IDE_IS_PROJECT_FILE (item) || IDE_IS_PROJECT_FILES (item)))
 
229
    return;
 
230
 
 
231
  if (IDE_IS_PROJECT_FILES (item))
 
232
    {
 
233
      IdeContext *context;
 
234
      IdeVcs *vcs;
 
235
 
 
236
      context = ide_object_get_context (IDE_OBJECT (item));
 
237
      vcs = ide_context_get_vcs (context);
 
238
      file = ide_vcs_get_working_directory (vcs);
 
239
    }
 
240
  else if (!(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))))
 
241
    {
 
242
      return;
 
243
    }
 
244
 
 
245
  gb_file_manager_show (file, NULL);
 
246
}
 
247
 
 
248
/* Based on gdesktopappinfo.c in GIO */
 
249
static gchar *
 
250
find_terminal_executable (void)
 
251
{
 
252
  gsize i;
 
253
  gchar *path = NULL;
 
254
  g_autoptr(GSettings) terminal_settings = NULL;
 
255
  g_autofree gchar *gsettings_terminal = NULL;
 
256
  const gchar *terminals[] = {
 
257
    NULL,                     /* GSettings */
 
258
    "x-terminal-emulator",    /* Debian's alternative system */
 
259
    "gnome-terminal",
 
260
    NULL,                     /* getenv ("TERM") */
 
261
    "nxterm", "color-xterm",
 
262
    "rxvt", "xterm", "dtterm"
 
263
  };
 
264
 
 
265
  /* This is deprecated, but at least the user can specify it! */
 
266
  terminal_settings = g_settings_new ("org.gnome.desktop.default-applications.terminal");
 
267
  gsettings_terminal = g_settings_get_string (terminal_settings, "exec");
 
268
  terminals[0] = gsettings_terminal;
 
269
 
 
270
  /* This is generally one of the fallback terminals */
 
271
  terminals[3] = g_getenv ("TERM");
 
272
 
 
273
  for (i = 0; i < G_N_ELEMENTS (terminals) && path == NULL; ++i)
 
274
    {
 
275
      if (terminals[i] != NULL)
 
276
        {
 
277
          G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 
278
          path = g_find_program_in_path (terminals[i]);
 
279
          G_GNUC_END_IGNORE_DEPRECATIONS
 
280
        }
 
281
    }
 
282
 
 
283
  return path;
 
284
}
 
285
 
 
286
static void
 
287
gb_project_tree_actions_open_in_terminal (GSimpleAction *action,
 
288
                                          GVariant      *variant,
 
289
                                          gpointer       user_data)
 
290
{
 
291
  GbEditorWorkspace *self = user_data;
 
292
  GbTreeNode *selected;
 
293
  GObject *item;
 
294
  GFile *file;
 
295
  g_autofree gchar *workdir = NULL;
 
296
  g_autofree gchar *terminal_executable = NULL;
 
297
  const gchar *argv[] = { NULL, NULL };
 
298
  GError *error = NULL;
 
299
 
 
300
  g_assert (GB_IS_PROJECT_TREE (self));
 
301
 
 
302
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
303
      !(item = gb_tree_node_get_item (selected)) ||
 
304
      !(IDE_IS_PROJECT_FILE (item) || IDE_IS_PROJECT_FILES (item)))
 
305
    return;
 
306
 
 
307
  if (IDE_IS_PROJECT_FILES (item))
 
308
    {
 
309
      IdeContext *context;
 
310
      IdeVcs *vcs;
 
311
 
 
312
      context = ide_object_get_context (IDE_OBJECT (item));
 
313
      vcs = ide_context_get_vcs (context);
 
314
      file = ide_vcs_get_working_directory (vcs);
 
315
    }
 
316
  else if (!(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))))
 
317
    {
 
318
      return;
 
319
    }
 
320
 
 
321
  if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY)
 
322
    {
 
323
      g_autoptr(GFile) parent;
 
324
 
 
325
      parent = g_file_get_parent (file);
 
326
      workdir = g_file_get_path (parent);
 
327
    }
 
328
  else
 
329
    {
 
330
      workdir = g_file_get_path (file);
 
331
    }
 
332
 
 
333
  if (workdir == NULL)
 
334
    {
 
335
      g_warning ("Cannot load non-native file in terminal.");
 
336
      return;
 
337
    }
 
338
 
 
339
  terminal_executable = find_terminal_executable ();
 
340
  argv[0] = terminal_executable;
 
341
  g_return_if_fail (terminal_executable != NULL);
 
342
 
 
343
  /* Can't use GdkAppLaunchContext as
 
344
   * we cannot set the working directory.
 
345
   */
 
346
  if (!g_spawn_async (workdir, (gchar **)argv, NULL,
 
347
                      G_SPAWN_STDERR_TO_DEV_NULL,
 
348
                      NULL, NULL, NULL, &error))
 
349
    {
 
350
      g_warning ("%s", error->message);
 
351
      g_clear_error (&error);
 
352
      return;
 
353
    }
 
354
}
 
355
 
 
356
static void
 
357
gb_project_tree_actions_show_icons (GSimpleAction *action,
 
358
                                    GVariant      *variant,
 
359
                                    gpointer       user_data)
 
360
{
 
361
  GbProjectTree *self = user_data;
 
362
  gboolean show_icons;
 
363
 
 
364
  g_assert (GB_IS_PROJECT_TREE (self));
 
365
  g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN));
 
366
 
 
367
  show_icons = g_variant_get_boolean (variant);
 
368
  gb_tree_set_show_icons (GB_TREE (self), show_icons);
 
369
  g_simple_action_set_state (action, variant);
 
370
}
 
371
 
 
372
static IdeProjectFile *
 
373
create_file (IdeContext *context,
 
374
             GFile      *file,
 
375
             GFileType   file_type)
 
376
{
 
377
  g_autofree gchar *path = NULL;
 
378
  g_autofree gchar *name = NULL;
 
379
  g_autoptr(GFileInfo) file_info = NULL;
 
380
  IdeVcs *vcs;
 
381
  GFile *workdir;
 
382
 
 
383
  g_assert (IDE_IS_CONTEXT (context));
 
384
  g_assert (G_IS_FILE (file));
 
385
  g_assert ((file_type == G_FILE_TYPE_DIRECTORY) || (file_type == G_FILE_TYPE_REGULAR));
 
386
 
 
387
  vcs = ide_context_get_vcs (context);
 
388
  workdir = ide_vcs_get_working_directory (vcs);
 
389
  path = g_file_get_relative_path (workdir, file);
 
390
  name = g_file_get_basename (file);
 
391
 
 
392
  file_info = g_file_info_new ();
 
393
  g_file_info_set_file_type (file_info, file_type);
 
394
  g_file_info_set_name (file_info, name);
 
395
  g_file_info_set_display_name (file_info, name);
 
396
 
 
397
  return g_object_new (IDE_TYPE_PROJECT_FILE,
 
398
                       "context", context,
 
399
                       "file", file,
 
400
                       "file-info", file_info,
 
401
                       "path", path,
 
402
                       "parent", NULL,
 
403
                       NULL);
 
404
}
 
405
 
 
406
static void
 
407
gb_project_tree_actions__make_directory_cb (GObject      *object,
 
408
                                            GAsyncResult *result,
 
409
                                            gpointer      user_data)
 
410
{
 
411
  GFile *file = (GFile *)object;
 
412
  g_autoptr(GbTreeNode) node = user_data;
 
413
  g_autoptr(GError) error = NULL;
 
414
  g_autoptr(IdeProjectFile) project_file = NULL;
 
415
  GbProjectTree *self;
 
416
  GbWorkbench *workbench;
 
417
  IdeContext *context;
 
418
  IdeProject *project;
 
419
  GbTreeNode *created;
 
420
 
 
421
  g_assert (G_IS_FILE (file));
 
422
  g_assert (GB_IS_TREE_NODE (node));
 
423
 
 
424
  if (!g_file_make_directory_finish (file, result, &error))
 
425
    {
 
426
      /* todo: show error messsage */
 
427
      return;
 
428
    }
 
429
 
 
430
  self = GB_PROJECT_TREE (gb_tree_node_get_tree (node));
 
431
  if (self == NULL)
 
432
    return;
 
433
 
 
434
  workbench = gb_widget_get_workbench (GTK_WIDGET (self));
 
435
  if (workbench == NULL)
 
436
    return;
 
437
 
 
438
  context = gb_workbench_get_context (workbench);
 
439
  if (context == NULL)
 
440
    return;
 
441
 
 
442
  project = ide_context_get_project (context);
 
443
 
 
444
  project_file = create_file (context, file, G_FILE_TYPE_DIRECTORY);
 
445
  ide_project_add_file (project, project_file);
 
446
 
 
447
  gb_tree_node_rebuild (node);
 
448
  gb_tree_node_expand (node, FALSE);
 
449
 
 
450
  created = gb_tree_find_item (GB_TREE (self), G_OBJECT (project_file));
 
451
 
 
452
  if (created != NULL)
 
453
    gb_tree_node_select (created);
 
454
}
 
455
 
 
456
static void
 
457
gb_project_tree_actions__create_cb (GObject      *object,
 
458
                                    GAsyncResult *result,
 
459
                                    gpointer      user_data)
 
460
{
 
461
  GFile *file = (GFile *)object;
 
462
  g_autoptr(IdeProjectFile) project_file = NULL;
 
463
  g_autoptr(GbTreeNode) node = user_data;
 
464
  g_autoptr(GError) error = NULL;
 
465
  GbProjectTree *self;
 
466
  GbWorkbench *workbench;
 
467
  IdeContext *context;
 
468
  IdeProject *project;
 
469
  GbTreeNode *created;
 
470
 
 
471
  g_assert (G_IS_FILE (file));
 
472
  g_assert (GB_IS_TREE_NODE (node));
 
473
 
 
474
  if (!g_file_create_finish (file, result, &error))
 
475
    {
 
476
      /* todo: show error messsage */
 
477
      return;
 
478
    }
 
479
 
 
480
  self = GB_PROJECT_TREE (gb_tree_node_get_tree (node));
 
481
  if (self == NULL)
 
482
    return;
 
483
 
 
484
  workbench = gb_widget_get_workbench (GTK_WIDGET (self));
 
485
  if (workbench == NULL)
 
486
    return;
 
487
 
 
488
  context = gb_workbench_get_context (workbench);
 
489
  if (context == NULL)
 
490
    return;
 
491
 
 
492
  project = ide_context_get_project (context);
 
493
 
 
494
  project_file = create_file (context, file, G_FILE_TYPE_REGULAR);
 
495
  ide_project_add_file (project, project_file);
 
496
 
 
497
  gb_workbench_open (workbench, file);
 
498
 
 
499
  gb_tree_node_rebuild (node);
 
500
  gb_tree_node_expand (node, FALSE);
 
501
 
 
502
  created = gb_tree_find_item (GB_TREE (self), G_OBJECT (project_file));
 
503
 
 
504
  if (created != NULL)
 
505
    gb_tree_node_select (created);
 
506
}
 
507
 
 
508
static void
 
509
gb_project_tree_actions__popover_create_file_cb (GbProjectTree    *self,
 
510
                                                 GFile            *file,
 
511
                                                 GFileType         file_type,
 
512
                                                 GbNewFilePopover *popover)
 
513
{
 
514
  GbTreeNode *selected;
 
515
 
 
516
  g_assert (GB_IS_PROJECT_TREE (self));
 
517
  g_assert (G_IS_FILE (file));
 
518
  g_assert ((file_type == G_FILE_TYPE_DIRECTORY) ||
 
519
            (file_type == G_FILE_TYPE_REGULAR));
 
520
  g_assert (GB_IS_NEW_FILE_POPOVER (popover));
 
521
 
 
522
  selected = gb_tree_get_selected (GB_TREE (self));
 
523
 
 
524
  g_assert (selected != NULL);
 
525
  g_assert (GB_IS_TREE_NODE (selected));
 
526
 
 
527
  if (file_type == G_FILE_TYPE_DIRECTORY)
 
528
    {
 
529
      g_file_make_directory_async (file,
 
530
                                   G_PRIORITY_DEFAULT,
 
531
                                   NULL, /* cancellable */
 
532
                                   gb_project_tree_actions__make_directory_cb,
 
533
                                   g_object_ref (selected));
 
534
    }
 
535
  else if (file_type == G_FILE_TYPE_REGULAR)
 
536
    {
 
537
      g_file_create_async (file,
 
538
                           G_FILE_CREATE_NONE,
 
539
                           G_PRIORITY_DEFAULT,
 
540
                           NULL, /* cancellable */
 
541
                           gb_project_tree_actions__create_cb,
 
542
                           g_object_ref (selected));
 
543
    }
 
544
  else
 
545
    {
 
546
      g_assert_not_reached ();
 
547
    }
 
548
 
 
549
  self->expanded_in_new = FALSE;
 
550
 
 
551
  gtk_widget_hide (GTK_WIDGET (popover));
 
552
  gtk_widget_destroy (GTK_WIDGET (popover));
 
553
}
 
554
 
 
555
static void
 
556
gb_project_tree_actions__popover_closed_cb (GbProjectTree *self,
 
557
                                            GtkPopover    *popover)
 
558
{
 
559
  GbTreeNode *selected;
 
560
 
 
561
  g_assert (GB_IS_PROJECT_TREE (self));
 
562
  g_assert (GTK_IS_POPOVER (popover));
 
563
 
 
564
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) || !self->expanded_in_new)
 
565
    return;
 
566
 
 
567
  gb_tree_node_collapse (selected);
 
568
}
 
569
 
 
570
static void
 
571
gb_project_tree_actions_new (GbProjectTree *self,
 
572
                             GFileType      file_type)
 
573
{
 
574
  GbTreeNode *selected;
 
575
  GObject *item;
 
576
  GtkPopover *popover;
 
577
  IdeProjectFile *project_file;
 
578
  GFile *file = NULL;
 
579
  gboolean is_dir = FALSE;
 
580
 
 
581
  g_assert (GB_IS_PROJECT_TREE (self));
 
582
  g_assert ((file_type == G_FILE_TYPE_DIRECTORY) ||
 
583
            (file_type == G_FILE_TYPE_REGULAR));
 
584
 
 
585
again:
 
586
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
587
      !(item = gb_tree_node_get_item (selected)) ||
 
588
      !(IDE_IS_PROJECT_FILES (item) || IDE_IS_PROJECT_FILE (item)))
 
589
    return;
 
590
 
 
591
  if (IDE_IS_PROJECT_FILE (item))
 
592
    {
 
593
      if (!(project_file = IDE_PROJECT_FILE (item)) ||
 
594
          !(file = ide_project_file_get_file (project_file)))
 
595
        return;
 
596
      is_dir = project_file_is_directory (item);
 
597
    }
 
598
  else if (IDE_IS_PROJECT_FILES (item))
 
599
    {
 
600
      IdeContext *context;
 
601
      IdeVcs *vcs;
 
602
 
 
603
      context = ide_object_get_context (IDE_OBJECT (item));
 
604
      vcs = ide_context_get_vcs (context);
 
605
      file = ide_vcs_get_working_directory (vcs);
 
606
      is_dir = TRUE;
 
607
    }
 
608
  else
 
609
    {
 
610
      g_assert_not_reached ();
 
611
      return;
 
612
    }
 
613
 
 
614
  g_assert (G_IS_FILE (file));
 
615
 
 
616
  /*
 
617
   * If this item is an IdeProjectFile and not a directory, then we really
 
618
   * want to create a sibling.
 
619
   */
 
620
  if (!is_dir)
 
621
    {
 
622
      GtkTreePath *path;
 
623
 
 
624
      selected = gb_tree_node_get_parent (selected);
 
625
      gb_tree_node_select (selected);
 
626
      path = gb_tree_node_get_path (selected);
 
627
      gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0, 0);
 
628
      gtk_tree_path_free (path);
 
629
 
 
630
      goto again;
 
631
    }
 
632
 
 
633
  if ((self->expanded_in_new = !gb_tree_node_get_expanded (selected)))
 
634
    gb_tree_node_expand (selected, FALSE);
 
635
 
 
636
  popover = g_object_new (GB_TYPE_NEW_FILE_POPOVER,
 
637
                          "directory", file,
 
638
                          "file-type", file_type,
 
639
                          "position", GTK_POS_RIGHT,
 
640
                          NULL);
 
641
  g_signal_connect_object (popover,
 
642
                           "create-file",
 
643
                           G_CALLBACK (gb_project_tree_actions__popover_create_file_cb),
 
644
                           self,
 
645
                           G_CONNECT_SWAPPED);
 
646
  g_signal_connect_object (popover,
 
647
                           "closed",
 
648
                           G_CALLBACK (gb_project_tree_actions__popover_closed_cb),
 
649
                           self,
 
650
                           G_CONNECT_SWAPPED);
 
651
 
 
652
  gb_tree_node_show_popover (selected, popover);
 
653
}
 
654
 
 
655
static void
 
656
gb_project_tree_actions_new_directory (GSimpleAction *action,
 
657
                                       GVariant      *variant,
 
658
                                       gpointer       user_data)
 
659
{
 
660
  GbProjectTree *self = user_data;
 
661
 
 
662
  g_assert (GB_IS_PROJECT_TREE (self));
 
663
 
 
664
  gb_project_tree_actions_new (self, G_FILE_TYPE_DIRECTORY);
 
665
}
 
666
 
 
667
static void
 
668
gb_project_tree_actions_new_file (GSimpleAction *action,
 
669
                                  GVariant      *variant,
 
670
                                  gpointer       user_data)
 
671
{
 
672
  GbProjectTree *self = user_data;
 
673
 
 
674
  g_assert (GB_IS_PROJECT_TREE (self));
 
675
 
 
676
  gb_project_tree_actions_new (self, G_FILE_TYPE_REGULAR);
 
677
}
 
678
 
 
679
static gint
 
680
project_item_equal_func (GFile   *key,
 
681
                         GObject *item)
 
682
{
 
683
  g_assert (G_IS_FILE (key));
 
684
  g_assert (IDE_IS_PROJECT_ITEM (item));
 
685
 
 
686
  if (IDE_IS_PROJECT_FILE (item))
 
687
    {
 
688
      GFile *file;
 
689
 
 
690
      file = ide_project_file_get_file (IDE_PROJECT_FILE (item));
 
691
      g_assert (G_IS_FILE (file));
 
692
 
 
693
      return g_file_equal (key, file);
 
694
    }
 
695
 
 
696
  return FALSE;
 
697
}
 
698
 
 
699
static void
 
700
gb_project_tree_actions__project_rename_file_cb (GObject      *object,
 
701
                                                 GAsyncResult *result,
 
702
                                                 gpointer      user_data)
 
703
{
 
704
  IdeProject *project = (IdeProject *)object;
 
705
  g_autoptr(GbRenameFilePopover) popover = user_data;
 
706
  g_autoptr(GError) error = NULL;
 
707
  GbTreeNode *node;
 
708
  GFile *file;
 
709
  GbTree *tree;
 
710
  gboolean expanded = FALSE;
 
711
 
 
712
  g_assert (IDE_IS_PROJECT (project));
 
713
  g_assert (GB_IS_RENAME_FILE_POPOVER (popover));
 
714
 
 
715
  if (!ide_project_rename_file_finish (project, result, &error))
 
716
    {
 
717
      /* todo: display error */
 
718
      g_warning ("%s", error->message);
 
719
      return;
 
720
    }
 
721
 
 
722
  file = g_object_get_data (G_OBJECT (popover), "G_FILE");
 
723
  tree = GB_TREE (gtk_popover_get_relative_to (GTK_POPOVER (popover)));
 
724
 
 
725
  g_assert (G_IS_FILE (file));
 
726
  g_assert (GB_IS_TREE (tree));
 
727
 
 
728
  if ((node = gb_tree_get_selected (tree)))
 
729
    expanded = gb_tree_node_get_expanded (node);
 
730
 
 
731
  gb_tree_rebuild (tree);
 
732
 
 
733
  node = gb_tree_find_custom (tree,
 
734
                              (GEqualFunc)project_item_equal_func,
 
735
                              file);
 
736
 
 
737
  if (node != NULL)
 
738
    {
 
739
      gb_tree_node_expand (node, TRUE);
 
740
      if (!expanded)
 
741
        gb_tree_node_collapse (node);
 
742
      gb_tree_node_select (node);
 
743
      gb_tree_scroll_to_node (tree, node);
 
744
    }
 
745
 
 
746
  gtk_widget_hide (GTK_WIDGET (popover));
 
747
  gtk_widget_destroy (GTK_WIDGET (popover));
 
748
}
 
749
 
 
750
static void
 
751
gb_project_tree_actions__rename_file_cb (GbProjectTree       *self,
 
752
                                         GFile               *orig_file,
 
753
                                         GFile               *new_file,
 
754
                                         GbRenameFilePopover *popover)
 
755
{
 
756
  GbWorkbench *workbench;
 
757
  IdeContext *context;
 
758
  IdeProject *project;
 
759
 
 
760
  g_assert (GB_IS_PROJECT_TREE (self));
 
761
  g_assert (G_IS_FILE (orig_file));
 
762
  g_assert (G_IS_FILE (new_file));
 
763
  g_assert (GTK_IS_POPOVER (popover));
 
764
 
 
765
  workbench = gb_widget_get_workbench (GTK_WIDGET (self));
 
766
  context = gb_workbench_get_context (workbench);
 
767
  project = ide_context_get_project (context);
 
768
 
 
769
  /* todo: set busin spinner in popover */
 
770
 
 
771
  g_object_set_data_full (G_OBJECT (popover),
 
772
                          "G_FILE",
 
773
                          g_object_ref (new_file),
 
774
                          g_object_unref);
 
775
 
 
776
  ide_project_rename_file_async (project, orig_file, new_file, NULL,
 
777
                                 gb_project_tree_actions__project_rename_file_cb,
 
778
                                 g_object_ref (popover));
 
779
}
 
780
 
 
781
static void
 
782
gb_project_tree_actions_rename_file (GSimpleAction *action,
 
783
                                     GVariant      *variant,
 
784
                                     gpointer       user_data)
 
785
{
 
786
  GbProjectTree *self = user_data;
 
787
  GbTreeNode *selected;
 
788
  GtkPopover *popover;
 
789
  GObject *item;
 
790
  GFile *file;
 
791
  GFileInfo *file_info;
 
792
  gboolean is_dir;
 
793
 
 
794
  g_assert (GB_IS_PROJECT_TREE (self));
 
795
 
 
796
  if (!(selected = gb_tree_get_selected (GB_TREE (self))) ||
 
797
      !(item = gb_tree_node_get_item (selected)) ||
 
798
      !IDE_IS_PROJECT_FILE (item) ||
 
799
      !(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))) ||
 
800
      !(file_info = ide_project_file_get_file_info (IDE_PROJECT_FILE (item))))
 
801
    return;
 
802
 
 
803
  is_dir = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY);
 
804
 
 
805
  popover = g_object_new (GB_TYPE_RENAME_FILE_POPOVER,
 
806
                          "file", file,
 
807
                          "is-directory", is_dir,
 
808
                          "position", GTK_POS_RIGHT,
 
809
                          NULL);
 
810
  g_signal_connect_object (popover,
 
811
                           "rename-file",
 
812
                           G_CALLBACK (gb_project_tree_actions__rename_file_cb),
 
813
                           self,
 
814
                           G_CONNECT_SWAPPED);
 
815
  gb_tree_node_show_popover (selected, popover);
 
816
}
 
817
 
 
818
static void
 
819
gb_project_tree_actions__trash_file_cb (GObject      *object,
 
820
                                        GAsyncResult *result,
 
821
                                        gpointer      user_data)
 
822
{
 
823
  IdeProject *project = (IdeProject *)object;
 
824
  g_autoptr(GbProjectTree) self = user_data;
 
825
  g_autoptr(GError) error = NULL;
 
826
  GbTreeNode *node;
 
827
  GObject *item = NULL;
 
828
 
 
829
  g_assert (IDE_IS_PROJECT (project));
 
830
  g_assert (GB_IS_PROJECT_TREE (self));
 
831
 
 
832
  if (!ide_project_trash_file_finish (project, result, &error))
 
833
    {
 
834
      /* todo: warning dialog */
 
835
      g_warning ("%s", error->message);
 
836
      return;
 
837
    }
 
838
 
 
839
  /* todo: this should be done with tree observer */
 
840
  if ((node = gb_tree_get_selected (GB_TREE (self))))
 
841
    {
 
842
      if ((node = gb_tree_node_get_parent (node)))
 
843
        item = gb_tree_node_get_item (node);
 
844
    }
 
845
 
 
846
  gb_tree_rebuild (GB_TREE (self));
 
847
 
 
848
  if ((node = gb_tree_find_item (GB_TREE (self), item)))
 
849
    gb_tree_node_expand (node, TRUE);
 
850
}
 
851
 
 
852
static GbViewStack *
 
853
get_view_stack (GbView *view)
 
854
{
 
855
  GtkWidget *widget = (GtkWidget *)view;
 
856
 
 
857
  while ((widget != NULL) && !GB_IS_VIEW_STACK (widget))
 
858
    widget = gtk_widget_get_parent (widget);
 
859
 
 
860
  return (GbViewStack *)widget;
 
861
}
 
862
 
 
863
typedef struct
 
864
{
 
865
  GbDocument *document;
 
866
  GList      *views;
 
867
} ViewsRemoval;
 
868
 
 
869
static void
 
870
gb_project_tree_actions_close_views_cb (GtkWidget *widget,
 
871
                                        gpointer   user_data)
 
872
{
 
873
  GbDocument *document;
 
874
  ViewsRemoval *removal = user_data;
 
875
  GbView *view = (GbView *)widget;
 
876
 
 
877
  g_assert (GB_IS_VIEW (view));
 
878
  g_assert (removal != NULL);
 
879
  g_assert (GB_IS_DOCUMENT (removal->document));
 
880
 
 
881
  document = gb_view_get_document (view);
 
882
 
 
883
  if (document == removal->document)
 
884
    removal->views = g_list_prepend (removal->views, g_object_ref (view));
 
885
}
 
886
 
 
887
static void
 
888
gb_project_tree_actions_move_to_trash (GSimpleAction *action,
 
889
                                       GVariant      *param,
 
890
                                       gpointer       user_data)
 
891
{
 
892
  GbProjectTree *self = user_data;
 
893
  IdeBufferManager *buffer_manager;
 
894
  GbWorkbench *workbench;
 
895
  IdeContext *context;
 
896
  ViewsRemoval removal = { 0 };
 
897
  IdeBuffer *buffer;
 
898
  IdeProject *project;
 
899
  GbTreeNode *node;
 
900
  GFile *file;
 
901
  IdeFile *ifile;
 
902
  GObject *item;
 
903
  GList *iter;
 
904
 
 
905
  g_assert (G_IS_SIMPLE_ACTION (action));
 
906
  g_assert (GB_IS_PROJECT_TREE (self));
 
907
 
 
908
  workbench = gb_widget_get_workbench (GTK_WIDGET (self));
 
909
  context = gb_workbench_get_context (workbench);
 
910
  project = ide_context_get_project (context);
 
911
  buffer_manager = ide_context_get_buffer_manager (context);
 
912
 
 
913
  if (!(node = gb_tree_get_selected (GB_TREE (self))) ||
 
914
      !(item = gb_tree_node_get_item (node)) ||
 
915
      !IDE_IS_PROJECT_FILE (item) ||
 
916
      !(file = ide_project_file_get_file (IDE_PROJECT_FILE (item))))
 
917
    return;
 
918
 
 
919
  /*
 
920
   * Find all of the views that contain this file.
 
921
   * We do not close them until we leave the foreach callback.
 
922
   */
 
923
  ifile = ide_project_get_project_file (project, file);
 
924
  buffer = ide_buffer_manager_find_buffer (buffer_manager, ifile);
 
925
  if (buffer != NULL)
 
926
    {
 
927
      removal.document = g_object_ref (buffer);
 
928
      gb_workbench_views_foreach (workbench,
 
929
                                  gb_project_tree_actions_close_views_cb,
 
930
                                  &removal);
 
931
      g_object_unref (removal.document);
 
932
    }
 
933
 
 
934
  /*
 
935
   * Close all of the views that match the document.
 
936
   */
 
937
  for (iter = removal.views; iter; iter = iter->next)
 
938
    {
 
939
      GbViewStack *stack;
 
940
 
 
941
      stack = get_view_stack (iter->data);
 
942
      if (stack != NULL)
 
943
        gb_view_stack_remove (stack, iter->data);
 
944
    }
 
945
 
 
946
  g_list_free_full (removal.views, g_object_unref);
 
947
 
 
948
  /*
 
949
   * Now move the file to the trash.
 
950
   */
 
951
  ide_project_trash_file_async (project,
 
952
                                file,
 
953
                                NULL,
 
954
                                gb_project_tree_actions__trash_file_cb,
 
955
                                g_object_ref (self));
 
956
}
 
957
 
 
958
static GActionEntry GbProjectTreeActions[] = {
 
959
  { "collapse-all-nodes",     gb_project_tree_actions_collapse_all_nodes },
 
960
  { "move-to-trash",          gb_project_tree_actions_move_to_trash },
 
961
  { "new-directory",          gb_project_tree_actions_new_directory },
 
962
  { "new-file",               gb_project_tree_actions_new_file },
 
963
  { "open",                   gb_project_tree_actions_open },
 
964
  { "open-containing-folder", gb_project_tree_actions_open_containing_folder },
 
965
  { "open-in-terminal",       gb_project_tree_actions_open_in_terminal },
 
966
  { "open-with",              gb_project_tree_actions_open_with, "s" },
 
967
  { "open-with-editor",       gb_project_tree_actions_open_with_editor },
 
968
  { "refresh",                gb_project_tree_actions_refresh },
 
969
  { "rename-file",            gb_project_tree_actions_rename_file },
 
970
  { "show-icons",             NULL, NULL, "false", gb_project_tree_actions_show_icons },
 
971
};
 
972
 
 
973
void
 
974
gb_project_tree_actions_init (GbProjectTree *self)
 
975
{
 
976
  g_autoptr(GSettings) settings = NULL;
 
977
  g_autoptr(GSettings) tree_settings = NULL;
 
978
  g_autoptr(GSimpleActionGroup) actions = NULL;
 
979
  g_autoptr(GAction) action = NULL;
 
980
  g_autoptr(GVariant) show_icons = NULL;
 
981
 
 
982
  actions = g_simple_action_group_new ();
 
983
 
 
984
  settings = g_settings_new ("org.gtk.Settings.FileChooser");
 
985
  action = g_settings_create_action (settings, "sort-directories-first");
 
986
  g_action_map_add_action (G_ACTION_MAP (actions), action);
 
987
 
 
988
  g_action_map_add_action_entries (G_ACTION_MAP (actions),
 
989
                                   GbProjectTreeActions,
 
990
                                   G_N_ELEMENTS (GbProjectTreeActions),
 
991
                                   self);
 
992
  gtk_widget_insert_action_group (GTK_WIDGET (self),
 
993
                                  "project-tree",
 
994
                                  G_ACTION_GROUP (actions));
 
995
 
 
996
  tree_settings = g_settings_new ("org.gnome.builder.project-tree");
 
997
  show_icons = g_settings_get_value (tree_settings, "show-icons");
 
998
  action_set (G_ACTION_GROUP (actions), "show-icons",
 
999
              "state", show_icons,
 
1000
              NULL);
 
1001
 
 
1002
  gb_project_tree_actions_update (self);
 
1003
}
 
1004
 
 
1005
void
 
1006
gb_project_tree_actions_update (GbProjectTree *self)
 
1007
{
 
1008
  GActionGroup *group;
 
1009
  GbTreeNode *selection;
 
1010
  GObject *item = NULL;
 
1011
 
 
1012
  IDE_ENTRY;
 
1013
 
 
1014
  g_assert (GB_IS_PROJECT_TREE (self));
 
1015
 
 
1016
  group = gtk_widget_get_action_group (GTK_WIDGET (self), "project-tree");
 
1017
  g_assert (G_IS_SIMPLE_ACTION_GROUP (group));
 
1018
 
 
1019
  selection = gb_tree_get_selected (GB_TREE (self));
 
1020
  if (selection != NULL)
 
1021
    item = gb_tree_node_get_item (selection);
 
1022
 
 
1023
  action_set (group, "new-file",
 
1024
              "enabled", (IDE_IS_PROJECT_FILE (item) || IDE_IS_PROJECT_FILES (item)),
 
1025
              NULL);
 
1026
  action_set (group, "new-directory",
 
1027
              "enabled", (IDE_IS_PROJECT_FILE (item) || IDE_IS_PROJECT_FILES (item)),
 
1028
              NULL);
 
1029
  action_set (group, "open",
 
1030
              "enabled", (IDE_IS_PROJECT_FILE (item) && !project_file_is_directory (item)),
 
1031
              NULL);
 
1032
  action_set (group, "open-with-editor",
 
1033
              "enabled", (IDE_IS_PROJECT_FILE (item) && !project_file_is_directory (item)),
 
1034
              NULL);
 
1035
  action_set (group, "open-containing-folder",
 
1036
              "enabled", (IDE_IS_PROJECT_FILE (item) || IDE_IS_PROJECT_FILES (item)),
 
1037
              NULL);
 
1038
  action_set (group, "open-in-terminal",
 
1039
              "enabled", IDE_IS_PROJECT_FILE (item),
 
1040
              NULL);
 
1041
  action_set (group, "rename-file",
 
1042
              "enabled", IDE_IS_PROJECT_FILE (item),
 
1043
              NULL);
 
1044
  action_set (group, "move-to-trash",
 
1045
              "enabled", (IDE_IS_PROJECT_FILE (item) && !project_file_is_directory (item)),
 
1046
              NULL);
 
1047
 
 
1048
  IDE_EXIT;
 
1049
}