gedit-documents-panel.c 27.2 KB
Newer Older
Paolo Borelli's avatar
Paolo Borelli committed
1 2 3 4
/*
 * gedit-documents-panel.c
 * This file is part of gedit
 *
5
 * Copyright (C) 2005 - Paolo Maggi
Paolo Borelli's avatar
Paolo Borelli committed
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 59 Temple Place, Suite 330,
Paolo Borelli's avatar
Paolo Borelli committed
20 21
 * Boston, MA 02111-1307, USA.
 */
22

Paolo Borelli's avatar
Paolo Borelli committed
23
/*
24 25 26
 * Modified by the gedit Team, 2005. See the AUTHORS file for a
 * list of people on the gedit Team.
 * See the ChangeLog files for a list of changes.
Paolo Borelli's avatar
Paolo Borelli committed
27 28 29 30 31 32 33 34 35
 *
 * $Id$
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gedit-documents-panel.h"
36
#include "gedit-debug.h"
Paolo Borelli's avatar
Paolo Borelli committed
37
#include "gedit-utils.h"
38
#include "gedit-multi-notebook.h"
39
#include "gedit-notebook.h"
40
#include "gedit-cell-renderer-button.h"
Paolo Borelli's avatar
Paolo Borelli committed
41 42 43 44 45 46 47 48 49

#include <glib/gi18n.h>

#define GEDIT_DOCUMENTS_PANEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \
						  GEDIT_TYPE_DOCUMENTS_PANEL,            \
						  GeditDocumentsPanelPrivate))

struct _GeditDocumentsPanelPrivate
{
50 51
	GeditWindow        *window;
	GeditMultiNotebook *mnb;
Paolo Borelli's avatar
Paolo Borelli committed
52

53 54
	GtkWidget          *treeview;
	GtkTreeModel       *model;
55

56
	guint               selection_changed_handler_id;
57 58
	guint               refresh_idle_id;

59 60 61
	guint               adding_tab : 1;
	guint               is_reodering : 1;
	guint               setting_active_notebook : 1;
Paolo Borelli's avatar
Paolo Borelli committed
62 63
};

64
G_DEFINE_TYPE(GeditDocumentsPanel, gedit_documents_panel, GTK_TYPE_BOX)
Paolo Borelli's avatar
Paolo Borelli committed
65 66 67 68

enum
{
	PROP_0,
69 70 71 72 73
	PROP_WINDOW
};

enum
{
74
	PIXBUF_COLUMN = 0,
75
	NAME_COLUMN,
76
	NOTEBOOK_COLUMN,
77 78
	TAB_COLUMN,
	N_COLUMNS
Paolo Borelli's avatar
Paolo Borelli committed
79 80
};

81 82 83 84 85 86 87 88 89 90 91
#define MAX_DOC_NAME_LENGTH	60
#define NB_NAME_DATA_KEY	"DocumentsPanelNotebookNameKey"
#define TAB_NB_DATA_KEY		"DocumentsPanelTabNotebookKey"


static gchar *
notebook_get_name (GeditMultiNotebook *mnb,
		   GeditNotebook      *notebook)
{
	guint num;

92
	num = gedit_multi_notebook_get_notebook_num (mnb, notebook);
93 94 95

	return g_markup_printf_escaped ("Tab Group %i", num + 1);
}
Paolo Borelli's avatar
Paolo Borelli committed
96 97 98 99 100 101 102 103 104

static gchar *
tab_get_name (GeditTab *tab)
{
	GeditDocument *doc;
	gchar *name;
	gchar *docname;
	gchar *tab_name;

105 106
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL);

	doc = gedit_tab_get_document (tab);

	name = gedit_document_get_short_name_for_display (doc);

	/* Truncate the name so it doesn't get insanely wide. */
	docname = gedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH);

	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)))
	{
		if (gedit_document_get_readonly (doc))
		{
			tab_name = g_markup_printf_escaped ("<i>%s</i> [<i>%s</i>]",
							    docname,
122
							    _("Read-Only"));
Paolo Borelli's avatar
Paolo Borelli committed
123 124 125
		}
		else
		{
126
			tab_name = g_markup_printf_escaped ("<i>%s</i>",
Paolo Borelli's avatar
Paolo Borelli committed
127 128 129 130 131 132 133 134 135
							    docname);
		}
	}
	else
	{
		if (gedit_document_get_readonly (doc))
		{
			tab_name = g_markup_printf_escaped ("%s [<i>%s</i>]",
							    docname,
136
							    _("Read-Only"));
Paolo Borelli's avatar
Paolo Borelli committed
137 138 139 140 141 142 143 144 145 146 147 148 149
		}
		else
		{
			tab_name = g_markup_escape_text (docname, -1);
		}
	}

	g_free (docname);
	g_free (name);

	return tab_name;
}

150
static gboolean
151 152 153 154
get_iter_from_tab (GeditDocumentsPanel *panel,
		   GeditNotebook       *notebook,
		   GeditTab            *tab,
		   GtkTreeIter         *tab_iter)
