plugin-browser.c 30.8 KB
Newer Older
1
2
3
4
5
6
7
/*
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This is a plug-in for the GIMP.
 *
 * Copyright (C) 1999 Andy Thomas  alt@picnic.demon.co.uk
 *
8
 * Note some portions of the UI comes from the dbbrowser plugin.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

24
25
#include "config.h"

26
27
28
29
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
30

31
32
33
34
35
#ifdef __GNUC__
#warning GTK_DISABLE_DEPRECATED
#endif
#undef GTK_DISABLE_DEPRECATED

36
37
#include <gtk/gtk.h>

38
39
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
40

41
#include "libgimp/stdplugins-intl.h"
42

43

44
45
46
#define DBL_LIST_WIDTH  250
#define DBL_WIDTH       (DBL_LIST_WIDTH + 300)
#define DBL_HEIGHT      200
47

48
typedef struct
49
{
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  GtkWidget *dlg;
  GtkWidget *clist;
  GtkWidget *ctree;
  GtkWidget *search_entry;
  GtkWidget *descr_scroll;
  GtkWidget *name_button;
  GtkWidget *blurb_button;
  GtkWidget *scrolled_win;
  GtkWidget *ctree_scrolled_win;
  GtkWidget *info_table;
  GtkWidget *paned;
  GtkWidget *left_paned;
  GtkWidget *info_align;
  gint       num_plugins;
  gint       ctree_row;
  gint       clist_row;
  gint       c1size;
  gboolean   details_showing;
} PDesc;

typedef struct
{
  gchar *menu;
  gchar *accel;
  gchar *prog;
  gchar *types;
  gchar *realname;
  gint  instime;
} PInfo;

80
81
82
83
84


/* Declare some local functions.
 */
static void   query      (void);
85
86
87
88
89
static void   run        (const gchar      *name,
                          gint              nparams,
                          const GimpParam  *param,
                          gint             *nreturn_vals,
                          GimpParam       **return_vals);
90

91
92
93
static GtkWidget * gimp_plugin_desc (void);

static gint procedure_clist_select_callback (GtkWidget      *widget,
94
95
					     gint            row,
					     gint            column,
96
					     GdkEventButton *bevent,
97
					     PDesc          *desc);
98
static gint procedure_ctree_select_callback (GtkWidget      *widget,
99
					     GtkWidget      *row,
100
101
102
103
104
105
106
107
108
109
110
111
112
					     gint            column,
					     PDesc          *desc);


static gchar *proc_type_str[] =
{
  N_("Internal GIMP procedure"),
  N_("GIMP Plug-In"),
  N_("GIMP Extension"),
  N_("Temporary Procedure")
};

static PDesc *plugindesc = NULL;
113

114
GimpPlugInInfo PLUG_IN_INFO =
115
{
116
117
118
119
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
120
121
122
123
124
};


MAIN ()

125

126
static void
127
query (void)
128
{
129
  static GimpParamDef args[] =
130
  {
131
    { GIMP_PDB_INT32, "run_mode", "Interactive, [non-interactive]" }
132
  };
133

134
  gimp_install_procedure ("plug_in_plug_in_details",
Marc Lehmann's avatar
Marc Lehmann committed
135
                          "Displays plugin details",
136
137
138
139
140
141
                          "Helps browse the plugin menus system. You can "
			  "search for plugin names, sort by name or menu "
			  "location and you can view a tree representation "
			  "of the plugin menus. Can also be of help to find "
			  "where new plugins have installed themselves in "
			  "the menuing system",
142
143
144
                          "Andy Thomas",
                          "Andy Thomas",
                          "1999",
145
			  N_("<Toolbox>/Xtns/_Plugin Details"),
146
			  "",
147
                          GIMP_PLUGIN,
148
			  G_N_ELEMENTS (args), 0,
149
150
151
152
                          args, NULL);
}

static void
153
154
155
156
157
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
158
{
159
  static GimpParam  values[2];
160
  GimpRunMode       run_mode;
161
162
163
164

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
165
  *return_vals  = values;
166

167
168
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
169

170
  INIT_I18N ();
171

172
  if (strcmp (name, "plug_in_plug_in_details") == 0)
173
    {
174
175
      GtkWidget *plugin_dialog;

176
177
      *nreturn_vals = 1;

178
      values[0].data.d_status = GIMP_PDB_SUCCESS;
179

180
181
      plugin_dialog = gimp_plugin_desc ();

182
183
184
185
      gtk_main ();
    }
}

