gedit-documents-panel.c 27 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
 *
 * 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
18
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
Paolo Borelli's avatar
Paolo Borelli committed
19
 */
20

Paolo Borelli's avatar
Paolo Borelli committed
21
/*
22 23 24
 * 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
25 26 27 28 29 30 31 32 33
 *
 * $Id$
 */

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

#include "gedit-documents-panel.h"
34
#include "gedit-debug.h"
Paolo Borelli's avatar
Paolo Borelli committed
35
#include "gedit-utils.h"
36
#include "gedit-multi-notebook.h"
37
#include "gedit-notebook.h"
38
#include "gedit-notebook-popup-menu.h"
39
#include "gedit-cell-renderer-button.h"
Paolo Borelli's avatar
Paolo Borelli committed
40 41 42 43 44

#include <glib/gi18n.h>

struct _GeditDocumentsPanelPrivate
{
45 46
	GeditWindow        *window;
	GeditMultiNotebook *mnb;
Paolo Borelli's avatar
Paolo Borelli committed
47

48 49
	GtkWidget          *treeview;
	GtkTreeModel       *model;
50

51
	guint               selection_changed_handler_id;
52 53
	guint               refresh_idle_id;

54 55 56
	guint               adding_tab : 1;
	guint               is_reodering : 1;
	guint               setting_active_notebook : 1;
Paolo Borelli's avatar
Paolo Borelli committed
57 58
};

59
G_DEFINE_TYPE_WITH_PRIVATE (GeditDocumentsPanel, gedit_documents_panel, GTK_TYPE_BOX)
Paolo Borelli's avatar
Paolo Borelli committed
60 61 62 63

enum
{
	PROP_0,
64 65 66 67 68
	PROP_WINDOW
};

enum
{
69
	PIXBUF_COLUMN = 0,
70
	NAME_COLUMN,
71
	NOTEBOOK_COLUMN,
72 73
	TAB_COLUMN,
	N_COLUMNS
Paolo Borelli's avatar
Paolo Borelli committed
74 75
};

76 77 78 79 80 81 82 83 84 85 86
#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;

87
	num = gedit_multi_notebook_get_notebook_num (mnb, notebook);
88 89 90

	return g_markup_printf_escaped ("Tab Group %i", num + 1);
}
Paolo Borelli's avatar
Paolo Borelli committed
91 92 93 94 95 96 97 98 99

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

100 101
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	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,
117
							    _("Read-Only"));
Paolo Borelli's avatar
Paolo Borelli committed
118 119 120
		}
		else
		{
121
			tab_name = g_markup_printf_escaped ("<i>%s</i>",
Paolo Borelli's avatar
Paolo Borelli committed
122 123 124 125 126 127 128 129 130
							    docname);
		}
	}
	else
	{
		if (gedit_document_get_readonly (doc))
		{
			tab_name = g_markup_printf_escaped ("%s [<i>%s</i>]",
							    docname,
131
							    _("Read-Only"));
Paolo Borelli's avatar
Paolo Borelli committed
132 133 134 135 136 137 138 139 140 141 142 143 144
		}
		else
		{
			tab_name = g_markup_escape_text (docname, -1);
		}
	}

	g_free (docname);
	g_free (name);

	return tab_name;
}

145
static gboolean
146 147 148 149
get_iter_from_tab (GeditDocumentsPanel *panel,
		   GeditNotebook       *notebook,
		   GeditTab            *tab,
		   GtkTreeIter         *tab_iter)