155 156
{
	GtkTreeIter parent;
157 158
	gboolean success;
	gboolean search_notebook;
159 160 161 162 163 164 165 166

	/* Note: we cannot use the functions of the MultiNotebook
	 *       because in cases where the notebook or tab has been
	 *       removed already we will fail to get an iter.
	 */

	gedit_debug (DEBUG_PANEL);

167 168
	g_assert (notebook != NULL);
	/* tab may be NULL if just the notebook is specified */
169
	g_assert (tab_iter != NULL);
170

171
	search_notebook = (gedit_multi_notebook_get_n_notebooks (panel->priv->mnb) > 1);
172

173 174
	if (!gtk_tree_model_get_iter_first (panel->priv->model, &parent))
		return FALSE;
175

176
	success = FALSE;
177 178
	do
	{
179 180
		GtkTreeIter iter;

181
		if (search_notebook)
182 183 184 185 186 187 188 189
		{
			GeditNotebook *current_notebook;

			gtk_tree_model_get (panel->priv->model,
					    &parent,
					    NOTEBOOK_COLUMN, &current_notebook,
					    -1);

190
			if (current_notebook != NULL)
191
			{
192 193
				gboolean is_cur;
				is_cur = (current_notebook == notebook);
194

195 196 197
				g_object_unref (current_notebook);

				if (is_cur)
198
				{
199 200 201
					/* notebook found */
					search_notebook = FALSE;
					gtk_tree_model_iter_children (panel->priv->model, &iter, &parent);
202 203 204
				}
			}
		}
205
		else
206
		{
207
			iter = parent;
208
		}
209

210
		if (!search_notebook)
211
		{
212
			if (tab == NULL)
213
			{
214 215
				success = TRUE;
				break;
216
			}
217

218 219 220
			g_assert (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (panel->priv->model),
								&iter));

221 222
			do
			{
223
				GeditTab *current_tab;
224

225 226 227 228 229
				gtk_tree_model_get (panel->priv->model,
						    &iter,
						    TAB_COLUMN, &current_tab,
						    -1);

230
				if (current_tab != NULL)
231
				{
232 233 234 235
					gboolean is_cur;

					is_cur = (current_tab == tab);
					g_object_unref (current_tab);
236

237 238 239 240
					if (is_cur)
					{
						*tab_iter = iter;
						success = TRUE;
241

242 243 244
						/* break 2; */
						goto out;
					}
245 246 247 248 249 250 251 252 253 254 255 256 257
				}
			} while (gtk_tree_model_iter_next (panel->priv->model, &iter));

			/* We already found the notebook and the tab was not found */
			g_assert (!success);
		}
	} while (gtk_tree_model_iter_next (panel->priv->model, &parent));

out:

	return success;
}

258
static void
259 260
select_iter (GeditDocumentsPanel *panel,
	     GtkTreeIter         *iter)
Paolo Borelli's avatar
Paolo Borelli committed
261
{
262 263
	GtkTreeView *treeview = GTK_TREE_VIEW (panel->priv->treeview);
	GtkTreeSelection *selection;
Paolo Borelli's avatar
Paolo Borelli committed
264 265
	GtkTreePath *path;

266 267 268
	selection = gtk_tree_view_get_selection (treeview);

	gtk_tree_selection_select_iter (selection, iter);
Paolo Borelli's avatar
Paolo Borelli committed
269

270 271 272 273 274
	path = gtk_tree_model_get_path (panel->priv->model, iter);
	gtk_tree_view_scroll_to_cell (treeview,
				      path, NULL,
				      FALSE,
				      0, 0);
Paolo Borelli's avatar
Paolo Borelli committed
275 276 277 278
	gtk_tree_path_free (path);
}

static void
279 280 281 282
select_active_tab (GeditDocumentsPanel *panel)
{
	GeditNotebook *notebook;
	GeditTab *tab;
283
	gboolean have_tabs;
284 285

	notebook = gedit_multi_notebook_get_active_notebook (panel->priv->mnb);
286
	have_tabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) > 0;
287
	tab = gedit_multi_notebook_get_active_tab (panel->priv->mnb);
Paolo Borelli's avatar
Paolo Borelli committed
288

289
	if (notebook != NULL && tab != NULL && have_tabs)
Paolo Borelli's avatar
Paolo Borelli committed
290 291 292
	{
		GtkTreeIter iter;

293 294
		if (get_iter_from_tab (panel, notebook, tab, &iter))
			select_iter (panel, &iter);
295 296
	}
}
Paolo Borelli's avatar
Paolo Borelli committed
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
static void
multi_notebook_tab_switched (GeditMultiNotebook  *mnb,
			     GeditNotebook       *old_notebook,
			     GeditTab            *old_tab,
			     GeditNotebook       *new_notebook,
			     GeditTab            *new_tab,
			     GeditDocumentsPanel *panel)
{
	gedit_debug (DEBUG_PANEL);

	if (!panel->priv->setting_active_notebook &&
	    !_gedit_window_is_removing_tabs (panel->priv->window))
	{
		GtkTreeIter iter;

313 314
		if (get_iter_from_tab (panel, new_notebook, new_tab, &iter) &&
		    gtk_tree_store_iter_is_valid (GTK_TREE_STORE (panel->priv->model), &iter))
315
		{
316
			select_iter (panel, &iter);
Paolo Borelli's avatar
Paolo Borelli committed
317 318 319 320 321
		}
	}
}

static void
322 323 324
refresh_notebook (GeditDocumentsPanel *panel,
		  GeditNotebook       *notebook,
		  GtkTreeIter         *parent)