186
187
/* Bit of a fiddle but sorta has the effect I want... */

188
static void
189
details_callback (GtkWidget *widget,
190
                  PDesc     *pdesc)
191
{
192
  GtkLabel *lab = GTK_LABEL (GTK_BIN (widget)->child);
193

194
  /* This is a lame hack:
195
196
197
198
199
200
     We add the description on the right on the first details_callback.
     Otherwise the window reacts quite weird on resizes */
  if (pdesc->descr_scroll == NULL)
    {
      pdesc->descr_scroll = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pdesc->descr_scroll),
201
				      GTK_POLICY_ALWAYS,
202
				      GTK_POLICY_ALWAYS);
203
204
      gtk_widget_set_size_request (pdesc->descr_scroll,
				   DBL_WIDTH - DBL_LIST_WIDTH, -1);
205
206
      gtk_paned_pack2 (GTK_PANED (pdesc->paned), pdesc->descr_scroll,
		       FALSE, TRUE);
207
208
209
      gtk_clist_select_row (GTK_CLIST(pdesc->clist), pdesc->clist_row, -1);
    }

210
  if (pdesc->details_showing == FALSE)
211
    {
212
213
      GTK_PANED (pdesc->paned)->child1_resize = FALSE;
      gtk_label_set_text (lab, _("Details <<"));
214
215
216
217
218
      gtk_widget_show (pdesc->descr_scroll);
      pdesc->details_showing = TRUE;
    }
  else
    {
219
220
221
      GtkWidget *p = GTK_WIDGET (pdesc->paned)->parent;
      GTK_PANED (pdesc->paned)->child1_resize = TRUE;
      GTK_PANED (pdesc->paned)->child2_resize = TRUE;
Hans Breuer's avatar
Hans Breuer committed
222

223
      gtk_label_set_text (lab, _("Details >>"));
224
      gtk_widget_hide (pdesc->descr_scroll);
225
226
      gtk_paned_set_position (GTK_PANED (pdesc->paned),
			      p->allocation.width);/*plugindesc->c1size);*/
227
228
229
230
231
      pdesc->details_showing = FALSE;
    }
}

static gchar *
232
format_menu_path (gchar *s)
233
{
234
235
  gchar **str_array;
  gchar  *newstr = NULL;
236

237
  if (!s)
238
239
    return s;

240
  str_array = g_strsplit (s, "/", 0);
241

242
  newstr = g_strjoinv ("->", str_array);
243

244
  g_strfreev (str_array);
245
246
247
248
249

  return newstr;
}

static gint
250
251
procedure_general_select_callback (PDesc *pdesc,
				   PInfo *pinfo)
