Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ab04d4d878bb2d70555480ff884d1fea89a76bbe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
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
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
/*******************************************************************************
 * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
 * This program and the accompanying materials are made available under the terms
 * of the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tcf.te.ui.terminals.tabs;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
import org.eclipse.tcf.te.ui.terminals.nls.Messages;
import org.eclipse.tm.internal.terminal.control.ITerminalListener;
import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
import org.eclipse.tm.internal.terminal.control.TerminalViewControlFactory;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
import org.eclipse.ui.PlatformUI;

/**
 * Terminals tab folder manager.
 */
@SuppressWarnings({ "restriction" })
public class TabFolderManager extends PlatformObject implements ISelectionProvider {
	// Reference to the parent terminal consoles view
	private final ITerminalsView parentView;
	// Reference to the selection listener instance
	private final SelectionListener selectionListener;

	/**
	 * List of selection changed listeners.
	 */
	private final List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>();

	/**
	 * Map of tab command input field handler per tab item
	 */
	private final Map<CTabItem, TabCommandFieldHandler> commandFieldHandler = new HashMap<CTabItem, TabCommandFieldHandler>();

	/**
	 * The terminal control selection listener implementation.
	 */
	private class TerminalControlSelectionListener implements DisposeListener, MouseListener {
		private final ITerminalViewControl terminal;
		private boolean selectMode;

		/**
		 * Constructor.
		 *
		 * @param terminal The terminal control. Must not be <code>null</code>.
		 */
		public TerminalControlSelectionListener(ITerminalViewControl terminal) {
			Assert.isNotNull(terminal);
			this.terminal = terminal;

			// Register ourself as the required listener
			terminal.getControl().addDisposeListener(this);
			terminal.getControl().addMouseListener(this);
		}