Paolo Borelli's avatar
Paolo Borelli committed
325 326 327
{
	GList *tabs;
	GList *l;
328
	GtkTreeStore *tree_store;
Paolo Borelli's avatar
Paolo Borelli committed
329 330
	GeditTab *active_tab;

331
	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
332

333
	tree_store = GTK_TREE_STORE (panel->priv->model);
Paolo Borelli's avatar
Paolo Borelli committed
334 335 336

	active_tab = gedit_window_get_active_tab (panel->priv->window);

337
	tabs = gtk_container_get_children (GTK_CONTAINER (notebook));
Paolo Borelli's avatar
Paolo Borelli committed
338

339 340
	for (l = tabs; l != NULL; l = g_list_next (l))
	{
Paolo Borelli's avatar
Paolo Borelli committed
341 342 343 344 345 346 347 348
		GdkPixbuf *pixbuf;
		gchar *name;
		GtkTreeIter iter;

		name = tab_get_name (GEDIT_TAB (l->data));
		pixbuf = _gedit_tab_get_icon (GEDIT_TAB (l->data));

		/* Add a new row to the model */
349 350
		gtk_tree_store_append (tree_store, &iter, parent);
		gtk_tree_store_set (tree_store,
Paolo Borelli's avatar
Paolo Borelli committed
351
				    &iter,
352 353
				    PIXBUF_COLUMN, pixbuf,
				    NAME_COLUMN, name,
354
				    NOTEBOOK_COLUMN, notebook,
355
				    TAB_COLUMN, l->data,
Paolo Borelli's avatar
Paolo Borelli committed
356 357 358 359
				    -1);

		g_free (name);
		if (pixbuf != NULL)
360
		{
Paolo Borelli's avatar
Paolo Borelli committed
361
			g_object_unref (pixbuf);
362
		}
Paolo Borelli's avatar
Paolo Borelli committed
363 364 365

		if (l->data == active_tab)
		{
366 367 368
			select_iter (panel, &iter);
		}
	}
Paolo Borelli's avatar
Paolo Borelli committed
369

370 371
	g_list_free (tabs);
}
Paolo Borelli's avatar
Paolo Borelli committed
372

373 374 375 376
static void
refresh_notebook_foreach (GeditNotebook       *notebook,
			  GeditDocumentsPanel *panel)
{
377
	gboolean add_notebook;
Paolo Borelli's avatar
Paolo Borelli committed
378

379 380 381
	/* If we have only one notebook we don't want to show the notebook
	   header */
	add_notebook = (gedit_multi_notebook_get_n_notebooks (panel->priv->mnb) > 1);
382

383 384 385 386
	if (add_notebook)
	{
		GtkTreeIter iter;
		gchar *name;
387

388
		name = notebook_get_name (panel->priv->mnb, notebook);
389

390 391
		gtk_tree_store_append (GTK_TREE_STORE (panel->priv->model),
				       &iter, NULL);
392

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
		gtk_tree_store_set (GTK_TREE_STORE (panel->priv->model),
				    &iter,
				    PIXBUF_COLUMN, NULL,
				    NAME_COLUMN, name,
				    NOTEBOOK_COLUMN, notebook,
				    TAB_COLUMN, NULL,
				    -1);

		refresh_notebook (panel, notebook, &iter);

		g_free (name);
	}
	else
	{
		refresh_notebook (panel, notebook, NULL);
	}
409 410
}

411 412
static gboolean
refresh_list_idle (GeditDocumentsPanel *panel)
413
{
414 415
	GtkTreeSelection *selection;

416 417
	gedit_debug (DEBUG_PANEL);

418 419 420
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
	g_signal_handler_block (selection, panel->priv->selection_changed_handler_id);

421 422 423 424 425 426
	gtk_tree_store_clear (GTK_TREE_STORE (panel->priv->model));

	panel->priv->adding_tab = TRUE;
	gedit_multi_notebook_foreach_notebook (panel->priv->mnb,
					       (GtkCallback)refresh_notebook_foreach,
					       panel);
427
	panel->priv->adding_tab = FALSE;
Paolo Borelli's avatar
Paolo Borelli committed
428

429 430 431
	gtk_tree_view_expand_all (GTK_TREE_VIEW (panel->priv->treeview));

	select_active_tab (panel);
432 433 434

	panel->priv->refresh_idle_id = 0;

435 436
	g_signal_handler_unblock (selection, panel->priv->selection_changed_handler_id);

437 438 439 440 441 442 443 444 445 446 447 448 449
	return FALSE;
}

static void
refresh_list (GeditDocumentsPanel *panel)
{
	/* refresh in an idle so that when adding/removing many tabs
	 * the model is repopulated just once */
	if (panel->priv->refresh_idle_id == 0)
	{
		panel->priv->refresh_idle_id = gdk_threads_add_idle ((GSourceFunc) refresh_list_idle,
		                                                     panel);
	}
450 451 452 453 454 455 456 457 458 459 460 461 462
}

static void
document_changed (GtkTextBuffer       *buffer,
		  GeditDocumentsPanel *panel)
{
	gedit_debug (DEBUG_PANEL);

	select_active_tab (panel);

	g_signal_handlers_disconnect_by_func (buffer,
					      G_CALLBACK (document_changed),
					      panel);
Paolo Borelli's avatar
Paolo Borelli committed
463 464 465 466 467 468 469 470 471
}