252
{
253
  gchar           *selected_proc_blurb;
254
  gchar           *selected_proc_help;
255
256
257
  gchar           *selected_proc_author;
  gchar           *selected_proc_copyright;
  gchar           *selected_proc_date;
258
  GimpPDBProcType  selected_proc_type;
259
260
261
262
  gint             selected_nparams;
  gint             selected_nreturn_vals;
  GimpParamDef    *selected_params;
  GimpParamDef    *selected_return_vals;
263
264
265
266
267
268
269
270
  GtkWidget       *label;
  GtkWidget       *help;
  GtkWidget       *text_view;
  GtkTextBuffer   *text_buffer;
  GtkWidget       *old_table;
  GtkWidget       *old_align;
  gint             table_row = 0;
  gchar           *str;
271
272
273
274

  g_return_val_if_fail (pdesc != NULL, FALSE);
  g_return_val_if_fail (pinfo != NULL, FALSE);

275
276
277
  if (pdesc->descr_scroll == NULL)
    return FALSE;

278
279
280
  selected_proc_blurb     = NULL;
  selected_proc_help      = NULL;
  selected_proc_author    = NULL;
281
  selected_proc_copyright = NULL;
282
283
284
285
286
287
  selected_proc_date      = NULL;
  selected_proc_type      = 0;
  selected_nparams        = 0;
  selected_nreturn_vals   = 0;
  selected_params         = NULL;
  selected_return_vals    = NULL;
288

289
290
291
  gimp_procedural_db_proc_info (pinfo->realname,
				&selected_proc_blurb,
				&selected_proc_help,
292
				&selected_proc_author,
293
294
295
296
				&selected_proc_copyright,
				&selected_proc_date,
				&selected_proc_type,
				&selected_nparams, &selected_nreturn_vals,
297
				&selected_params,  &selected_return_vals);
298
299
300
301

  old_table = pdesc->info_table;
  old_align = pdesc->info_align;

302
  pdesc->info_table = gtk_table_new (10, 5, FALSE);
303
304
  pdesc->info_align = gtk_alignment_new (0.5, 0.5, 0, 0);

305
  gtk_table_set_col_spacings (GTK_TABLE (pdesc->info_table), 3);
306
307
308

  /* Number of plugins */

Sven Neumann's avatar
Sven Neumann committed
309
  str = g_strdup_printf (_("Number of Plugin Interfaces: %d"),
310
311
312
			 pdesc->num_plugins);
  label = gtk_label_new (str);
  g_free (str);
313
314
315
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
316
  gtk_widget_show (label);
317
318
  table_row++;

319
  label = gtk_hseparator_new (); /* ok, not really a label ... :) */
320
321
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
322
  gtk_widget_show (label);
323
324
325
326
  table_row++;

  /* menu path */

327
  label = gtk_label_new (_("Menu Path:"));
328
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
329
330
331
332
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
  gtk_widget_show(label);

333
  label = gtk_label_new (format_menu_path (pinfo->menu));
334
335
336
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
337
  gtk_widget_show (label);
338
339
  table_row++;

340
  label = gtk_hseparator_new (); /* ok, not really a label ... :) */
341
342
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
343
  gtk_widget_show (label);
344
345
346
347
  table_row++;

  /* show the name */

348
  label = gtk_label_new (_("Name:"));
349
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
350
351
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
352
  gtk_widget_show (label);
353

354
355
356
  label = gtk_entry_new ();
  gtk_entry_set_text (GTK_ENTRY (label), pinfo->realname);
  gtk_entry_set_editable (GTK_ENTRY (label), FALSE);
357
358
  gtk_table_attach (GTK_TABLE(pdesc->info_table), label,
		    1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
359
  gtk_widget_show (label);
360
361
  table_row++;

362
  label = gtk_hseparator_new (); /* ok, not really a label ... :) */
363
364
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
365
  gtk_widget_show (label);
366
367
368
369
  table_row++;

  /* show the description */

370
  label = gtk_label_new (_("Blurb:"));
371
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
372
373
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
374
  gtk_widget_show (label);
375

376
  label = gtk_label_new (selected_proc_blurb);
377
378
379
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
380
  gtk_widget_show (label);
381
382
  table_row++;

383
  label = gtk_hseparator_new (); /* ok, not really a label ... :) */
384
385
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
386
  gtk_widget_show (label);
387
388
389
  table_row++;

  /* show the help */
390
  if (selected_proc_help && (strlen (selected_proc_help) > 1))
391
    {
392
      label = gtk_label_new (_("Help:"));
393
      gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
394
      gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
395
			0, 1, table_row, table_row+1,
396
			GTK_FILL, GTK_FILL, 3, 0);
397
      gtk_widget_show (label);
398

399
400
401
402
403
404
405
      help = gtk_table_new (2, 2, FALSE);
      gtk_table_set_row_spacing (GTK_TABLE (help), 0, 2);
      gtk_table_set_col_spacing (GTK_TABLE (help), 0, 2);
      gtk_table_attach (GTK_TABLE (pdesc->info_table), help,
			1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
      gtk_widget_show (help);
      table_row++;
406

Hans Breuer's avatar
Hans Breuer committed
407
408
409
410
      text_buffer = gtk_text_buffer_new  (NULL);
      gtk_text_buffer_set_text (text_buffer, selected_proc_help, -1);

      text_view = gtk_text_view_new_with_buffer (text_buffer);
411
      g_object_unref (text_buffer);
Hans Breuer's avatar
Hans Breuer committed
412
413
414
415

      gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
      gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);

416
      gtk_widget_set_size_request (text_view, -1, 60);
Hans Breuer's avatar
Hans Breuer committed
417
      gtk_table_attach (GTK_TABLE (help), text_view, 0, 1, 0, 1,
418
419
			GTK_EXPAND | GTK_SHRINK | GTK_FILL,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
Hans Breuer's avatar
Hans Breuer committed
420
      gtk_widget_show (text_view);
421

422
      label = gtk_hseparator_new (); /* ok, not really a label ... :) */
423
424
      gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
			0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
425
      gtk_widget_show (label);
426
427
428
429
430
431

      table_row++;
    }

  /* show the type */

432
  label = gtk_label_new (_("Type:"));
433
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
434
435
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
436
  gtk_widget_show (label);
437

438
  label = gtk_label_new (gettext (proc_type_str[selected_proc_type]));
439
440
441
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
442
  gtk_widget_show (label);
443
444
  table_row++;

445
  label = gtk_hseparator_new (); /* ok, not really a label ... :) */
446
447
  gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
		    0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
448
  gtk_widget_show (label);
449
450
451
452
  table_row++;

  /* Remove old and replace with new */

453
454
  if (old_table)
    gtk_widget_destroy (old_table);
455

456
457
  if (old_align)
    gtk_widget_destroy (old_align);
458
459
460

  gtk_container_add (GTK_CONTAINER (pdesc->info_align),pdesc->info_table);

461
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (pdesc->descr_scroll),
462
463
					 pdesc->info_align);

464
465
466
467
468
469
  gtk_widget_show (pdesc->info_table);
  gtk_widget_show (pdesc->info_align);

  if (selected_proc_blurb)
    g_free (selected_proc_blurb);
  if (selected_proc_help)
470
    g_free (selected_proc_help);
471
472
473
474
475
476
477
478
479
480
  if (selected_proc_author)
    g_free (selected_proc_author);
  if (selected_proc_copyright)
    g_free (selected_proc_copyright);
  if (selected_proc_date)
    g_free (selected_proc_date);
  if (selected_params)
    g_free (selected_params);
  if (selected_return_vals)
    g_free (selected_return_vals);
481

482
483
484
  return FALSE;
}