150 151
{
	GtkTreeIter parent;
152 153
	gboolean success;
	gboolean search_notebook;
154 155 156 157 158 159 160 161

	/* 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);

162 163
	g_assert (notebook != NULL);
	/* tab may be NULL if just the notebook is specified */
164
	g_assert (tab_iter != NULL);
165

166
	search_notebook = (gedit_multi_notebook_get_n_notebooks (panel->priv->mnb) > 1);
167

168 169
	if (!gtk_tree_model_get_iter_first (panel->priv->model, &parent))
		return FALSE;
170

171
	success = FALSE;
172 173
	do
	{
174 175
		GtkTreeIter iter;

176
		if (search_notebook)
177 178 179 180 181 182 183 184
		{
			GeditNotebook *current_notebook;

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

185
			if (current_notebook != NULL)
186
			{
187 188
				gboolean is_cur;
				is_cur = (current_notebook == notebook);
189

190 191 192
				g_object_unref (current_notebook);

				if (is_cur)
193
				{
194 195 196
					/* notebook found */
					search_notebook = FALSE;
					gtk_tree_model_iter_children (panel->priv->model, &iter, &parent);
197 198 199
				}
			}
		}
200
		else
201
		{
202
			iter = parent;
203
		}
204

205
		if (!search_notebook)
206
		{
207
			if (tab == NULL)
208
			{
209 210
				success = TRUE;
				break;
211
			}
212

213 214 215
			g_assert (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (panel->priv->model),
								&iter));

216 217
			do
			{
218
				GeditTab *current_tab;
219

220 221 222 223 224
				gtk_tree_model_get (panel->priv->model,
						    &iter,
						    TAB_COLUMN, &current_tab,
						    -1);

225
				if (current_tab != NULL)
226
				{
227 228 229 230
					gboolean is_cur;

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

232 233 234 235
					if (is_cur)
					{
						*tab_iter = iter;
						success = TRUE;
236

237 238 239
						/* break 2; */
						goto out;
					}
240 241 242 243 244 245 246 247 248 249 250 251 252
				}
			} 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;
}

253
static void
254 255
select_iter (GeditDocumentsPanel *panel,
	     GtkTreeIter         *iter)
Paolo Borelli's avatar
Paolo Borelli committed
256
{
257 258
	GtkTreeView *treeview = GTK_TREE_VIEW (panel->priv->treeview);
	GtkTreeSelection *selection;
Paolo Borelli's avatar
Paolo Borelli committed
259 260
	GtkTreePath *path;

261 262 263
	selection = gtk_tree_view_get_selection (treeview);

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

265 266 267 268 269
	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
270 271 272 273
	gtk_tree_path_free (path);
}

static void
274 275 276 277
select_active_tab (GeditDocumentsPanel *panel)
{
	GeditNotebook *notebook;
	GeditTab *tab;
278
	gboolean have_tabs;
279 280

	notebook = gedit_multi_notebook_get_active_notebook (panel->priv->mnb);
281
	have_tabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) > 0;
282
	tab = gedit_multi_notebook_get_active_tab (panel->priv->mnb);
Paolo Borelli's avatar
Paolo Borelli committed
283

284
	if (notebook != NULL && tab != NULL && have_tabs)
Paolo Borelli's avatar
Paolo Borelli committed
285 286 287
	{
		GtkTreeIter iter;

288 289
		if (get_iter_from_tab (panel, notebook, tab, &iter))
			select_iter (panel, &iter);
290 291
	}
}
Paolo Borelli's avatar
Paolo Borelli committed
292

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
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;

308 309
		if (get_iter_from_tab (panel, new_notebook, new_tab, &iter) &&
		    gtk_tree_store_iter_is_valid (GTK_TREE_STORE (panel->priv->model), &iter))
310
		{
311
			select_iter (panel, &iter);
Paolo Borelli's avatar
Paolo Borelli committed
312 313 314 315 316
		}
	}
}

static void
317 318 319
refresh_notebook (GeditDocumentsPanel *panel,
		  GeditNotebook       *notebook,
		  GtkTreeIter         *parent)