static void
sync_name_and_icon (GeditTab            *tab,
		    GParamSpec          *pspec,
		    GeditDocumentsPanel *panel)
{
	GtkTreeIter iter;

472 473
	gedit_debug (DEBUG_PANEL);

474 475 476 477 478 479 480
	if (get_iter_from_tab (panel,
			       gedit_multi_notebook_get_active_notebook (panel->priv->mnb),
			       tab,
			       &iter))
	{
		gchar *name;
		GdkPixbuf *pixbuf;
481

482 483
		name = tab_get_name (tab);
		pixbuf = _gedit_tab_get_icon (tab);
Paolo Borelli's avatar
Paolo Borelli committed
484

485 486 487 488 489
		gtk_tree_store_set (GTK_TREE_STORE (panel->priv->model),
				    &iter,
				    PIXBUF_COLUMN, pixbuf,
				    NAME_COLUMN, name,
				    -1);
Paolo Borelli's avatar
Paolo Borelli committed
490

491 492 493 494 495
		g_free (name);
		if (pixbuf != NULL)
		{
			g_object_unref (pixbuf);
		}
496
	}
Paolo Borelli's avatar
Paolo Borelli committed
497 498 499
}

static void
500 501 502 503
multi_notebook_tab_removed (GeditMultiNotebook  *mnb,
			    GeditNotebook       *notebook,
			    GeditTab            *tab,
			    GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
504
{
505 506 507 508 509 510
	gedit_debug (DEBUG_PANEL);

	g_signal_handlers_disconnect_by_func (gedit_tab_get_document (tab),
					      G_CALLBACK (document_changed),
					      panel);

Paolo Borelli's avatar
Paolo Borelli committed
511
	g_signal_handlers_disconnect_by_func (tab,
512 513
					      G_CALLBACK (sync_name_and_icon),
					      panel);
Paolo Borelli's avatar
Paolo Borelli committed
514

515
	refresh_list (panel);
Paolo Borelli's avatar
Paolo Borelli committed
516 517 518
}

static void
519 520 521 522
multi_notebook_tab_added (GeditMultiNotebook  *mnb,
			  GeditNotebook       *notebook,
			  GeditTab            *tab,
			  GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
523
{
524 525
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
526 527 528 529
	g_signal_connect (tab,
			 "notify::name",
			  G_CALLBACK (sync_name_and_icon),
			  panel);
530
	g_signal_connect (tab,
Paolo Borelli's avatar
Paolo Borelli committed
531 532 533 534
			 "notify::state",
			  G_CALLBACK (sync_name_and_icon),
			  panel);

535
	refresh_list (panel);
536 537 538 539 540 541 542
}

static void
multi_notebook_notebook_removed (GeditMultiNotebook  *mnb,
				 GeditNotebook       *notebook,
				 GeditDocumentsPanel *panel)
{
543
	refresh_list (panel);
544 545
}

Paolo Borelli's avatar
Paolo Borelli committed
546
static void
547 548 549 550 551
multi_notebook_tabs_reordered (GeditMultiNotebook  *mnb,
                               GeditNotebook       *notebook,
                               GtkWidget           *page,
                               gint                 page_num,
                               GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
552
{
553 554
	gedit_debug (DEBUG_PANEL);

555 556
	if (panel->priv->is_reodering)
		return;
557

Paolo Borelli's avatar
Paolo Borelli committed
558
	refresh_list (panel);
559
}
Paolo Borelli's avatar
Paolo Borelli committed
560 561 562 563 564

static void
set_window (GeditDocumentsPanel *panel,
	    GeditWindow         *window)
{
565 566
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
567 568 569
	g_return_if_fail (panel->priv->window == NULL);
	g_return_if_fail (GEDIT_IS_WINDOW (window));

570
	panel->priv->window = g_object_ref (window);
571
	panel->priv->mnb = GEDIT_MULTI_NOTEBOOK (_gedit_window_get_multi_notebook (window));
Paolo Borelli's avatar
Paolo Borelli committed
572

573 574 575
	g_signal_connect (panel->priv->mnb,
			  "notebook-removed",
			  G_CALLBACK (multi_notebook_notebook_removed),
Paolo Borelli's avatar
Paolo Borelli committed
576
			  panel);
577 578 579
	g_signal_connect (panel->priv->mnb,
			  "tab-added",
			  G_CALLBACK (multi_notebook_tab_added),
Paolo Borelli's avatar
Paolo Borelli committed
580
			  panel);
581 582 583
	g_signal_connect (panel->priv->mnb,
			  "tab-removed",
			  G_CALLBACK (multi_notebook_tab_removed),
Paolo Borelli's avatar
Paolo Borelli committed
584
			  panel);
585
	g_signal_connect (panel->priv->mnb,
586
			  "page-reordered",
587 588
			  G_CALLBACK (multi_notebook_tabs_reordered),
			  panel);
589 590 591
	g_signal_connect (panel->priv->mnb,
			  "switch-tab",
			  G_CALLBACK (multi_notebook_tab_switched),
592
			  panel);
593 594

	refresh_list (panel);
Paolo Borelli's avatar
Paolo Borelli committed
595 596 597
}

static void
598 599
treeview_selection_changed (GtkTreeSelection    *selection,
			    GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
600 601 602
{
	GtkTreeIter iter;

603 604
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
605 606
	if (gtk_tree_selection_get_selected (selection, NULL, &iter))
	{
607 608 609
		GeditNotebook *notebook;
		GeditTab *tab;

610 611
		gtk_tree_model_get (panel->priv->model,
				    &iter,
612 613
				    NOTEBOOK_COLUMN, &notebook,
				    TAB_COLUMN, &tab,
Paolo Borelli's avatar
Paolo Borelli committed
614 615
				    -1);

616
		if (tab != NULL)
Paolo Borelli's avatar
Paolo Borelli committed
617
		{
618 619
			gedit_multi_notebook_set_active_tab (panel->priv->mnb,
							     tab);
620 621
			if (notebook != NULL)
				g_object_unref (notebook);
622
			g_object_unref (tab);
623
		}
624
		else if (notebook != NULL)
625 626 627 628 629 630 631 632 633 634 635 636 637
		{
			panel->priv->setting_active_notebook = TRUE;
			gtk_widget_grab_focus (GTK_WIDGET (notebook));
			panel->priv->setting_active_notebook = FALSE;

			tab = gedit_multi_notebook_get_active_tab (panel->priv->mnb);
			if (tab != NULL)
			{
				g_signal_connect (gedit_tab_get_document (tab),
						  "changed",
						  G_CALLBACK (document_changed),
						  panel);
			}
638 639

			g_object_unref (notebook);
640 641
		}
	}
Paolo Borelli's avatar
Paolo Borelli committed
642 643 644 645
}

static void
gedit_documents_panel_set_property (GObject      *object,
646 647 648
				    guint         prop_id,
				    const GValue *value,
				    GParamSpec   *pspec)
Paolo Borelli's avatar
Paolo Borelli committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
{
	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object);

	switch (prop_id)
	{
		case PROP_WINDOW:
			set_window (panel, g_value_get_object (value));
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
gedit_documents_panel_get_property (GObject    *object,
				    guint       prop_id,
				    GValue     *value,
				    GParamSpec *pspec)
{
	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object);

	switch (prop_id)
	{
		case PROP_WINDOW:
675
			g_value_set_object (value, panel->priv->window);
Paolo Borelli's avatar
Paolo Borelli committed
676
			break;
677

Paolo Borelli's avatar
Paolo Borelli committed
678 679
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
680
			break;
Paolo Borelli's avatar
Paolo Borelli committed
681 682 683 684 685 686 687
	}
}

static void
gedit_documents_panel_finalize (GObject *object)
{
	/* GeditDocumentsPanel *tab = GEDIT_DOCUMENTS_PANEL (object); */
688 689 690 691

	/* TODO disconnect signal with window */

	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
692 693 694 695

	G_OBJECT_CLASS (gedit_documents_panel_parent_class)->finalize (object);
}

696 697 698 699 700
static void
gedit_documents_panel_dispose (GObject *object)
{
	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object);

701 702
	gedit_debug (DEBUG_PANEL);

703 704 705 706 707 708
	if (panel->priv->refresh_idle_id != 0)
	{
		g_source_remove (panel->priv->refresh_idle_id);
		panel->priv->refresh_idle_id = 0;
	}

709
	g_clear_object (&panel->priv->window);
710 711 712 713

	G_OBJECT_CLASS (gedit_documents_panel_parent_class)->dispose (object);
}

714
static void
Paolo Borelli's avatar
Paolo Borelli committed
715 716 717 718 719
gedit_documents_panel_class_init (GeditDocumentsPanelClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = gedit_documents_panel_finalize;
720
	object_class->dispose = gedit_documents_panel_dispose;
Paolo Borelli's avatar
Paolo Borelli committed
721 722 723 724 725 726
	object_class->get_property = gedit_documents_panel_get_property;
	object_class->set_property = gedit_documents_panel_set_property;

	g_object_class_install_property (object_class,
					 PROP_WINDOW,
					 g_param_spec_object ("window",
727 728 729 730 731 732 733 734
							      "Window",
							      "The GeditWindow this GeditDocumentsPanel is associated with",
							      GEDIT_TYPE_WINDOW,
							      G_PARAM_READWRITE |
							      G_PARAM_CONSTRUCT_ONLY |
							      G_PARAM_STATIC_STRINGS));

	g_type_class_add_private (object_class, sizeof (GeditDocumentsPanelPrivate));
Paolo Borelli's avatar
Paolo Borelli committed
735 736 737 738 739
}

static GtkTreePath *
get_current_path (GeditDocumentsPanel *panel)
{
740 741 742 743 744
	gint notebook_num;
	gint page_num;
	GtkWidget *notebook;

	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
745

746
	notebook = _gedit_window_get_notebook (panel->priv->window);
Paolo Borelli's avatar
Paolo Borelli committed
747

748 749 750
	notebook_num = gedit_multi_notebook_get_notebook_num (panel->priv->mnb,
							      GEDIT_NOTEBOOK (notebook));
	page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
Paolo Borelli's avatar
Paolo Borelli committed
751

752
	return gtk_tree_path_new_from_indices (notebook_num, page_num, -1);
Paolo Borelli's avatar
Paolo Borelli committed
753 754 755 756 757 758 759 760 761 762 763
}

static void
menu_position (GtkMenu             *menu,
	       gint                *x,
	       gint                *y,
	       gboolean            *push_in,
	       GeditDocumentsPanel *panel)
{
	GtkTreePath *path;
	GdkRectangle rect;
764
	gint wy;
Paolo Borelli's avatar
Paolo Borelli committed
765 766
	GtkRequisition requisition;
	GtkWidget *w;
767
	GtkAllocation allocation;
Paolo Borelli's avatar
Paolo Borelli committed
768

769 770
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
771 772 773 774 775 776 777
	w = panel->priv->treeview;

	path = get_current_path (panel);
	gtk_tree_view_get_cell_area (GTK_TREE_VIEW (w),
				     path,
				     NULL,
				     &rect);
Daniel Trebbien's avatar
Daniel Trebbien committed
778
	gtk_tree_path_free (path);
Paolo Borelli's avatar
Paolo Borelli committed
779 780 781

	wy = rect.y;

782
	gdk_window_get_origin (gtk_widget_get_window (w), x, y);
783
	gtk_widget_get_preferred_size (GTK_WIDGET (menu), &requisition, NULL);
784 785
	gtk_widget_get_allocation (w, &allocation);

Paolo Borelli's avatar
Paolo Borelli committed
786 787
	if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
	{
788
		*x += allocation.x + allocation.width - requisition.width - 10;
Paolo Borelli's avatar
Paolo Borelli committed
789 790 791
	}
	else
	{
792
		*x += allocation.x + 10;
Paolo Borelli's avatar
Paolo Borelli committed
793 794 795
	}

	wy = MAX (*y + 5, *y + wy + 5);
796
	wy = MIN (wy, *y + allocation.height - requisition.height - 5);
797

Paolo Borelli's avatar
Paolo Borelli committed
798 799 800 801 802 803
	*y = wy;

	*push_in = TRUE;
}

static gboolean
804 805
show_tab_popup_menu (GeditDocumentsPanel *panel,
		     GdkEventButton      *event)
Paolo Borelli's avatar
Paolo Borelli committed
806 807 808
{
	GtkWidget *menu;

809 810
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
811
	menu = gtk_ui_manager_get_widget (gedit_window_get_ui_manager (panel->priv->window),
812
					  "/NotebookPopup");
Paolo Borelli's avatar
Paolo Borelli committed
813 814 815 816
	g_return_val_if_fail (menu != NULL, FALSE);

	if (event != NULL)
	{
817 818 819
		gtk_menu_popup (GTK_MENU (menu),
				NULL,
				NULL,
Paolo Borelli's avatar
Paolo Borelli committed
820 821
				NULL,
				NULL,
822
				event->button,
Paolo Borelli's avatar
Paolo Borelli committed
823 824 825 826
				event->time);
	}
	else
	{
827
		gtk_menu_popup (GTK_MENU (menu),
Paolo Borelli's avatar
Paolo Borelli committed
828
				NULL,
829
				NULL,
830
				(GtkMenuPositionFunc)menu_position,
Paolo Borelli's avatar
Paolo Borelli committed
831
				panel,
832
				0,
Paolo Borelli's avatar
Paolo Borelli committed
833 834 835 836 837 838 839 840 841 842 843 844 845
				gtk_get_current_event_time ());

		gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
	}

	return TRUE;
}

static gboolean
panel_button_press_event (GtkTreeView         *treeview,
			  GdkEventButton      *event,
			  GeditDocumentsPanel *panel)
{
846 847 848
	gboolean ret = FALSE;

	gedit_debug (DEBUG_PANEL);
849

850 851 852 853 854 855 856 857 858 859 860 861 862
	if (event->type == GDK_BUTTON_PRESS && event->button == 3 &&
	    event->window == gtk_tree_view_get_bin_window (treeview))
	{
		GtkTreePath *path = NULL;

		/* Change the cursor position */
		if (gtk_tree_view_get_path_at_pos (treeview,
						   event->x,
						   event->y,
						   &path,
						   NULL,
						   NULL,
						   NULL))
Paolo Borelli's avatar
Paolo Borelli committed
863
		{
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
			GtkTreeIter iter;
			gchar *path_string;

			path_string = gtk_tree_path_to_string (path);

			if (gtk_tree_model_get_iter_from_string (panel->priv->model,
								 &iter,
								 path_string))
			{
				GeditTab *tab;

				gtk_tree_model_get (panel->priv->model,
						    &iter,
						    TAB_COLUMN, &tab,
						    -1);

				if (tab != NULL)
				{
					gtk_tree_view_set_cursor (treeview,
								  path,
								  NULL,
								  FALSE);

					/* A row exists at the mouse position */
					ret = show_tab_popup_menu (panel, event);
889 890

					g_object_unref (tab);
891
				}
Paolo Borelli's avatar
Paolo Borelli committed
892
			}
893 894 895

			g_free (path_string);
			gtk_tree_path_free (path);
Paolo Borelli's avatar
Paolo Borelli committed
896 897
		}
	}
898

899
	return ret;
Paolo Borelli's avatar
Paolo Borelli committed
900 901
}

902 903 904
static gchar *
notebook_get_tooltip (GeditMultiNotebook *mnb,
		      GeditNotebook      *notebook)
Paolo Borelli's avatar
Paolo Borelli committed
905
{
906 907 908 909 910 911 912
	gchar *tooltip;
	gchar *notebook_name;
	gint num_pages;


	notebook_name = notebook_get_name (mnb, notebook);
	num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
Paolo Borelli's avatar
Paolo Borelli committed
913

914 915 916 917 918 919 920 921
	tooltip = g_markup_printf_escaped ("<b>Name:</b> %s\n\n"
					   "<b>Number of Tabs:</b> %i",
					   notebook_name,
					   num_pages);

	g_free (notebook_name);

	return tooltip;
Paolo Borelli's avatar
Paolo Borelli committed
922 923
}

924
static gboolean
925 926 927 928 929 930
treeview_query_tooltip (GtkWidget           *widget,
			gint                 x,
			gint                 y,
			gboolean             keyboard_tip,
			GtkTooltip          *tooltip,
			GeditDocumentsPanel *panel)
931 932 933 934 935
{
	GtkTreeIter iter;
	GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
	GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
	GtkTreePath *path = NULL;
936 937
	GeditNotebook *notebook;
	GeditTab *tab;
938 939
	gchar *tip;

940 941
	gedit_debug (DEBUG_PANEL);

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
	if (keyboard_tip)
	{
		gtk_tree_view_get_cursor (tree_view, &path, NULL);

		if (path == NULL)
		{
			return FALSE;
		}
	}
	else
	{
		gint bin_x, bin_y;

		gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
								   x, y,
								   &bin_x, &bin_y);
958

959 960 961 962 963 964 965 966 967 968
		if (!gtk_tree_view_get_path_at_pos (tree_view,
						    bin_x, bin_y,
						    &path,
						    NULL, NULL, NULL))
		{
			return FALSE;
		}
	}

	gtk_tree_model_get_iter (model, &iter, path);
969 970
	gtk_tree_model_get (model,
			    &iter,
971 972
			    NOTEBOOK_COLUMN, &notebook,
			    TAB_COLUMN, &tab,
973 974
			    -1);

975 976
	if (tab != NULL)
	{
977
		tip = _gedit_tab_get_tooltip (tab);
978
		g_object_unref (tab);
979 980 981 982 983 984
	}
	else
	{
		tip = notebook_get_tooltip (panel->priv->mnb, notebook);
	}

985 986
	gtk_tooltip_set_markup (tooltip, tip);

987
	g_object_unref (notebook);
988 989 990 991 992 993
	g_free (tip);
	gtk_tree_path_free (path);

	return TRUE;
}

994
/* TODO
995 996 997 998 999 1000 1001 1002
static void
treeview_row_inserted (GtkTreeModel        *tree_model,
		       GtkTreePath         *path,
		       GtkTreeIter         *iter,
		       GeditDocumentsPanel *panel)
{
	GeditTab *tab;
	gint *indeces;
1003 1004 1005 1006 1007
	gchar *path_string;
	GeditNotebook *notebook;

	gedit_debug (DEBUG_PANEL);

1008 1009
	if (panel->priv->adding_tab)
		return;
1010

1011
	panel->priv->is_reodering = TRUE;
1012 1013 1014 1015 1016

	path_string = gtk_tree_path_to_string (path);

	gedit_debug_message (DEBUG_PANEL, "New Path: %s", path_string);

1017
	g_message ("%s", path_string);
1018 1019 1020

	gtk_tree_model_get (panel->priv->model,
			    iter,
1021 1022
			    NOTEBOOK_COLUMN, &notebook,
			    TAB_COLUMN, &tab,
1023 1024
			    -1);

1025
	panel->priv->is_reodering = FALSE;
1026 1027

	g_free (path_string);
1028 1029
}
*/
1030

1031 1032 1033 1034 1035
static void
close_button_clicked (GtkCellRenderer     *cell,
                      const gchar         *path,
                      GeditDocumentsPanel *panel)
{
1036 1037 1038
	GtkTreeIter iter;
	GeditTab *tab;
	GeditNotebook *notebook;
1039

1040 1041 1042 1043 1044
	if (!gtk_tree_model_get_iter_from_string (panel->priv->model,
	                                          &iter, path))
	{
	        return;
	}
1045

1046
	gtk_tree_model_get (panel->priv->model,
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
		            &iter,
		            NOTEBOOK_COLUMN, &notebook,
		            TAB_COLUMN, &tab,
		            -1);

	if (tab == NULL)
	{
		gedit_notebook_remove_all_tabs (notebook);
	}
	else
	{
1058 1059
		gtk_container_remove (GTK_CONTAINER (notebook),
		                      GTK_WIDGET (tab));
1060 1061 1062 1063 1064 1065
		g_object_unref (tab);
	}

	g_object_unref (notebook);
}

1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
static void
pixbuf_data_func (GtkTreeViewColumn   *column,
		  GtkCellRenderer     *cell,
		  GtkTreeModel        *model,
		  GtkTreeIter         *iter,
		  GeditDocumentsPanel *panel)
{
	GeditTab *tab;

	gtk_tree_model_get (model,
			    iter,
			    TAB_COLUMN, &tab,
			    -1);

	gtk_cell_renderer_set_visible (cell, tab != NULL);
1081

1082 1083 1084 1085
	if (tab != NULL)
	{
		g_object_unref (tab);
	}
1086 1087
}

Paolo Borelli's avatar
Paolo Borelli committed
1088 1089 1090
static void
gedit_documents_panel_init (GeditDocumentsPanel *panel)
{
1091 1092 1093 1094
	GtkWidget *sw;
	GtkTreeViewColumn *column;
	GtkCellRenderer *cell;
	GtkTreeSelection *selection;
1095
	GIcon *icon;
1096 1097

	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
1098 1099

	panel->priv = GEDIT_DOCUMENTS_PANEL_GET_PRIVATE (panel);
1100

1101 1102
	panel->priv->adding_tab = FALSE;
	panel->priv->is_reodering = FALSE;
1103

1104 1105 1106
	gtk_orientable_set_orientation (GTK_ORIENTABLE (panel),
	                                GTK_ORIENTATION_VERTICAL);

Paolo Borelli's avatar
Paolo Borelli committed
1107 1108
	/* Create the scrolled window */
	sw = gtk_scrolled_window_new (NULL, NULL);
1109

Paolo Borelli's avatar
Paolo Borelli committed
1110 1111 1112
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
1113
	gtk_widget_show (sw);
Paolo Borelli's avatar
Paolo Borelli committed
1114
	gtk_box_pack_start (GTK_BOX (panel), sw, TRUE, TRUE, 0);
1115

Paolo Borelli's avatar
Paolo Borelli committed
1116
	/* Create the empty model */
1117
	panel->priv->model = GTK_TREE_MODEL (gtk_tree_store_new (N_COLUMNS,
1118 1119
								 GDK_TYPE_PIXBUF,
								 G_TYPE_STRING,
1120 1121
								 G_TYPE_OBJECT,
								 G_TYPE_OBJECT));
Paolo Borelli's avatar
Paolo Borelli committed
1122 1123 1124 1125

	/* Create the treeview */
	panel->priv->treeview = gtk_tree_view_new_with_model (panel->priv->model);
	g_object_unref (G_OBJECT (panel->priv->model));
1126
	gtk_container_add (GTK_CONTAINER (sw), panel->priv->treeview);
Paolo Borelli's avatar
Paolo Borelli committed
1127
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (panel->priv->treeview), FALSE);
1128
	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (panel->priv->treeview), FALSE); /* TODO */
1129 1130
	gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (panel->priv->treeview), FALSE);
	gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (panel->priv->treeview), 18);