485
static void
486
487
expand_to (PDesc        *pdesc,
	   GtkCTreeNode *parent)
488
{
489
  if (parent)
490
    {
491
      expand_to (pdesc, (GTK_CTREE_ROW (parent))->parent);
492
      gtk_ctree_expand (GTK_CTREE (pdesc->ctree), parent);
493
494
495
496
    }
}

static gint
497
procedure_clist_select_callback (GtkWidget      *widget,
498
499
				 gint            row,
				 gint            column,
500
				 GdkEventButton *bevent,
501
                                 PDesc          *pdesc)
502
{
503
  PInfo        *pinfo;
504
  GtkCTreeNode *found_node;
505
506
507

  g_return_val_if_fail (pdesc != NULL, FALSE);

508
  pinfo = (PInfo *) gtk_clist_get_row_data (GTK_CLIST (widget), row);
509

510
  if (!pinfo)
511
512
513
514
    return FALSE;

  /* Must select the correct one in the ctree structure */

515
516
  found_node = gtk_ctree_find_by_row_data (GTK_CTREE (pdesc->ctree),
					   NULL, pinfo);
517

518
  if (found_node)
519
    {
520
521
522
      GtkCTreeRow  *ctr;
      GtkCTreeNode *parent;
      gint          sel_row;
523
524
525

      /* Make sure this is expanded */

526
      ctr = GTK_CTREE_ROW (found_node);
527

528
      parent = GTK_CTREE_NODE (ctr->parent);
529

530
      expand_to (pdesc, parent);
531

532
      sel_row = gtk_clist_find_row_from_data (GTK_CLIST (pdesc->ctree), pinfo);
533

534
535
      gtk_widget_hide (pdesc->ctree);
      gtk_widget_show (pdesc->ctree);
536

537
538
539
      g_signal_handlers_block_by_func (pdesc->ctree,
				       G_CALLBACK (procedure_ctree_select_callback),
				       pdesc);
540

541
      gtk_clist_select_row (GTK_CLIST (pdesc->ctree), sel_row, -1);
542
      gtk_ctree_select (GTK_CTREE (pdesc->ctree), found_node);
543

544
545
546
547
      gtk_clist_moveto (GTK_CLIST (pdesc->ctree),
			sel_row,
			0,
			0.5, 0.5);
548

549
550
551
      g_signal_handlers_unblock_by_func (pdesc->ctree,
					 G_CALLBACK (procedure_ctree_select_callback),
					 pdesc);
552
553
554
555
556

      pdesc->ctree_row = sel_row;
    }
  else
    {
557
      g_warning ("Failed to find node in ctree");
558
559
    }

560
  return procedure_general_select_callback (pdesc, pinfo);
561
562
}