Paolo Borelli's avatar
Paolo Borelli committed
320 321 322
{
	GList *tabs;
	GList *l;
323
	GtkTreeStore *tree_store;
Paolo Borelli's avatar
Paolo Borelli committed
324 325
	GeditTab *active_tab;

326
	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
327

328
	tree_store = GTK_TREE_STORE (panel->priv->model);
Paolo Borelli's avatar
Paolo Borelli committed
329 330 331

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

332
	tabs = gtk_container_get_children (GTK_CONTAINER (notebook));
Paolo Borelli's avatar
Paolo Borelli committed
333

334 335
	for (l = tabs; l != NULL; l = g_list_next (l))
	{
Paolo Borelli's avatar
Paolo Borelli committed
336 337 338 339 340 341 342 343
		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 */
344 345
		gtk_tree_store_append (tree_store, &iter, parent);
		gtk_tree_store_set (tree_store,
Paolo Borelli's avatar
Paolo Borelli committed
346
				    &iter,
347 348
				    PIXBUF_COLUMN, pixbuf,
				    NAME_COLUMN, name,
349
				    NOTEBOOK_COLUMN, notebook,
350
				    TAB_COLUMN, l->data,
Paolo Borelli's avatar
Paolo Borelli committed
351 352 353 354
				    -1);

		g_free (name);
		if (pixbuf != NULL)
355
		{
Paolo Borelli's avatar
Paolo Borelli committed
356
			g_object_unref (pixbuf);
357
		}
Paolo Borelli's avatar
Paolo Borelli committed
358 359 360

		if (l->data == active_tab)
		{
361 362 363
			select_iter (panel, &iter);
		}
	}
Paolo Borelli's avatar
Paolo Borelli committed
364

365 366
	g_list_free (tabs);
}
Paolo Borelli's avatar
Paolo Borelli committed
367