1131

1132
	/* Disable search because each time the selection is changed, the
1133
	   active tab is changed which focuses the view, and thus would remove
1134 1135 1136
	   the search entry, rendering it useless */
	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (panel->priv->treeview), FALSE);

1137
	/* Disable focus so it doesn't steal focus each time from the view */
1138 1139
	gtk_widget_set_can_focus (panel->priv->treeview, FALSE);

1140
	gtk_widget_set_has_tooltip (panel->priv->treeview, TRUE);
1141

Paolo Borelli's avatar
Paolo Borelli committed
1142
	gtk_widget_show (panel->priv->treeview);
1143

Paolo Borelli's avatar
Paolo Borelli committed
1144 1145 1146 1147 1148
	column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_title (column, _("Documents"));

	cell = gtk_cell_renderer_pixbuf_new ();
	gtk_tree_view_column_pack_start (column, cell, FALSE);
1149
	gtk_tree_view_column_add_attribute (column, cell, "pixbuf", PIXBUF_COLUMN);
1150 1151 1152 1153
	gtk_tree_view_column_set_cell_data_func (column, cell,
						 (GtkTreeCellDataFunc)pixbuf_data_func,
						 panel, NULL);

Paolo Borelli's avatar
Paolo Borelli committed
1154
	cell = gtk_cell_renderer_text_new ();