563
/* This was an attempt to get around a problem in gtk
564
565
566
567
568
569
570
571
572
573
574
575
 * where the scroll windows that contain ctree and clist
 * refuse to respond to the moveto funcions unless the
 * widgets are visible.
 * Still did not work 100% even after this.
 * Note the scrollbars are in the correct position but the
 * widget needs to be redrawn at the correct location.
 */

static gint
page_select_callback (GtkNotebook     *notebook,
		      GtkNotebookPage *page,
		      guint            page_num,
576
                      PDesc           *pdesc)
577
{
578
  if (page_num == 0)
579
    {
580
      gtk_clist_select_row (GTK_CLIST (pdesc->clist), pdesc->clist_row, -1);
581
582
583
584
      gtk_clist_moveto (GTK_CLIST (pdesc->clist),
			pdesc->clist_row,
			0,
			0.5, 0.0);
585
586
587
    }
  else
    {
588
      gtk_clist_select_row (GTK_CLIST (pdesc->ctree), pdesc->ctree_row, -1);
589
590
591
592
      gtk_clist_moveto (GTK_CLIST (pdesc->ctree),
			pdesc->ctree_row,
			0,
			0.5, 0.0);
593
    }
594

595
596
597
598
599
  return FALSE;
}

static gint
procedure_ctree_select_callback (GtkWidget *widget,
600
601
				 GtkWidget *row,
				 gint       column,
602
                                 PDesc     *pdesc)
603
{
604
605
606
  PInfo    *pinfo;
  gboolean  is_leaf;
  gint      sel_row;
607
608
609

  /* row is not a leaf the we have no interest in it */

610
611
612
613
614
615
616
617
618
619
  gtk_ctree_get_node_info (GTK_CTREE (widget),
			   GTK_CTREE_NODE (row),
			   NULL,
			   NULL,
			   NULL,
			   NULL,
			   NULL,
			   NULL,
			   &is_leaf,
			   NULL);
620

621
622
  if (!is_leaf)
    return FALSE;
623

624
625
  pinfo = (PInfo *) gtk_ctree_node_get_row_data (GTK_CTREE (widget),
						 GTK_CTREE_NODE (row));
626
627
628
629

  /* Must set clist to this one */
  /* Block signals */

630
631
632
  g_signal_handlers_block_by_func (pdesc->clist,
				   G_CALLBACK (procedure_clist_select_callback),
				   pdesc);
633

634
  sel_row = gtk_clist_find_row_from_data (GTK_CLIST (pdesc->clist), pinfo);
635
  gtk_clist_select_row (GTK_CLIST (pdesc->clist), sel_row, -1);
636
637
638
639
  gtk_clist_moveto (GTK_CLIST (pdesc->clist),
		    sel_row,
		    0,
		    0.5, 0.5);
640

641
642
643
  g_signal_handlers_unblock_by_func (pdesc->clist,
				     G_CALLBACK (procedure_clist_select_callback),
				     pdesc);
644
645

  pdesc->clist_row = sel_row;
646

647
  return procedure_general_select_callback (pdesc, pinfo);
648
649
650
}

static void
651
pinfo_free (gpointer p)
652
{
653
  PInfo *pinfo = p;
654

655
656
657
658
659
660
  g_free (pinfo->menu);
  g_free (pinfo->accel);
  g_free (pinfo->prog);
  g_free (pinfo->types);
  g_free (pinfo->realname);
  g_free (pinfo);
661
662
}

