2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.merge.builtin.visualizer;
44
import java.awt.Component;
45
import java.awt.Dimension;
46
import java.awt.Point;
47
import java.awt.Rectangle;
48
import java.awt.Toolkit;
49
import java.beans.PropertyChangeEvent;
50
import java.beans.PropertyChangeListener;
51
import java.beans.PropertyVetoException;
52
import java.io.IOException;
53
import java.lang.ref.Reference;
54
import java.lang.ref.WeakReference;
55
import java.util.ArrayList;
56
import java.util.HashMap;
57
import java.util.Iterator;
59
import javax.swing.JPopupMenu;
60
import javax.swing.SwingUtilities;
61
import javax.swing.event.ChangeListener;
62
import javax.swing.plaf.TabbedPaneUI;
63
import org.openide.DialogDisplayer;
64
import org.openide.ErrorManager;
66
import org.openide.NotifyDescriptor;
67
import org.openide.actions.FileSystemAction;
68
import org.openide.awt.MouseUtils;
69
import org.openide.awt.JPopupMenuPlus;
70
import org.openide.cookies.CloseCookie;
71
import org.openide.cookies.SaveCookie;
72
import org.openide.nodes.Node;
73
import org.openide.windows.TopComponent;
74
import org.openide.windows.Workspace;
75
import org.openide.util.*;
76
import org.openide.util.actions.CallableSystemAction;
77
import org.openide.util.actions.SystemAction;
78
import org.openide.windows.WindowManager;
79
import org.netbeans.api.javahelp.Help;
82
* This is a component, that acts as a non modal dialog.
83
* There are problems with accessibility to non-modal dialogs,
84
* therefore this approach was chosen.
85
* @author Martin Entlicher
87
public class MergeDialogComponent extends TopComponent implements ChangeListener {
89
public static final String PROP_PANEL_CLOSING = "panelClosing"; // NOI18N
90
public static final String PROP_ALL_CLOSED = "allPanelsClosed"; // NOI18N
91
public static final String PROP_ALL_CANCELLED = "allPanelsCancelled"; // NOI18N
92
public static final String PROP_PANEL_SAVE = "panelSave"; // NOI18N
94
private Map<MergePanel, MergeNode> nodesForPanels = new HashMap<MergePanel, MergeNode>();
96
/** Creates new form MergeDialogComponent */
97
public MergeDialogComponent() {
100
putClientProperty("PersistenceType", "Never");
101
setName(org.openide.util.NbBundle.getMessage(MergeDialogComponent.class, "MergeDialogComponent.title"));
102
getAccessibleContext().setAccessibleName(NbBundle.getMessage(MergeDialogComponent.class, "ACSN_Merge_Dialog_Component")); // NOI18N
103
getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(MergeDialogComponent.class, "ACSD_Merge_Dialog_Component")); // NOI18N
104
mergeTabbedPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(MergeDialogComponent.class, "ACSN_Merge_Tabbed_Pane")); // NOI18N
105
mergeTabbedPane.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(MergeDialogComponent.class, "ACSD_Merge_Tabbed_Pane")); // NOI18N
108
public HelpCtx getHelpCtx() {
109
return new HelpCtx(MergeDialogComponent.class);
112
/** This method is called from within the constructor to
113
* initialize the form.
114
* WARNING: Do NOT modify this code. The content of this method is
115
* always regenerated by the Form Editor.
117
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
118
private void initComponents() {
119
java.awt.GridBagConstraints gridBagConstraints;
121
mergeTabbedPane = new javax.swing.JTabbedPane();
122
buttonsPanel = new javax.swing.JPanel();
123
okButton = new javax.swing.JButton();
124
cancelButton = new javax.swing.JButton();
125
helpButton = new javax.swing.JButton();
127
FormListener formListener = new FormListener();
129
setLayout(new java.awt.GridBagLayout());
131
mergeTabbedPane.setTabPlacement(javax.swing.JTabbedPane.BOTTOM);
132
mergeTabbedPane.setPreferredSize(new java.awt.Dimension(600, 600));
133
gridBagConstraints = new java.awt.GridBagConstraints();
134
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
135
gridBagConstraints.weightx = 1.0;
136
gridBagConstraints.weighty = 1.0;
137
add(mergeTabbedPane, gridBagConstraints);
139
buttonsPanel.setLayout(new java.awt.GridBagLayout());
141
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(MergeDialogComponent.class, "BTN_OK")); // NOI18N
142
okButton.setToolTipText(org.openide.util.NbBundle.getBundle(MergeDialogComponent.class).getString("ACS_BTN_OKA11yDesc")); // NOI18N
143
okButton.addActionListener(formListener);
144
gridBagConstraints = new java.awt.GridBagConstraints();
145
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
146
gridBagConstraints.weightx = 1.0;
147
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
148
buttonsPanel.add(okButton, gridBagConstraints);
150
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MergeDialogComponent.class, "BTN_Cancel")); // NOI18N
151
cancelButton.setToolTipText(org.openide.util.NbBundle.getBundle(MergeDialogComponent.class).getString("ACS_BTN_CancelA11yDesc")); // NOI18N
152
cancelButton.addActionListener(formListener);
153
gridBagConstraints = new java.awt.GridBagConstraints();
154
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
155
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
156
buttonsPanel.add(cancelButton, gridBagConstraints);
158
org.openide.awt.Mnemonics.setLocalizedText(helpButton, org.openide.util.NbBundle.getMessage(MergeDialogComponent.class, "BTN_Help")); // NOI18N
159
helpButton.setToolTipText(org.openide.util.NbBundle.getBundle(MergeDialogComponent.class).getString("ACS_BTN_HelpA11yDesc")); // NOI18N
160
helpButton.addActionListener(formListener);
161
gridBagConstraints = new java.awt.GridBagConstraints();
162
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
163
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
164
buttonsPanel.add(helpButton, gridBagConstraints);
166
gridBagConstraints = new java.awt.GridBagConstraints();
167
gridBagConstraints.gridy = 1;
168
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
169
gridBagConstraints.weightx = 1.0;
170
gridBagConstraints.insets = new java.awt.Insets(12, 12, 11, 11);
171
add(buttonsPanel, gridBagConstraints);
174
// Code for dispatching events from components to event handlers.
176
private class FormListener implements java.awt.event.ActionListener {
178
public void actionPerformed(java.awt.event.ActionEvent evt) {
179
if (evt.getSource() == okButton) {
180
MergeDialogComponent.this.okButtonActionPerformed(evt);
182
else if (evt.getSource() == cancelButton) {
183
MergeDialogComponent.this.cancelButtonActionPerformed(evt);
185
else if (evt.getSource() == helpButton) {
186
MergeDialogComponent.this.helpButtonActionPerformed(evt);
189
}// </editor-fold>//GEN-END:initComponents
191
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
192
// Add your handling code here:
193
//List panelsToCloseList;
195
synchronized (this) {
196
panels = mergeTabbedPane.getComponents();
198
boolean warning = false;
199
ArrayList<String> unsavedPanelNames = new ArrayList<String>();
200
ArrayList<SaveCookie> saveCookies = new ArrayList<SaveCookie>();
201
for (int i = 0; i < panels.length; i++) {
202
MergePanel panel = (MergePanel) panels[i];
203
if((panel.getNumUnresolvedConflicts() > 0) && (!warning))
205
MergeNode node = nodesForPanels.get(panel);
207
if ((sc = node.getCookie(SaveCookie.class)) != null) {
208
unsavedPanelNames.add(panel.getName());
213
// XXX can format with one format string
214
if (unsavedPanelNames.size() == 1) {
215
ret = DialogDisplayer.getDefault().notify(
216
new NotifyDescriptor.Confirmation((warning)?NbBundle.getMessage(MergeDialogComponent.class,"SaveFileWarningQuestion",unsavedPanelNames.get(0)):
217
NbBundle.getMessage(MergeDialogComponent.class,"SaveFileQuestion",unsavedPanelNames.get(0)),
218
NotifyDescriptor.YES_NO_CANCEL_OPTION));
219
} else if (unsavedPanelNames.size() > 1) {
220
ret = DialogDisplayer.getDefault().notify(
221
new NotifyDescriptor.Confirmation((warning)?NbBundle.getMessage(MergeDialogComponent.class,"SaveFilesWarningQuestion",new Integer(unsavedPanelNames.size())):
222
NbBundle.getMessage(MergeDialogComponent.class,"SaveFilesQuestion",new Integer(unsavedPanelNames.size())),
223
NotifyDescriptor.YES_NO_CANCEL_OPTION));
226
ret = DialogDisplayer.getDefault().notify(
227
new NotifyDescriptor.Confirmation(NbBundle.getMessage(MergeDialogComponent.class,"WarningQuestion",new Integer(unsavedPanelNames.size())),
228
NotifyDescriptor.OK_CANCEL_OPTION));
229
if(ret.equals(NotifyDescriptor.NO_OPTION))
232
ret = NotifyDescriptor.YES_OPTION;
234
if (!NotifyDescriptor.YES_OPTION.equals(ret) && !NotifyDescriptor.NO_OPTION.equals(ret)) return ;
235
if (NotifyDescriptor.YES_OPTION.equals(ret) || NotifyDescriptor.OK_OPTION.equals(ret)) {
236
for (SaveCookie sc: saveCookies) {
237
IOException ioException = null;
240
} catch (UserQuestionException uqex) {
241
Object status = DialogDisplayer.getDefault().notify(
242
new NotifyDescriptor.Confirmation(uqex.getLocalizedMessage()));
243
if (status == NotifyDescriptor.OK_OPTION || status == NotifyDescriptor.YES_OPTION) {
248
} catch (IOException ioex) {
255
} catch (IOException ioex) {
259
} else if (status != NotifyDescriptor.NO_OPTION) {
263
} catch (IOException ioEx) {
266
if (ioException != null) {
267
ErrorManager.getDefault().notify(ioException);
268
// cancel the close - there was an error on save
273
for (int i = 0; i < panels.length; i++) {
274
MergePanel panel = (MergePanel) panels[i];
276
fireVetoableChange(PROP_PANEL_CLOSING, null, panel);
277
} catch (PropertyVetoException pvex) {
280
removeMergePanel(panel);
282
}//GEN-LAST:event_okButtonActionPerformed
284
private void helpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_helpButtonActionPerformed
285
Help help = (Help) Lookup.getDefault().lookup(Help.class);
286
help.showHelp(getHelpCtx());
287
}//GEN-LAST:event_helpButtonActionPerformed
289
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
290
// Add your handling code here:
291
synchronized (this) {
293
fireVetoableChange(PROP_ALL_CANCELLED, null, null);
294
} catch (PropertyVetoException pvex) {}
297
}//GEN-LAST:event_cancelButtonActionPerformed
299
protected void componentClosed() {
301
synchronized (this) {
303
fireVetoableChange(PROP_ALL_CANCELLED, null, null);
304
} catch (PropertyVetoException pvex) {}
306
panels = mergeTabbedPane.getComponents();
308
for (int i = 0; i < panels.length; i++) {
309
MergePanel panel = (MergePanel) panels[i];
310
removeMergePanel(panel);
314
/** @return Preferred size of editor top component */
315
public Dimension getPreferredSize() {
316
Rectangle bounds = WindowManager.getDefault().getCurrentWorkspace().getBounds();
317
return new Dimension(bounds.width / 2, (int) (bounds.height / 1.25));
320
public int getPersistenceType() {
321
return TopComponent.PERSISTENCE_NEVER;
324
// Variables declaration - do not modify//GEN-BEGIN:variables
325
private javax.swing.JPanel buttonsPanel;
326
private javax.swing.JButton cancelButton;
327
private javax.swing.JButton helpButton;
328
private javax.swing.JTabbedPane mergeTabbedPane;
329
private javax.swing.JButton okButton;
330
// End of variables declaration//GEN-END:variables
332
public void addNotify() {
334
javax.swing.JRootPane root = getRootPane();
335
if (root != null) root.setDefaultButton(okButton);
337
javax.swing.SwingUtilities.invokeLater(new Runnable() {
339
javax.swing.JRootPane root = getRootPane();
340
if (root != null) root.setDefaultButton(okButton);
346
private void initListeners() {
347
mergeTabbedPane.addMouseListener(new PopupMenuImpl());
348
mergeTabbedPane.addChangeListener(this);
351
public void open(Workspace workspace) {
352
super.open(workspace);
356
public synchronized void addMergePanel(MergePanel panel) {
357
mergeTabbedPane.addTab(panel.getName(), panel);
358
MergeNode node = new MergeNode(panel);
359
nodesForPanels.put(panel, node);
360
mergeTabbedPane.setSelectedComponent(panel);
361
setActivatedNodes(new Node[] { node });
364
public synchronized void removeMergePanel(MergePanel panel) {
365
mergeTabbedPane.remove(panel);
366
nodesForPanels.remove(panel);
367
if (mergeTabbedPane.getTabCount() == 0) {
369
fireVetoableChange(PROP_ALL_CLOSED, null, null);
370
} catch (PropertyVetoException pvex) {
377
public MergePanel getSelectedMergePanel() {
378
Component selected = mergeTabbedPane.getSelectedComponent();
379
if (selected == null || !(selected instanceof MergePanel)) return null;
380
return ((MergePanel) selected);
383
private static JPopupMenu createPopupMenu(MergePanel panel) {
384
JPopupMenu popup = new JPopupMenuPlus();
385
SystemAction[] actions = panel.getSystemActions();
386
for (int i = 0; i < actions.length; i++) {
387
if (actions[i] == null) {
388
popup.addSeparator();
389
} else if (actions[i] instanceof CallableSystemAction) {
390
popup.add(((CallableSystemAction)actions[i]).getPopupPresenter());
391
//add FileSystemAction to pop-up menu
392
} else if (actions[i] instanceof FileSystemAction) {
393
popup.add(((FileSystemAction)actions[i]).getPopupPresenter());
399
/** Shows given popup on given coordinations and takes care about the
400
* situation when menu can exceed screen limits.
401
* Copied from org.netbeans.core.windows.frames.DefaultContainerImpl
403
private static void showPopupMenu(JPopupMenu popup, Point p, Component comp) {
404
SwingUtilities.convertPointToScreen(p, comp);
405
Dimension popupSize = popup.getPreferredSize();
406
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
408
if (p.x + popupSize.width > screenSize.width) {
409
p.x = screenSize.width - popupSize.width;
411
if (p.y + popupSize.height > screenSize.height) {
412
p.y = screenSize.height - popupSize.height;
414
SwingUtilities.convertPointFromScreen(p, comp);
415
popup.show(comp, p.x, p.y);
418
/** Listen on tabbed pane merge panel selection */
419
public void stateChanged(javax.swing.event.ChangeEvent changeEvent) {
420
MergePanel panel = (MergePanel) mergeTabbedPane.getSelectedComponent();
422
Node node = nodesForPanels.get(panel);
424
setActivatedNodes(new Node[] { node });
429
/** Popup menu reaction implementation */
430
private class PopupMenuImpl extends MouseUtils.PopupMouseAdapter {
431
public PopupMenuImpl () {}
433
/** Called when the seqeunce of mouse events should lead to actual
434
* showing of the popup menu. */
435
protected void showPopup(java.awt.event.MouseEvent mouseEvent) {
436
TabbedPaneUI tabUI = mergeTabbedPane.getUI();
437
int clickTab = tabUI.tabForCoordinate(mergeTabbedPane, mouseEvent.getX(), mouseEvent.getY());
438
MergePanel panel = getSelectedMergePanel();
442
if (clickTab != -1) {
443
//Click is on valid tab, not on empty area in tab
444
showPopupMenu(createPopupMenu(panel), mouseEvent.getPoint(), mergeTabbedPane);
450
private class MergeNode extends org.openide.nodes.AbstractNode implements PropertyChangeListener, SaveCookie {
452
private Reference<MergePanel> mergePanelRef;
454
public MergeNode(MergePanel panel) {
455
super(org.openide.nodes.Children.LEAF);
456
panel.addPropertyChangeListener(WeakListeners.propertyChange(this, panel));
457
mergePanelRef = new WeakReference<MergePanel>(panel);
458
getCookieSet().add(new CloseCookieImpl());
462
private void activateSave() {
463
getCookieSet().add(this);
466
private void deactivateSave() {
467
getCookieSet().remove(this);
470
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
471
if (MergePanel.PROP_CAN_BE_SAVED.equals(propertyChangeEvent.getPropertyName())) {
473
} else if (MergePanel.PROP_CAN_NOT_BE_SAVED.equals(propertyChangeEvent.getPropertyName())) {
478
public void save() throws java.io.IOException {
480
MergeDialogComponent.this.fireVetoableChange(PROP_PANEL_SAVE, null, mergePanelRef.get());
481
} catch (PropertyVetoException vetoEx) {
482
Throwable cause = vetoEx.getCause();
483
if (cause instanceof IOException) {
484
throw (IOException) cause;
486
throw new java.io.IOException(vetoEx.getLocalizedMessage());
489
//System.out.println("SAVE called.");
493
private class CloseCookieImpl extends Object implements CloseCookie {
494
public CloseCookieImpl () {}
496
public boolean close() {
498
MergeDialogComponent.this.fireVetoableChange(PROP_PANEL_CLOSING, null, mergePanelRef.get());
499
} catch (PropertyVetoException vetoEx) {
502
removeMergePanel(mergePanelRef.get());