1155 1156
	gtk_tree_view_column_pack_start (column, cell, TRUE);
	gtk_tree_view_column_add_attribute (column, cell, "markup", NAME_COLUMN);
Paolo Borelli's avatar
Paolo Borelli committed
1157 1158
	gtk_tree_view_append_column (GTK_TREE_VIEW (panel->priv->treeview),
				     column);
1159

1160
	cell = gedit_cell_renderer_button_new ();
1161 1162 1163 1164

	icon = g_themed_icon_new_with_default_fallbacks ("window-close-symbolic");
	g_object_set (cell, "gicon", icon, NULL);
	g_object_unref (icon);
1165 1166 1167 1168 1169 1170
	gtk_tree_view_column_pack_end (column, cell, FALSE);
	g_signal_connect (cell,
	                  "clicked",
	                  G_CALLBACK (close_button_clicked),
	                  panel);

1171
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
Paolo Borelli's avatar
Paolo Borelli committed
1172
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1173 1174 1175 1176
	panel->priv->selection_changed_handler_id = g_signal_connect (selection,
								      "changed",
								       G_CALLBACK (treeview_selection_changed),
								       panel);
1177

Paolo Borelli's avatar
Paolo Borelli committed
1178 1179 1180 1181
	g_signal_connect (panel->priv->treeview,
			  "button-press-event",
			  G_CALLBACK (panel_button_press_event),
			  panel);
1182
	g_signal_connect (panel->priv->treeview,
1183 1184
			  "query-tooltip",
			  G_CALLBACK (treeview_query_tooltip),
1185
			  panel);
1186

1187
	/*
1188
	g_signal_connect (panel->priv->model,
1189 1190
			  "row-inserted",
			  G_CALLBACK (treeview_row_inserted),
1191
			  panel);*/
Paolo Borelli's avatar
Paolo Borelli committed
1192 1193 1194 1195 1196
}

GtkWidget *
gedit_documents_panel_new (GeditWindow *window)
{
1197 1198
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
1199 1200
	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL);

1201 1202 1203
	return g_object_new (GEDIT_TYPE_DOCUMENTS_PANEL,
			     "window", window,
			     NULL);
Paolo Borelli's avatar
Paolo Borelli committed
1204
}
Garrett Regier's avatar
Garrett Regier committed
1205

1206
/* ex:set ts=8 noet: */