663
static GtkCTreeNode *
664
665
666
get_parent (PDesc       *pdesc,
	    GHashTable  *ghash,
	    gchar       *mpath)
667
668
669
670
671
672
673
{
  GtkCTreeNode *parent;
  GtkCTreeNode *last_parent;
  gchar *tmp_ptr;
  gchar *str_ptr;
  gchar *leaf_ptr;
  gchar *labels[3];
674

675
  if (mpath == NULL)
676
677
    return NULL; /* Parent is root */

678
  parent = g_hash_table_lookup (ghash, mpath);
679

680
  if (parent)
681
682
    {
      /* found node */
683
      return parent;
684
685
686
    }

  /* Next one up */
687
  tmp_ptr = g_strdup (mpath);
688

689
  str_ptr = strrchr (tmp_ptr,'/');
690

691
  if (str_ptr == NULL)
692
693
694
695
696
697
698
699
700
    {
      /*       printf("Root node for %s\n",mpath); */
      leaf_ptr = mpath;
      tmp_ptr = "<root>";
      last_parent = NULL;
    }
  else
    {
      leaf_ptr = g_strdup(str_ptr+1);
701

702
      *str_ptr = '\000';
703
704

      last_parent = get_parent (pdesc, ghash, tmp_ptr);
705
706
    }

707
708
709
  labels[0] = g_strdup (leaf_ptr);
  labels[1] = g_strdup ("");
  labels[2] = g_strdup ("");
710
711
712

  /*   printf("get_parent::creating node %s under %s\n",leaf_ptr,tmp_ptr); */

713
714
715
716
717
718
719
720
721
722
723
724
  parent = gtk_ctree_insert_node (GTK_CTREE (pdesc->ctree),
				  last_parent,
				  NULL,
				  labels,
				  4,
				  NULL,
				  NULL,
				  NULL,
				  NULL,
				  FALSE,
				  FALSE);

725
  g_hash_table_insert (ghash, mpath, parent);
726
727
728
729
730

  return parent;
}

static void
731
732
733
734
735
736
737
insert_into_ctree (PDesc      *pdesc,
		   gchar      *name,
		   gchar      *xtimestr,
		   gchar      *menu_str,
		   gchar      *types_str,
		   GHashTable *ghash,
		   PInfo      *pinfo)
738
739
740
741
742
743
744
745
746
747
748
{
  gchar *labels[3];
  gchar *str_ptr;
  gchar *tmp_ptr;
  gchar *leaf_ptr;
  GtkCTreeNode *parent = NULL;
  GtkCTreeNode *leaf_node = NULL;

  /* Find all nodes */
  /* Last one is the leaf part */

749
750
751
752
753
  tmp_ptr = g_strdup (menu_str);

  str_ptr = strrchr (tmp_ptr, '/');

  if (str_ptr == NULL)
754
755
    return; /* No node */

756
  leaf_ptr = g_strdup (str_ptr + 1);
757
758
759
760
761

  *str_ptr = '\000';

  /*   printf("inserting %s...\n",menu_str); */

762
  parent = get_parent (pdesc, ghash, tmp_ptr);
763
764
765

  /* Last was a leaf */
  /*   printf("found leaf %s parent = %p\n",leaf_ptr,parent); */
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
  labels[0] = g_strdup (name);
  labels[1] = g_strdup (xtimestr);
  labels[2] = g_strdup (types_str);

  leaf_node = gtk_ctree_insert_node (GTK_CTREE (pdesc->ctree),
				     parent,
				     NULL,
				     labels,
				     4,
				     NULL,
				     NULL,
				     NULL,
				     NULL,
				     TRUE,
				     FALSE);

  gtk_ctree_node_set_row_data (GTK_CTREE (pdesc->ctree), leaf_node, pinfo);
783
784
785
}

static void
786
787
get_plugin_info (PDesc       *pdesc,
		 const gchar *search_text)