		/**
		 * Returns the associated terminal view control.
		 *
		 * @return The terminal view control.
		 */
		protected final ITerminalViewControl getTerminal() {
			return terminal;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
		 */
		@Override
		public void widgetDisposed(DisposeEvent e) {
			// Widget got disposed, check if it is ours
			// If a tab item gets disposed, we have to dispose the terminal as well
			if (e.getSource().equals(terminal.getControl())) {
				// Remove as listener
				getTerminal().getControl().removeDisposeListener(this);
				getTerminal().getControl().removeMouseListener(this);
			}
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
		 */
		@Override
		public void mouseDown(MouseEvent e) {
			// Left button down -> select mode starts
			if (e.button == 1) selectMode = true;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
		 */
		@Override
		public void mouseUp(MouseEvent e) {
			if (e.button == 1 && selectMode) {
				selectMode = false;
				// Fire a selection changed event with the terminal controls selection
		        try {
		            Display display = PlatformUI.getWorkbench().getDisplay();
		            display.asyncExec(new Runnable() {
						@Override
						public void run() {
							fireSelectionChanged(new StructuredSelection(getTerminal().getSelection()));
						}
					});
		        }
		        catch (Exception ex) {
		            // if display is disposed, silently ignore.
		        }
			}
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
		 */
		@Override
		public void mouseDoubleClick(MouseEvent e) {
		}
	}

	/**
	 * Constructor.
	 *
	 * @param parentView The parent terminals console view. Must not be <code>null</code>.
	 */
	public TabFolderManager(ITerminalsView parentView) {
		super();
		Assert.isNotNull(parentView);
		this.parentView = parentView;

		// Attach a selection listener to the tab folder
		selectionListener = doCreateTabFolderSelectionListener(this);
		if (getTabFolder() != null) getTabFolder().addSelectionListener(selectionListener);
	}

	/**
	 * Creates the terminal console tab folder selection listener instance.
	 *
	 * @param parent The parent terminal console tab folder manager. Must not be <code>null</code>.
	 * @return The selection listener instance.
	 */
	protected TabFolderSelectionListener doCreateTabFolderSelectionListener(TabFolderManager parent) {
		Assert.isNotNull(parent);
		return new TabFolderSelectionListener(parent);
	}

	/**
	 * Returns the parent terminal consoles view.
	 *
	 * @return The terminal consoles view instance.
	 */
	protected final ITerminalsView getParentView() {
		return parentView;
	}

	/**
	 * Returns the tab folder associated with the parent view.
	 *
	 * @return The tab folder or <code>null</code>.
	 */
	protected final CTabFolder getTabFolder() {
		return (CTabFolder)getParentView().getAdapter(CTabFolder.class);
	}

	/**
	 * Returns the selection changed listeners currently registered.
	 *
	 * @return The registered selection changed listeners or an empty array.
	 */
	protected final ISelectionChangedListener[] getSelectionChangedListeners() {
		return selectionChangedListeners.toArray(new ISelectionChangedListener[selectionChangedListeners.size()]);
	}

	/**
	 * Dispose the tab folder manager instance.
	 */
	public void dispose() {
		// Dispose the selection listener
		if (getTabFolder() != null && !getTabFolder().isDisposed()) getTabFolder().removeSelectionListener(selectionListener);
		// Dispose the tab command field handler
		for (TabCommandFieldHandler handler : commandFieldHandler.values()) {
			handler.dispose();
		}
		commandFieldHandler.clear();
	}

	/**
	 * Creates a new tab item with the given title and connector.
	 *
	 * @param title The tab title. Must not be <code>null</code>.
	 * @param encoding The terminal encoding or <code>null</code>.
	 * @param connector The terminal connector. Must not be <code>null</code>.
	 * @param data The custom terminal data node or <code>null</code>.
	 * @param flags The flags controlling how the console is opened or <code>null</code> to use defaults.
	 *
	 * @return The created tab item or <code>null</code> if failed.
	 */
	@SuppressWarnings({ "unused", "unchecked" })
	public CTabItem createTabItem(String title, String encoding, ITerminalConnector connector, Object data, Map<String, Boolean> flags) {
		Assert.isNotNull(title);
		Assert.isNotNull(connector);

		// The result tab item
		CTabItem item = null;

		// Get the tab folder from the parent viewer
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder != null) {
			// Generate a unique title string for the new tab item (must be called before creating the item itself)
			title = makeUniqueTitle(title, tabFolder);
			// Create the tab item
			item = new CTabItem(tabFolder, SWT.CLOSE);
			// Set the tab item title
			item.setText(title);
			// Set the tab icon
			Image image = getTabItemImage(connector, data);
			if (image != null) item.setImage(image);

			// Setup the tab item listeners
			setupTerminalTabListeners(item);

			// Create the composite to create the terminal control within
			Composite composite = new Composite(tabFolder, SWT.NONE);
			composite.setLayout(new FillLayout());
			// Associate the composite with the tab item
			item.setControl(composite);

			// Refresh the layout
			tabFolder.getParent().layout(true);

			// Create the terminal control
			ITerminalViewControl terminal = TerminalViewControlFactory.makeControl(doCreateTerminalTabTerminalListener(this, item), composite, new ITerminalConnector[] { connector }, true);
			if (terminal instanceof ITerminalControl && data instanceof Map<?,?>) {
				Object value = ((Map<String, Object>)data).get(ITerminalsConnectorConstants.PROP_DATA_NO_RECONNECT);
				boolean noReconnect = value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
				((ITerminalControl)terminal).setConnectOnEnterIfClosed(!noReconnect);
			}

			// Add middle mouse button paste support
			addMiddleMouseButtonPasteSupport(terminal);
			// Add the "selection" listener to the terminal control
			new TerminalControlSelectionListener(terminal);
			// Configure the terminal encoding
			try { terminal.setEncoding(encoding); } catch (UnsupportedEncodingException e) { /* ignored on purpose */ }
			// Associated the terminal with the tab item
			item.setData(terminal);
			// Associated the custom data node with the tab item (if any)
			if (data != null) item.setData("customData", data); //$NON-NLS-1$

			// Overwrite the text canvas help id
			String contextHelpId = getParentView().getContextHelpId();
			if (contextHelpId != null) {
				PlatformUI.getWorkbench().getHelpSystem().setHelp(terminal.getControl(), contextHelpId);
			}

			// Set the context menu
			TabFolderMenuHandler menuHandler = (TabFolderMenuHandler)getParentView().getAdapter(TabFolderMenuHandler.class);
			if (menuHandler != null) {
				Menu menu = (Menu)menuHandler.getAdapter(Menu.class);
				if (menu != null) {
					// One weird occurrence of IllegalArgumentException: Widget has wrong parent.
					// Inspecting the code, this seem extremely unlikely. The terminal is created
					// from a composite parent, the composite parent from the tab folder and the menu
					// from the tab folder. Means, at the end all should have the same menu shell, shouldn't they?
					try {
						terminal.getControl().setMenu(menu);
					} catch (IllegalArgumentException e) {
						// Log exception only if debug mode is set to 1.
						if (UIPlugin.getTraceHandler().isSlotEnabled(1, null)) {
							e.printStackTrace();
						}
					}
				}
			}

			// Select the created item within the tab folder
			tabFolder.setSelection(item);

			// Set the connector
			terminal.setConnector(connector);

			// And connect the terminal
			terminal.connectTerminal();

			// Fire selection changed event
			fireSelectionChanged();
		}

		// Return the create tab item finally.
		return item;
	}

	/**
 	 * Used for DnD of terminal tab items between terminal views
 	 * <p>
	 * Create a new tab item in the "dropped" terminal view using the
	 * information stored in the given item.
	 *
	 * @param oldItem The old dragged tab item. Must not be <code>null</code>.
	 * @return The new dropped tab item.
	 */
	@SuppressWarnings("unchecked")
    public CTabItem cloneTabItemAfterDrop(CTabItem oldItem) {
		Assert.isNotNull(oldItem);

		ITerminalViewControl terminal = (ITerminalViewControl)oldItem.getData();
		ITerminalConnector connector = terminal.getTerminalConnector();
		Object data = oldItem.getData("customData"); //$NON-NLS-1$
		Map<String, Object> properties = (Map<String, Object>)oldItem.getData("properties"); //$NON-NLS-1$
		String title = oldItem.getText();

		// The result tab item
		CTabItem item = null;

		// Get the tab folder from the parent viewer
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder != null) {
			// Generate a unique title string for the new tab item (must be called before creating the item itself)
			title = makeUniqueTitle(title, tabFolder);
			// Create the tab item
			item = new CTabItem(tabFolder, SWT.CLOSE);
			// Set the tab item title
			item.setText(title);
			// Set the tab icon
			Image image = getTabItemImage(connector, data);
			if (image != null) item.setImage(image);

			// Setup the tab item listeners
			setupTerminalTabListeners(item);
			// Move the terminal listener to the new item
			TabTerminalListener.move(oldItem, item);

			// Create the composite to create the terminal control within
			Composite composite = new Composite(tabFolder, SWT.NONE);
			composite.setLayout(new FillLayout());
			// Associate the composite with the tab item
			item.setControl(composite);

			// Refresh the layout
			tabFolder.getParent().layout(true);

			// Remember terminal state
			TerminalState oldState = terminal.getState();

			// Keep the context menu from being disposed
			terminal.getControl().setMenu(null);

			// change the "parent".
			Assert.isTrue(terminal instanceof ITerminalControl);
			((ITerminalControl)terminal).setupTerminal(composite);

			// Add middle mouse button paste support
			addMiddleMouseButtonPasteSupport(terminal);

			item.setData(terminal);

			// Associate the custom data node with the tab item (if any)
			if (data != null) item.setData("customData", data); //$NON-NLS-1$
			// Associate the properties with the tab item (if any)
			if (properties != null) item.setData("properties", properties); //$NON-NLS-1$

			// Overwrite the text canvas help id
			String contextHelpId = getParentView().getContextHelpId();
			if (contextHelpId != null) {
				PlatformUI.getWorkbench().getHelpSystem().setHelp(terminal.getControl(), contextHelpId);
			}

			// Set the context menu
			TabFolderMenuHandler menuHandler = (TabFolderMenuHandler)getParentView().getAdapter(TabFolderMenuHandler.class);
			if (menuHandler != null) {
				Menu menu = (Menu)menuHandler.getAdapter(Menu.class);
				if (menu != null) {
					// One weird occurrence of IllegalArgumentException: Widget has wrong parent.
					// Inspecting the code, this seem extremely unlikely. The terminal is created
					// from a composite parent, the composite parent from the tab folder and the menu
					// from the tab folder. Means, at the end all should have the same menu shell, shouldn't they?
					try {
						terminal.getControl().setMenu(menu);
					} catch (IllegalArgumentException e) {
						// Log exception only if debug mode is set to 1.
						if (UIPlugin.getTraceHandler().isSlotEnabled(1, null)) {
							e.printStackTrace();
						}
					}
				}
			}

			// Select the created item within the tab folder
			tabFolder.setSelection(item);

			// Set the connector
			terminal.setConnector(connector);

			// needed to get the focus and cursor
			Assert.isTrue(terminal instanceof ITerminalControl);
			((ITerminalControl)terminal).setState(oldState);

			// Fire selection changed event
			fireSelectionChanged();
		}

		// Return the create tab item finally.
		return item;
	}


	protected void addMiddleMouseButtonPasteSupport(final ITerminalViewControl terminal) {
		terminal.getControl().addMouseListener(new MouseAdapter(){
			@Override
            public void mouseDown(MouseEvent e) {
				// paste when the middle button is clicked
				if (e.button == 2) {
					Clipboard clipboard = terminal.getClipboard();
					if (clipboard.isDisposed()) return;
					int clipboardType = DND.SELECTION_CLIPBOARD;
					if (clipboard.getAvailableTypes(clipboardType).length == 0)
						// use normal clipboard if selection clipboard is not available
						clipboardType = DND.CLIPBOARD;
					String text = (String) clipboard.getContents(TextTransfer.getInstance(), clipboardType);
					if (text != null && text.length() > 0)
						terminal.pasteString(text);
				}
			}
		});
    }

	/**
	 * Generate a unique title string based on the given proposal.
	 *
	 * @param proposal The proposal. Must not be <code>null</code>.
	 * @return The unique title string.
	 */
	protected String makeUniqueTitle(String proposal, CTabFolder tabFolder) {
		Assert.isNotNull(proposal);
		Assert.isNotNull(tabFolder);

		String title = proposal;
		int index = 0;

		// Loop all existing tab items and check the titles. We have to remember
		// all found titles as modifying the proposal might in turn conflict again
		// with the title of a tab already checked.
		List<String> titles = new ArrayList<String>();
		for (CTabItem item : tabFolder.getItems()) {
			// Get the tab item title
			titles.add(item.getText());
		}
		// Make the proposal unique be appending (<n>) against all known titles.
		while (titles.contains(title)) title = proposal + " (" + ++index + ")"; //$NON-NLS-1$ //$NON-NLS-2$

		return title;
	}

	/**
	 * Setup the terminal console tab item listeners.
	 *
	 * @param item The tab item. Must not be <code>null</code>.
	 */
	protected void setupTerminalTabListeners(final CTabItem item) {
		Assert.isNotNull(item);

		// Create and associate the disposal listener
		DisposeListener disposeListener = doCreateTerminalTabDisposeListener(this);

		// store the listener to make access easier e.g. needed in DnD
		item.setData("disposeListener", disposeListener); //$NON-NLS-1$
		item.addDisposeListener(disposeListener);
	}

	/**
	 * Creates a new terminal console tab terminal listener instance.
	 *
	 * @param tabFolderManager The tab folder manager. Must not be <code>null</code>.
	 * @param item The tab item. Must not be <code>null</code>.
	 *
	 * @return The terminal listener instance.
	 */
	protected ITerminalListener doCreateTerminalTabTerminalListener(TabFolderManager tabFolderManager, CTabItem item) {
		Assert.isNotNull(item);
		return new TabTerminalListener(tabFolderManager, item);
	}

	/**
	 * Creates a new terminal console tab dispose listener instance.
	 *
	 * @param parent The parent terminal console tab folder manager. Must not be <code>null</code>.
	 * @return The dispose listener instance.
	 */
	protected DisposeListener doCreateTerminalTabDisposeListener(TabFolderManager parent) {
		Assert.isNotNull(parent);
		return new TabDisposeListener(parent);
	}

	/**
	 * Returns the tab item image.
	 *
	 * @param connector The terminal connector. Must not be <code>null</code>.
	 * @param data The custom terminal data node or <code>null</code>.
	 *
	 * @return The tab item image or <code>null</code>.
	 */
	protected Image getTabItemImage(ITerminalConnector connector, Object data) {
		Assert.isNotNull(connector);
		return UIPlugin.getImage(ImageConsts.VIEW_Terminals);
	}

	/**
	 * Lookup a tab item with the given title and the given terminal connector.
	 * <p>
	 * <b>Note:</b> The method will handle unified tab item titles itself.
	 *
	 * @param title The tab item title. Must not be <code>null</code>.
	 * @param connector The terminal connector. Must not be <code>null</code>.
	 * @param data The custom terminal data node or <code>null</code>.
	 *
	 * @return The corresponding tab item or <code>null</code>.
	 */
	public CTabItem findTabItem(String title, ITerminalConnector connector, Object data) {
		Assert.isNotNull(title);
		Assert.isNotNull(connector);

		// Get the tab folder
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder == null) return null;

		// Loop all existing tab items and try to find a matching title
		for (CTabItem item : tabFolder.getItems()) {
			// Disposed items cannot be matched
			if (item.isDisposed()) continue;
			// Get the title from the current tab item
			String itemTitle = item.getText();
			// The terminal console state might be signaled to the user via the
			// terminal console tab title. Filter out any prefix "<.*>\s*".
			itemTitle = itemTitle.replaceFirst("^<.*>\\s*", ""); //$NON-NLS-1$ //$NON-NLS-2$
			if (itemTitle.startsWith(title)) {
				// The title string matches -> double check with the terminal connector
				ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
				ITerminalConnector connector2 = terminal.getTerminalConnector();
				// If the connector id and name matches -> check on the settings
				if (connector.getId().equals(connector2.getId()) && connector.getName().equals(connector2.getName())) {
					if (!connector.isInitialized()) {
						// an uninitialized connector does not yield a sensible summary
						return item;
					}
					String summary = connector.getSettingsSummary();
					String summary2 = connector2.getSettingsSummary();
					// If we have matching settings -> we've found the matching item
					if (summary.equals(summary2)) return item;
				}
			}
		}

		return null;
	}