368 369 370 371
static void
refresh_notebook_foreach (GeditNotebook       *notebook,
			  GeditDocumentsPanel *panel)
{
372
	gboolean add_notebook;
Paolo Borelli's avatar
Paolo Borelli committed
373

374 375 376
	/* 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);
377

378 379 380 381
	if (add_notebook)
	{
		GtkTreeIter iter;
		gchar *name;
382

383
		name = notebook_get_name (panel->priv->mnb, notebook);
384

385 386
		gtk_tree_store_append (GTK_TREE_STORE (panel->priv->model),
				       &iter, NULL);
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
		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);
	}
404 405
}

406 407
static gboolean
refresh_list_idle (GeditDocumentsPanel *panel)
408
{
409 410
	GtkTreeSelection *selection;

411 412
	gedit_debug (DEBUG_PANEL);

413 414 415
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
	g_signal_handler_block (selection, panel->priv->selection_changed_handler_id);

416 417 418 419 420 421
	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);
422
	panel->priv->adding_tab = FALSE;
Paolo Borelli's avatar
Paolo Borelli committed
423

424 425 426
	gtk_tree_view_expand_all (GTK_TREE_VIEW (panel->priv->treeview));

	select_active_tab (panel);
427 428 429

	panel->priv->refresh_idle_id = 0;

430 431
	g_signal_handler_unblock (selection, panel->priv->selection_changed_handler_id);

432 433 434 435 436 437 438 439 440 441 442 443 444
	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);
	}
445 446 447 448 449 450 451 452 453 454 455 456 457
}

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
458 459 460 461 462 463 464 465 466
}

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

467 468
	gedit_debug (DEBUG_PANEL);

469 470 471 472 473 474 475
	if (get_iter_from_tab (panel,
			       gedit_multi_notebook_get_active_notebook (panel->priv->mnb),
			       tab,
			       &iter))
	{
		gchar *name;
		GdkPixbuf *pixbuf;
476

477 478
		name = tab_get_name (tab);
		pixbuf = _gedit_tab_get_icon (tab);
Paolo Borelli's avatar
Paolo Borelli committed
479

480 481 482 483 484
		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
485

486 487 488 489 490
		g_free (name);
		if (pixbuf != NULL)
		{
			g_object_unref (pixbuf);
		}
491
	}
Paolo Borelli's avatar
Paolo Borelli committed
492 493 494
}

static void
495 496 497 498
multi_notebook_tab_removed (GeditMultiNotebook  *mnb,
			    GeditNotebook       *notebook,
			    GeditTab            *tab,
			    GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
499
{
500 501 502 503 504 505
	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
506
	g_signal_handlers_disconnect_by_func (tab,
507 508
					      G_CALLBACK (sync_name_and_icon),
					      panel);
Paolo Borelli's avatar
Paolo Borelli committed
509

510
	refresh_list (panel);
Paolo Borelli's avatar
Paolo Borelli committed
511 512 513
}

static void
514 515 516 517
multi_notebook_tab_added (GeditMultiNotebook  *mnb,
			  GeditNotebook       *notebook,
			  GeditTab            *tab,
			  GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
518
{
519 520
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
521 522 523 524
	g_signal_connect (tab,
			 "notify::name",
			  G_CALLBACK (sync_name_and_icon),
			  panel);
525
	g_signal_connect (tab,
Paolo Borelli's avatar
Paolo Borelli committed
526 527 528 529
			 "notify::state",
			  G_CALLBACK (sync_name_and_icon),
			  panel);

530
	refresh_list (panel);
531 532 533 534 535 536 537
}

static void
multi_notebook_notebook_removed (GeditMultiNotebook  *mnb,
				 GeditNotebook       *notebook,
				 GeditDocumentsPanel *panel)
{
538
	refresh_list (panel);
539 540
}

Paolo Borelli's avatar
Paolo Borelli committed
541
static void
542 543 544 545 546
multi_notebook_tabs_reordered (GeditMultiNotebook  *mnb,
                               GeditNotebook       *notebook,
                               GtkWidget           *page,
                               gint                 page_num,
                               GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
547
{
548 549
	gedit_debug (DEBUG_PANEL);

550 551
	if (panel->priv->is_reodering)
		return;
552

Paolo Borelli's avatar
Paolo Borelli committed
553
	refresh_list (panel);
554
}
Paolo Borelli's avatar
Paolo Borelli committed
555 556 557 558 559

static void
set_window (GeditDocumentsPanel *panel,
	    GeditWindow         *window)
{
560 561
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
562 563 564
	g_return_if_fail (panel->priv->window == NULL);
	g_return_if_fail (GEDIT_IS_WINDOW (window));

565
	panel->priv->window = g_object_ref (window);
566
	panel->priv->mnb = GEDIT_MULTI_NOTEBOOK (_gedit_window_get_multi_notebook (window));
Paolo Borelli's avatar
Paolo Borelli committed
567

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

	refresh_list (panel);
Paolo Borelli's avatar
Paolo Borelli committed
590 591 592
}

static void
593 594
treeview_selection_changed (GtkTreeSelection    *selection,
			    GeditDocumentsPanel *panel)
Paolo Borelli's avatar
Paolo Borelli committed
595 596 597
{
	GtkTreeIter iter;

598 599
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
600 601
	if (gtk_tree_selection_get_selected (selection, NULL, &iter))
	{
602 603 604
		GeditNotebook *notebook;
		GeditTab *tab;

605 606
		gtk_tree_model_get (panel->priv->model,
				    &iter,
607 608
				    NOTEBOOK_COLUMN, &notebook,
				    TAB_COLUMN, &tab,
Paolo Borelli's avatar
Paolo Borelli committed
609 610
				    -1);

611
		if (tab != NULL)
Paolo Borelli's avatar
Paolo Borelli committed
612
		{
613 614
			gedit_multi_notebook_set_active_tab (panel->priv->mnb,
							     tab);
615 616
			if (notebook != NULL)
				g_object_unref (notebook);
617
			g_object_unref (tab);
618
		}
619
		else if (notebook != NULL)
620 621 622 623 624 625 626 627 628 629 630 631 632
		{
			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);
			}
633 634

			g_object_unref (notebook);
635 636
		}
	}
Paolo Borelli's avatar
Paolo Borelli committed
637 638 639 640
}

static void
gedit_documents_panel_set_property (GObject      *object,
641 642 643
				    guint         prop_id,
				    const GValue *value,
				    GParamSpec   *pspec)
Paolo Borelli's avatar
Paolo Borelli committed
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
{
	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:
670
			g_value_set_object (value, panel->priv->window);
Paolo Borelli's avatar
Paolo Borelli committed
671
			break;
672

Paolo Borelli's avatar
Paolo Borelli committed
673 674
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
675
			break;
Paolo Borelli's avatar
Paolo Borelli committed
676 677 678 679 680 681 682
	}
}

static void
gedit_documents_panel_finalize (GObject *object)
{
	/* GeditDocumentsPanel *tab = GEDIT_DOCUMENTS_PANEL (object); */
683 684 685 686

	/* TODO disconnect signal with window */

	gedit_debug (DEBUG_PANEL);
Paolo Borelli's avatar
Paolo Borelli committed
687 688 689 690

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

691 692 693 694 695
static void
gedit_documents_panel_dispose (GObject *object)
{
	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object);

696 697
	gedit_debug (DEBUG_PANEL);

698 699 700 701 702 703
	if (panel->priv->refresh_idle_id != 0)
	{
		g_source_remove (panel->priv->refresh_idle_id);
		panel->priv->refresh_idle_id = 0;
	}

704
	g_clear_object (&panel->priv->window);
705 706 707 708

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

709
static void
Paolo Borelli's avatar
Paolo Borelli committed
710 711 712 713 714
gedit_documents_panel_class_init (GeditDocumentsPanelClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = gedit_documents_panel_finalize;
715
	object_class->dispose = gedit_documents_panel_dispose;
Paolo Borelli's avatar
Paolo Borelli committed
716 717 718 719 720 721
	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",
722 723 724 725 726 727
							      "Window",
							      "The GeditWindow this GeditDocumentsPanel is associated with",
							      GEDIT_TYPE_WINDOW,
							      G_PARAM_READWRITE |
							      G_PARAM_CONSTRUCT_ONLY |
							      G_PARAM_STATIC_STRINGS));