788
{
Sven Neumann's avatar
Sven Neumann committed
789
  GimpParam *return_vals;
790
  gint nreturn_vals;
791
792
793
794
795
796
797
798
  gint  row_count = 0;
  gchar **menu_strs;
  gchar **accel_strs;
  gchar **prog_strs;
  gchar **types_strs;
  gchar **realname_strs;
  gint  *time_ints;

799
  GHashTable* ghash = g_hash_table_new (g_str_hash, g_str_equal);
800

801
  if (!search_text)
802
803
804
805
    search_text = "";

  return_vals = gimp_run_procedure ("gimp_plugins_query",
                                    &nreturn_vals,
Sven Neumann's avatar
Sven Neumann committed
806
807
				    GIMP_PDB_STRING,search_text,
                                    GIMP_PDB_END);
808

Sven Neumann's avatar
Sven Neumann committed
809
  if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
810
811
812
    {
      int loop;
      pdesc->num_plugins = return_vals[1].data.d_int32;
813
814
815
816
817
818
      menu_strs          = return_vals[2].data.d_stringarray;
      accel_strs         = return_vals[4].data.d_stringarray;
      prog_strs          = return_vals[6].data.d_stringarray;
      types_strs         = return_vals[8].data.d_stringarray;
      time_ints          = return_vals[10].data.d_int32array;
      realname_strs      = return_vals[12].data.d_stringarray;
819
820
821

      for (loop = 0; loop < return_vals[1].data.d_int32; loop++)
	{
822
	  PInfo *pinfo;
823
824
825
826
827
828
829
830
831
832
833
834
835
836
	  gchar *labels[4];
	  gchar *name;
	  gchar xtimestr[50];
	  struct tm * x;
	  time_t tx;
	  int ret;

	  name = strrchr (menu_strs[loop], '/');

	  if (name)
	    name = name + 1;
	  else
	    name = menu_strs[loop];

837
	  pinfo = g_new0 (PInfo, 1);
838
839

	  tx = time_ints[loop];
840
	  if (tx)
841
	    {
842
843
              gchar *utf8;

844
845
	      x = localtime (&tx);
	      ret = strftime (xtimestr, sizeof (xtimestr), "%c", x);
846
	      xtimestr[ret] = 0;
847
848
849
850
851
852
853

              if ((utf8 = g_locale_to_utf8 (xtimestr, -1, NULL, NULL, NULL)))
                {
                  strncpy (xtimestr, utf8, sizeof (xtimestr));
                  xtimestr[sizeof (xtimestr) - 1] = 0;
                  g_free (utf8);
                }
854
855
	    }
	  else
856
	    strcpy (xtimestr,"");
857

858
859
860
861
862
	  pinfo->menu     = g_strdup (menu_strs[loop]);
	  pinfo->accel    = g_strdup (accel_strs[loop]);
	  pinfo->prog     = g_strdup (prog_strs[loop]);
	  pinfo->types    = g_strdup (types_strs[loop]);
	  pinfo->instime  = time_ints[loop];
863
	  pinfo->realname = g_strdup (realname_strs[loop]);
864

865
866
867
868
	  labels[0] = g_strdup (name);
	  labels[1] = g_strdup (xtimestr);
	  labels[2] = g_strdup (menu_strs[loop]);
	  labels[3] = g_strdup (types_strs[loop]);
869

870
	  gtk_clist_insert (GTK_CLIST (pdesc->clist), row_count, labels);
871

872
873
	  gtk_clist_set_row_data_full (GTK_CLIST (pdesc->clist), row_count,
				       pinfo, pinfo_free);
874
875
876
877

	  row_count++;

	  /* Now do the tree view.... */
878
879
880
	  g_signal_handlers_block_by_func (pdesc->ctree,
					   G_CALLBACK (procedure_ctree_select_callback),
					   pdesc);
881
882
883
884
885
886
887
	  insert_into_ctree (pdesc,
			     name,
			     xtimestr,
			     menu_strs[loop],
			     types_strs[loop],
			     ghash,
			     pinfo);
888
889
890
	  g_signal_handlers_unblock_by_func (pdesc->ctree,
					     G_CALLBACK (procedure_ctree_select_callback),
					     pdesc);
891
892
893
894
895
896
897
	}
    }

  gimp_destroy_params (return_vals, nreturn_vals);
}

static gint
898
899
900
date_sort (GtkCList      *clist,
	   gconstpointer  ptr1,
	   gconstpointer  ptr2)