	/**
	 * Make the given tab item the active tab and bring the tab to the top.
	 *
	 * @param item The tab item. Must not be <code>null</code>.
	 */
	public void bringToTop(CTabItem item) {
		Assert.isNotNull(item);

		// Get the tab folder
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder == null) return;

		// Set the given tab item as selection to the tab folder
		tabFolder.setSelection(item);
		// Fire selection changed event
		fireSelectionChanged();
	}

	/**
	 * Returns the currently active tab.
	 *
	 * @return The active tab item or <code>null</code> if none.
	 */
	public CTabItem getActiveTabItem() {
		// Get the tab folder
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder == null) return null;

		return tabFolder.getSelection();
	}

	/**
	 * Remove all terminated tab items.
	 */
	public void removeTerminatedItems() {
		// Get the tab folder
		CTabFolder tabFolder = getTabFolder();
		if (tabFolder == null) return;

		// Loop the items and check for terminated status
		for (CTabItem item: tabFolder.getItems()) {
			// Disposed items cannot be matched
			if (item.isDisposed()) continue;
			// Check if the item is terminated
			if (isTerminatedTabItem(item)) {
				// item is terminated -> dispose
				item.dispose();
			}
		}
	}

	/**
	 * Checks if the given tab item represents a terminated console. Subclasses may
	 * overwrite this method to extend the definition of terminated.
	 *
	 * @param item The tab item or <code>null</code>.
	 * @return <code>True</code> if the tab item represents a terminated console, <code>false</code> otherwise.
	 */
	protected boolean isTerminatedTabItem(CTabItem item) {
		// Null items or disposed items cannot be matched
		if (item == null || item.isDisposed()) return false;

		// First, match the item title. If it contains "<terminated>", the item can be removed
		String itemTitle = item.getText();
		if (itemTitle != null && itemTitle.contains("<terminated>")) { //$NON-NLS-1$
			return true;
		}
		// Second, check if the associated terminal control is closed
		// The title string matches -> double check with the terminal connector
		ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
		if (terminal != null && terminal.getState() == TerminalState.CLOSED) {
			return true;
		}

		return false;
	}

	/**
	 * Returns the command input field handler for the given tab item.
	 *
	 * @param item The tab item or <code>null</code>.
	 * @return The command input field handler or <code>null</code>.
	 */
	public final TabCommandFieldHandler getTabCommandFieldHandler(CTabItem item) {
		// Null items or disposed items cannot be matched
		if (item == null || item.isDisposed()) return null;

		TabCommandFieldHandler handler = commandFieldHandler.get(item);
		if (handler == null) {
			handler = new TabCommandFieldHandler(this, item);
			commandFieldHandler.put(item, handler);
		}
		return handler;
	}

	/**
	 * Dispose the command input field handler for the given tab item.
	 *
	 * @param item The tab item or <code>null</code>.
	 */
	protected void disposeTabCommandFieldHandler(CTabItem item) {
		// Null items or disposed items cannot be matched
		if (item == null || item.isDisposed()) return;

		TabCommandFieldHandler handler = commandFieldHandler.remove(item);
		if (handler != null) handler.dispose();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	@Override
	public void addSelectionChangedListener(ISelectionChangedListener listener) {
		if (listener != null && !selectionChangedListeners.contains(listener)) selectionChangedListeners.add(listener);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	@Override
	public void removeSelectionChangedListener(ISelectionChangedListener listener) {
		if (listener != null) selectionChangedListeners.remove(listener);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
	 */
	@Override
	public ISelection getSelection() {
		CTabItem activeTabItem = getActiveTabItem();
		return activeTabItem != null ? new StructuredSelection(activeTabItem) : new StructuredSelection();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
	 */
	@Override
	public void setSelection(ISelection selection) {
		if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
			// The first selection element which is a CTabItem will become the active item
			Iterator<?> iterator = ((IStructuredSelection)selection).iterator();
			while (iterator.hasNext()) {
				Object candidate = iterator.next();
				if (candidate instanceof CTabItem) { bringToTop((CTabItem)candidate); return; }
			}
		}
		// fire a changed event in any case
		fireSelectionChanged(selection);
	}

	/**
	 * Fire the selection changed event to the registered listeners.
	 */
	protected void fireSelectionChanged() {
		updateStatusLine();
		fireSelectionChanged(getSelection());
	}

	/**
	 * Fire the selection changed event to the registered listeners.
	 */
	protected final void fireSelectionChanged(ISelection selection) {
		// Create the selection changed event
		SelectionChangedEvent event = new SelectionChangedEvent(TabFolderManager.this, selection);

		// First, invoke the registered listeners and let them do their job
		for (ISelectionChangedListener listener : selectionChangedListeners) {
			listener.selectionChanged(event);
		}
	}

	/**
	 * Update the parent view status line.
	 */
	public final void updateStatusLine() {
		String message = null;
		IStatusLineManager manager = parentView.getViewSite().getActionBars().getStatusLineManager();

		CTabItem item = getActiveTabItem();
		if (item != null && !item.isDisposed()) {
			ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
			if (terminal != null && !terminal.isDisposed()) {
				StringBuilder buffer = new StringBuilder();

				buffer.append(state2msg(item, terminal.getState()));
				buffer.append(" - "); //$NON-NLS-1$

				String encoding = terminal.getEncoding();
				if (encoding == null || "ISO-8859-1".equals(encoding)) { //$NON-NLS-1$
					encoding = "Default (ISO-8859-1)"; //$NON-NLS-1$
				}
				buffer.append(NLS.bind(Messages.TabFolderManager_encoding, encoding));

				message = buffer.toString();
			}
		}

		manager.setMessage(message);
	}

	/**
	 * Returns the string representation of the given terminal state.
	 *
	 * @param item The tab folder item. Must not be <code>null</code>.
	 * @param state The terminal state. Must not be <code>null</code>.
	 *
	 * @return The string representation.
	 */
	@SuppressWarnings("unchecked")
    protected String state2msg(CTabItem item, TerminalState state) {
		Assert.isNotNull(item);
		Assert.isNotNull(state);

		// Determine the terminal properties of the tab folder
		Map<String, Object> properties = (Map<String, Object>)item.getData("properties"); //$NON-NLS-1$

		// Get he current terminal state as string
		String stateStr = state.toString();
		// Lookup a matching text representation of the state
		String key = "TabFolderManager_state_" + stateStr.replaceAll("\\.", " ").trim().toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		String stateMsg = null;
		if (properties != null) stateMsg = properties.get(key) instanceof String ? (String) properties.get(key) : null;
		if (stateMsg == null) stateMsg = Messages.getString(key);
		if (stateMsg == null) stateMsg = stateStr;

		return stateMsg;
	}
}

Back to the top