Paolo Borelli's avatar
Paolo Borelli committed
728 729 730 731 732
}

static GtkTreePath *
get_current_path (GeditDocumentsPanel *panel)
{
733 734 735 736 737
	gint notebook_num;
	gint page_num;
	GtkWidget *notebook;

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

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

741 742 743
	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
744

745
	return gtk_tree_path_new_from_indices (notebook_num, page_num, -1);
Paolo Borelli's avatar
Paolo Borelli committed
746 747 748 749 750 751 752 753 754 755 756
}

static void
menu_position (GtkMenu             *menu,
	       gint                *x,
	       gint                *y,
	       gboolean            *push_in,
	       GeditDocumentsPanel *panel)
{
	GtkTreePath *path;
	GdkRectangle rect;
757
	gint wy;
Paolo Borelli's avatar
Paolo Borelli committed
758 759
	GtkRequisition requisition;
	GtkWidget *w;
760
	GtkAllocation allocation;
Paolo Borelli's avatar
Paolo Borelli committed
761

762 763
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
764 765 766 767 768 769 770
	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
771
	gtk_tree_path_free (path);
Paolo Borelli's avatar
Paolo Borelli committed
772 773 774

	wy = rect.y;

775
	gdk_window_get_origin (gtk_widget_get_window (w), x, y);
776
	gtk_widget_get_preferred_size (GTK_WIDGET (menu), &requisition, NULL);
777 778
	gtk_widget_get_allocation (w, &allocation);

Paolo Borelli's avatar
Paolo Borelli committed
779 780
	if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
	{
781
		*x += allocation.x + allocation.width - requisition.width - 10;
Paolo Borelli's avatar
Paolo Borelli committed
782 783 784
	}
	else
	{
785
		*x += allocation.x + 10;
Paolo Borelli's avatar
Paolo Borelli committed
786 787 788
	}

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

Paolo Borelli's avatar
Paolo Borelli committed
791 792 793 794 795 796
	*y = wy;

	*push_in = TRUE;
}