901
902
903
904
905
{
  GtkCListRow *row1 = (GtkCListRow *) ptr1;
  GtkCListRow *row2 = (GtkCListRow *) ptr2;

  /* Get the data for the row */
906
907
  PInfo *row1_pinfo = row1->data;
  PInfo *row2_pinfo = row2->data;
908
909
910

  /* Want to sort on the date field */

911
  if (row2_pinfo->instime < row1_pinfo->instime)
912
    {
913
      return -1;
914
    }
915
916

  if (row2_pinfo->instime > row1_pinfo->instime)
917
    {
918
      return 1;
919
920
    }

921
  return 0;
922
923
}

924
925
926
static void
clist_click_column (GtkCList *clist,
		    gint      column,
927
		    gpointer  data)
928
929
930
{
  if (column == 1)
    {
931
      gtk_clist_set_compare_func (clist, date_sort);
932
933
934
    }
  else
    {
935
      gtk_clist_set_compare_func (clist, NULL); /* Set back to default */
936
937
938
939
940
941
942
943
944
945
946
    }

  if (column == clist->sort_column)
    {
      if (clist->sort_type == GTK_SORT_ASCENDING)
	clist->sort_type = GTK_SORT_DESCENDING;
      else
	clist->sort_type = GTK_SORT_ASCENDING;
    }
  else
    gtk_clist_set_sort_column (clist, column);
947

948
949
950
  gtk_clist_sort (clist);
}

951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
static void
dialog_response (GtkWidget *widget,
                 gint       response_id,
                 PDesc     *pdesc)
{
  switch (response_id)
    {
    case GTK_RESPONSE_OK:
      {
        const gchar *search_text = NULL;

        if (widget != NULL)
          {
            /* The result of a button press... read entry data */
            search_text =
              gtk_entry_get_text (GTK_ENTRY (plugindesc->search_entry));
          }

        gtk_clist_freeze (GTK_CLIST (pdesc->ctree));
        gtk_clist_clear (GTK_CLIST (pdesc->ctree));
        gtk_clist_freeze (GTK_CLIST (pdesc->clist));
        gtk_clist_clear (GTK_CLIST (pdesc->clist));

        get_plugin_info (pdesc, search_text);

        gtk_clist_columns_autosize (GTK_CLIST (plugindesc->clist));

        gtk_clist_sort (GTK_CLIST (pdesc->clist));
        gtk_clist_thaw (GTK_CLIST (pdesc->clist));
        gtk_ctree_sort_recursive (GTK_CTREE (pdesc->ctree), NULL);
        gtk_clist_thaw (GTK_CLIST (pdesc->ctree));
      }
      break;

    default:
      gtk_widget_destroy (pdesc->dlg);
      gtk_main_quit ();
      break;
    }
}
991
992

static GtkWidget *
993
gimp_plugin_desc (void)
994
{
995
996
997
998
  GtkWidget  *button;
  GtkWidget  *hbox, *searchhbox, *vbox;
  GtkWidget  *label, *notebook, *swindow;
  gchar      *clabels[4];
999

1000
  gimp_ui_init ("plugindetails", FALSE);
1001

1002
  plugindesc = g_new0 (PDesc, 1);
1003
1004

  /* the dialog box */
1005
  plugindesc->dlg =
1006
    gimp_dialog_new (_("Plugin Descriptions"), "plugindetails",
1007
                     NULL, 0,
1008
		     gimp_standard_help_func, "filters/plugindetails.html",
1009

1010
1011
		     GTK_STOCK_CLOSE,     GTK_RESPONSE_CLOSE,
		     _("Search by Name"), GTK_RESPONSE_OK,
1012
1013

		     NULL);
1014
1015
1016

  plugindesc->details_showing = FALSE;

1017
1018
  g_signal_connect (plugindesc->dlg, "response",
                    G_CALLBACK (dialog_response),
1019
                    plugindesc);
1020

1021
  /* hbox : left=notebook ; right=description */
1022

1023
  plugindesc->paned = hbox = gtk_hpaned_new ();
1024
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (plugindesc->dlg)->vbox),
1025
1026
		      hbox, TRUE, TRUE, 0);
  gtk_widget_show (hbox);
1027

1028
  /* left = vbox : the list and the search entry */
1029

1030
  plugindesc->left_paned = vbox = gtk_vbox_new (FALSE, 0);
1031
  gtk_container_set_border_width (