static gboolean
797
show_tab_popup_menu (GeditDocumentsPanel *panel,
798
		     GeditTab            *tab,
799
		     GdkEventButton      *event)
Paolo Borelli's avatar
Paolo Borelli committed
800 801 802
{
	GtkWidget *menu;

803 804
	gedit_debug (DEBUG_PANEL);

805
	menu = gedit_notebook_popup_menu_new (panel->priv->window, tab);
Paolo Borelli's avatar
Paolo Borelli committed
806 807 808

	if (event != NULL)
	{
809 810 811
		gtk_menu_popup (GTK_MENU (menu),
				NULL,
				NULL,
Paolo Borelli's avatar
Paolo Borelli committed
812 813
				NULL,
				NULL,
814
				event->button,
Paolo Borelli's avatar
Paolo Borelli committed
815 816 817 818
				event->time);
	}
	else
	{
819
		gtk_menu_popup (GTK_MENU (menu),
Paolo Borelli's avatar
Paolo Borelli committed
820
				NULL,
821
				NULL,
822
				(GtkMenuPositionFunc)menu_position,
Paolo Borelli's avatar
Paolo Borelli committed
823
				panel,
824
				0,
Paolo Borelli's avatar
Paolo Borelli committed
825 826 827 828 829 830 831 832 833 834 835 836 837
				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)
{
838 839 840
	gboolean ret = FALSE;

	gedit_debug (DEBUG_PANEL);
841

842 843 844
	if ((event->type == GDK_BUTTON_PRESS) &&
	    (gdk_event_triggers_context_menu ((GdkEvent *) event)) &&
	    (event->window == gtk_tree_view_get_bin_window (treeview)))
845 846 847 848 849 850 851 852 853 854 855
	{
		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
856
		{
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
			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 */
881
					ret = show_tab_popup_menu (panel, tab, event);
882 883

					g_object_unref (tab);
884
				}
Paolo Borelli's avatar
Paolo Borelli committed
885
			}
886 887 888

			g_free (path_string);
			gtk_tree_path_free (path);
Paolo Borelli's avatar
Paolo Borelli committed
889 890
		}
	}
891

892
	return ret;
Paolo Borelli's avatar
Paolo Borelli committed
893 894
}

895 896 897
static gchar *
notebook_get_tooltip (GeditMultiNotebook *mnb,
		      GeditNotebook      *notebook)
Paolo Borelli's avatar
Paolo Borelli committed
898
{
899 900 901 902 903 904 905
	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
906

907 908 909 910 911 912 913 914
	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
915 916
}

917
static gboolean
918 919 920 921 922 923
treeview_query_tooltip (GtkWidget           *widget,
			gint                 x,
			gint                 y,
			gboolean             keyboard_tip,
			GtkTooltip          *tooltip,
			GeditDocumentsPanel *panel)
924 925 926 927 928
{
	GtkTreeIter iter;
	GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
	GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
	GtkTreePath *path = NULL;
929 930
	GeditNotebook *notebook;
	GeditTab *tab;
931 932
	gchar *tip;

933 934
	gedit_debug (DEBUG_PANEL);

935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
	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);
951

952 953 954 955 956 957 958 959 960 961
		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);
962 963
	gtk_tree_model_get (model,
			    &iter,
964 965
			    NOTEBOOK_COLUMN, &notebook,
			    TAB_COLUMN, &tab,
966 967
			    -1);

968 969
	if (tab != NULL)
	{
970
		tip = _gedit_tab_get_tooltip (tab);
971
		g_object_unref (tab);
972 973 974 975 976 977
	}
	else
	{
		tip = notebook_get_tooltip (panel->priv->mnb, notebook);
	}

978 979
	gtk_tooltip_set_markup (tooltip, tip);

980
	g_object_unref (notebook);
981 982 983 984 985 986
	g_free (tip);
	gtk_tree_path_free (path);

	return TRUE;
}

987
/* TODO
988 989 990 991 992 993 994 995
static void
treeview_row_inserted (GtkTreeModel        *tree_model,
		       GtkTreePath         *path,
		       GtkTreeIter         *iter,
		       GeditDocumentsPanel *panel)
{
	GeditTab *tab;
	gint *indeces;
996 997 998 999 1000
	gchar *path_string;
	GeditNotebook *notebook;

	gedit_debug (DEBUG_PANEL);

1001 1002
	if (panel->priv->adding_tab)
		return;
1003

1004
	panel->priv->is_reodering = TRUE;
1005 1006 1007 1008 1009

	path_string = gtk_tree_path_to_string (path);

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

1010
	g_message ("%s", path_string);
1011 1012 1013

	gtk_tree_model_get (panel->priv->model,
			    iter,
1014 1015
			    NOTEBOOK_COLUMN, &notebook,
			    TAB_COLUMN, &tab,
1016 1017
			    -1);

1018
	panel->priv->is_reodering = FALSE;
1019 1020

	g_free (path_string);
1021 1022
}
*/
1023

1024 1025 1026 1027 1028
static void
close_button_clicked (GtkCellRenderer     *cell,
                      const gchar         *path,
                      GeditDocumentsPanel *panel)
{
1029 1030 1031
	GtkTreeIter iter;
	GeditTab *tab;
	GeditNotebook *notebook;
1032

1033 1034 1035 1036 1037
	if (!gtk_tree_model_get_iter_from_string (panel->priv->model,
	                                          &iter, path))
	{
	        return;
	}
1038

1039
	gtk_tree_model_get (panel->priv->model,
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
		            &iter,
		            NOTEBOOK_COLUMN, &notebook,
		            TAB_COLUMN, &tab,
		            -1);

	if (tab == NULL)
	{
		gedit_notebook_remove_all_tabs (notebook);
	}
	else
	{
1051 1052
		gtk_container_remove (GTK_CONTAINER (notebook),
		                      GTK_WIDGET (tab));
1053 1054 1055 1056 1057 1058
		g_object_unref (tab);
	}

	g_object_unref (notebook);
}

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
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);
1074

1075 1076 1077 1078
	if (tab != NULL)
	{
		g_object_unref (tab);
	}
1079 1080
}

Paolo Borelli's avatar
Paolo Borelli committed
1081 1082 1083
static void
gedit_documents_panel_init (GeditDocumentsPanel *panel)
{
1084 1085 1086 1087
	GtkWidget *sw;
	GtkTreeViewColumn *column;
	GtkCellRenderer *cell;
	GtkTreeSelection *selection;
1088
	GIcon *icon;
1089 1090

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

1092
	panel->priv = gedit_documents_panel_get_instance_private (panel);
1093

1094 1095
	panel->priv->adding_tab = FALSE;
	panel->priv->is_reodering = FALSE;
1096

1097 1098 1099
	gtk_orientable_set_orientation (GTK_ORIENTABLE (panel),
	                                GTK_ORIENTATION_VERTICAL);

Paolo Borelli's avatar
Paolo Borelli committed
1100 1101
	/* Create the scrolled window */
	sw = gtk_scrolled_window_new (NULL, NULL);
1102

Paolo Borelli's avatar
Paolo Borelli committed
1103 1104 1105
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
1106
	gtk_widget_show (sw);
Paolo Borelli's avatar
Paolo Borelli committed
1107
	gtk_box_pack_start (GTK_BOX (panel), sw, TRUE, TRUE, 0);
1108

Paolo Borelli's avatar
Paolo Borelli committed
1109
	/* Create the empty model */
1110
	panel->priv->model = GTK_TREE_MODEL (gtk_tree_store_new (N_COLUMNS,
1111 1112
								 GDK_TYPE_PIXBUF,
								 G_TYPE_STRING,
1113 1114
								 G_TYPE_OBJECT,
								 G_TYPE_OBJECT));
Paolo Borelli's avatar
Paolo Borelli committed
1115 1116 1117 1118

	/* Create the treeview */
	panel->priv->treeview = gtk_tree_view_new_with_model (panel->priv->model);
	g_object_unref (G_OBJECT (panel->priv->model));
1119
	gtk_container_add (GTK_CONTAINER (sw), panel->priv->treeview);
Paolo Borelli's avatar
Paolo Borelli committed
1120
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (panel->priv->treeview), FALSE);
1121
	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (panel->priv->treeview), FALSE); /* TODO */
1122 1123
	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);
1124

1125
	/* Disable search because each time the selection is changed, the
1126
	   active tab is changed which focuses the view, and thus would remove
1127 1128 1129
	   the search entry, rendering it useless */
	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (panel->priv->treeview), FALSE);

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

1133
	gtk_widget_set_has_tooltip (panel->priv->treeview, TRUE);
1134

Paolo Borelli's avatar
Paolo Borelli committed
1135
	gtk_widget_show (panel->priv->treeview);
1136

Paolo Borelli's avatar
Paolo Borelli committed
1137 1138 1139 1140 1141
	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);
1142
	gtk_tree_view_column_add_attribute (column, cell, "pixbuf", PIXBUF_COLUMN);
1143 1144 1145 1146
	gtk_tree_view_column_set_cell_data_func (column, cell,
						 (GtkTreeCellDataFunc)pixbuf_data_func,
						 panel, NULL);

Paolo Borelli's avatar
Paolo Borelli committed
1147
	cell = gtk_cell_renderer_text_new ();
1148 1149
	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
1150 1151
	gtk_tree_view_append_column (GTK_TREE_VIEW (panel->priv->treeview),
				     column);
1152

1153
	cell = gedit_cell_renderer_button_new ();
1154 1155 1156 1157

	icon = g_themed_icon_new_with_default_fallbacks ("window-close-symbolic");
	g_object_set (cell, "gicon", icon, NULL);
	g_object_unref (icon);
1158 1159 1160 1161 1162 1163
	gtk_tree_view_column_pack_end (column, cell, FALSE);
	g_signal_connect (cell,
	                  "clicked",
	                  G_CALLBACK (close_button_clicked),
	                  panel);

1164
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
Paolo Borelli's avatar
Paolo Borelli committed
1165
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1166 1167 1168 1169
	panel->priv->selection_changed_handler_id = g_signal_connect (selection,
								      "changed",
								       G_CALLBACK (treeview_selection_changed),
								       panel);
1170

Paolo Borelli's avatar
Paolo Borelli committed
1171 1172 1173 1174
	g_signal_connect (panel->priv->treeview,
			  "button-press-event",
			  G_CALLBACK (panel_button_press_event),
			  panel);
1175
	g_signal_connect (panel->priv->treeview,
1176 1177
			  "query-tooltip",
			  G_CALLBACK (treeview_query_tooltip),
1178
			  panel);
1179

1180
	/*
1181
	g_signal_connect (panel->priv->model,
1182 1183
			  "row-inserted",
			  G_CALLBACK (treeview_row_inserted),
1184
			  panel);*/
Paolo Borelli's avatar
Paolo Borelli committed
1185 1186 1187 1188 1189
}

GtkWidget *
gedit_documents_panel_new (GeditWindow *window)
{
1190 1191
	gedit_debug (DEBUG_PANEL);

Paolo Borelli's avatar
Paolo Borelli committed
1192 1193
	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL);

1194 1195 1196
	return g_object_new (GEDIT_TYPE_DOCUMENTS_PANEL,
			     "window", window,
			     NULL);
Paolo Borelli's avatar
Paolo Borelli committed
1197
}
Garrett Regier's avatar
Garrett Regier committed
1198

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