commit
44a36e5927
4722 changed files with 603578 additions and 0 deletions
@ -0,0 +1,81 @@ |
||||
# Boilerplate .gitattributes |
||||
# |
||||
# If you add anything here that might be useful for other (new) projects, |
||||
# consider changing ssh://git/git/template.git |
||||
|
||||
* text=auto !eol |
||||
|
||||
.*ignore text |
||||
.gitignore text |
||||
.gitattributes text |
||||
|
||||
*.txt text |
||||
*.java text diff=java |
||||
*.html text diff=html |
||||
*.css text |
||||
*.js text |
||||
*.ts text |
||||
*.sql text |
||||
*.properties text |
||||
*.ipr text |
||||
*.iml text |
||||
*.iws text |
||||
*.xml text |
||||
*.form text |
||||
*.dtd text |
||||
*.xsl text |
||||
*.xslt text |
||||
*.plist text |
||||
*.vm text |
||||
*.install4j text |
||||
|
||||
*.cs text diff=csharp |
||||
*.csproj text merge=union |
||||
*.sln text merge=union eol=crlf |
||||
*.dsp text eol=crlf |
||||
*.dsw text eol=crlf |
||||
*.c text diff=cpp |
||||
*.cpp text diff=cpp |
||||
*.h text diff=cpp |
||||
|
||||
*.php text diff=php |
||||
*.py text diff=python |
||||
|
||||
*.sh text eol=lf |
||||
*.bat text eol=crlf |
||||
*.cmd text eol=crlf |
||||
*.lnk text eol=crlf |
||||
Makefile text |
||||
|
||||
*.png binary |
||||
*.jpg binary |
||||
*.gif binary |
||||
*.license binary |
||||
*.jar binary |
||||
*.zip binary |
||||
*.exe binary |
||||
*.bin binary |
||||
*.dat binary |
||||
*.bmp binary |
||||
*.ico binary |
||||
*.pdf binary |
||||
|
||||
*.json text |
||||
|
||||
*.jsbe text eol=crlf |
||||
*.jsbp text eol=crlf |
||||
*.jsbn text eol=crlf |
||||
*.jsbh text eol=crlf |
||||
*.jsbf text eol=crlf |
||||
*.jsbc text eol=crlf |
||||
|
||||
*.mp4 binary |
||||
|
||||
*.soy text |
||||
*.groovy text |
||||
*.gant text |
||||
|
||||
README text |
||||
|
||||
*.pem text |
||||
*.gpg text |
@ -0,0 +1,3 @@ |
||||
build/ |
||||
build.idea/ |
||||
ant/generated.xml |
@ -0,0 +1,8 @@ |
||||
# Default ignored files |
||||
/shelf/ |
||||
/workspace.xml |
||||
# Datasource local storage ignored files |
||||
/dataSources/ |
||||
/dataSources.local.xml |
||||
# Editor-based HTTP Client requests |
||||
/httpRequests/ |
@ -0,0 +1,9 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module type="JAVA_MODULE" version="4"> |
||||
<component name="NewModuleRootManager" inherit-compiler-output="true"> |
||||
<exclude-output /> |
||||
<content url="file://$MODULE_DIR$" /> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
</component> |
||||
</module> |
@ -0,0 +1,10 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="JavaScriptSettings"> |
||||
<option name="languageLevel" value="ES6" /> |
||||
</component> |
||||
<component name="PreferredVcsStorage"> |
||||
<preferredVcsName>ApexVCS</preferredVcsName> |
||||
</component> |
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8_112" project-jdk-type="JavaSDK" /> |
||||
</project> |
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="ProjectModuleManager"> |
||||
<modules> |
||||
<module fileurl="file://$PROJECT_DIR$/.idea/jiraclient.iml" filepath="$PROJECT_DIR$/.idea/jiraclient.iml" /> |
||||
</modules> |
||||
</component> |
||||
</project> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="VcsDirectoryMappings"> |
||||
<mapping directory="" vcs="Git" /> |
||||
</component> |
||||
</project> |
@ -0,0 +1,13 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module relativePaths="true" type="JAVA_MODULE" version="4"> |
||||
<component name="NewModuleRootManager" inherit-compiler-output="false"> |
||||
<output url="file://$MODULE_DIR$/../build.idea/AppInit" /> |
||||
<exclude-output /> |
||||
<content url="file://$MODULE_DIR$"> |
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> |
||||
</content> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
</component> |
||||
</module> |
||||
|
@ -0,0 +1,9 @@ |
||||
package com.almworks.appinit; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public interface AWTEventPreprocessor { |
||||
boolean preprocess(AWTEvent event, boolean alreadyConsumed); |
||||
|
||||
boolean postProcess(AWTEvent event, boolean alreadyConsumed); |
||||
} |
@ -0,0 +1,117 @@ |
||||
package com.almworks.appinit; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.InputEvent; |
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.util.ArrayList; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* @author dyoma |
||||
*/ |
||||
public class EventQueueReplacement extends EventQueue { |
||||
// @Nullable
|
||||
private static EventQueueReplacement INSTANCE; |
||||
private final java.util.List<AWTEventPreprocessor> myPreprocessors = new ArrayList(); |
||||
private AWTEventPreprocessor[] myArray; |
||||
|
||||
private EventQueueReplacement() { |
||||
} |
||||
|
||||
// @ThreadSafe
|
||||
public void addPreprocessor(final AWTEventPreprocessor preprocessor) { |
||||
synchronized (myPreprocessors) { |
||||
myPreprocessors.add(preprocessor); |
||||
resetArray(); |
||||
} |
||||
} |
||||
|
||||
private void resetArray() { |
||||
int size = myPreprocessors.size(); |
||||
myArray = size == 0 ? null : myPreprocessors.toArray(new AWTEventPreprocessor[size]); |
||||
} |
||||
|
||||
// @ThreadSafe
|
||||
public boolean removePreprocessor(AWTEventPreprocessor preprocessor) { |
||||
synchronized (myPreprocessors) { |
||||
boolean result = myPreprocessors.remove(preprocessor); |
||||
resetArray(); |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
protected void dispatchEvent(AWTEvent event) { |
||||
boolean consumed = false; |
||||
AWTEventPreprocessor[] array = myArray; |
||||
if (array != null) { |
||||
for (AWTEventPreprocessor preprocessor : array) { |
||||
consumed = preprocessor.preprocess(event, consumed); |
||||
if (consumed) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (!consumed) { |
||||
super.dispatchEvent(event); |
||||
} |
||||
consumed = consumed || ((event instanceof InputEvent) && ((InputEvent) event).isConsumed()); |
||||
if (array != null && !consumed) { |
||||
for (AWTEventPreprocessor processor : array) { |
||||
consumed = processor.postProcess(event, consumed); |
||||
if (consumed) break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// @NotNull
|
||||
public static EventQueueReplacement ensureInstalled() { |
||||
synchronized (EventQueueReplacement.class) { |
||||
if (INSTANCE != null) return INSTANCE; |
||||
} |
||||
Runnable runnable = new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
synchronized (EventQueueReplacement.class) { |
||||
if (INSTANCE == null) { |
||||
EventQueueReplacement queue = new EventQueueReplacement(); |
||||
Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue); |
||||
INSTANCE = queue; |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
if (EventQueue.isDispatchThread()) { |
||||
// Install event queue in separate invocation event. Otherwise previous AWT thread become not-AWT, so AWT confined object may be accessed from two threads (old AWT and new AWT).
|
||||
// Code in both threads may check the thread and get the confirmation.
|
||||
// To avoid such problems:
|
||||
// 1. Install event queue from current AWT thread. This ensures that no other code is executed in AWT thread, no AWT confined objects can be accessed right during event queue
|
||||
// replacement.
|
||||
// 2. Install event queue in separate invocation event. Avoid any activity before and especially after event queue replacement. Since this thread is not AWT thread any more.
|
||||
// This check logs error if it can not control right event queue replacement.
|
||||
Logger.getLogger("").log(Level.WARNING, "Install event queue from not dispatch thread", new Throwable()); |
||||
runnable.run(); |
||||
} |
||||
else try { |
||||
SwingUtilities.invokeAndWait(runnable); |
||||
} catch (InterruptedException e) { |
||||
throw new RuntimeException(e); |
||||
} catch (InvocationTargetException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
synchronized (EventQueueReplacement.class) { |
||||
return INSTANCE; |
||||
} |
||||
} |
||||
|
||||
public static void detachPreprocessor(AWTEventPreprocessor preprocessor) { |
||||
EventQueueReplacement instance; |
||||
synchronized (EventQueueReplacement.class) { |
||||
instance = INSTANCE; |
||||
if (instance == null) |
||||
return; |
||||
} |
||||
instance.removePreprocessor(preprocessor); |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module relativePaths="true" type="JAVA_MODULE" version="4"> |
||||
<component name="NewModuleRootManager"> |
||||
<output url="file://$MODULE_DIR$/../build.idea/Application" /> |
||||
<exclude-output /> |
||||
<content url="file://$MODULE_DIR$"> |
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> |
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> |
||||
</content> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
<orderEntry type="library" name="junit" level="project" /> |
||||
<orderEntry type="library" name="picocontainer" level="project" /> |
||||
<orderEntry type="module" module-name="Utils" /> |
||||
<orderEntry type="module-library"> |
||||
<library> |
||||
<CLASSES> |
||||
<root url="file://$MODULE_DIR$/rc" /> |
||||
</CLASSES> |
||||
<JAVADOC /> |
||||
<SOURCES /> |
||||
</library> |
||||
</orderEntry> |
||||
<orderEntry type="module" module-name="CoreComponents" exported="" /> |
||||
<orderEntry type="module" module-name="Engine" exported="" /> |
||||
<orderEntry type="module" module-name="Launcher" /> |
||||
<orderEntry type="library" name="jetbrains-annotations" level="project" /> |
||||
<orderEntry type="module" module-name="Services" exported="" /> |
||||
<orderEntry type="library" name="jgoodies-forms" level="project" /> |
||||
<orderEntry type="module" module-name="CommonUtils" /> |
||||
<orderEntry type="module" module-name="Screenshot" /> |
||||
<orderEntry type="module" module-name="CommonUtils" /> |
||||
<orderEntry type="library" name="itext" level="project" /> |
||||
<orderEntry type="library" name="jna" level="project" /> |
||||
<orderEntry type="module" module-name="ItemStorage" /> |
||||
<orderEntry type="module" module-name="ItemSync" /> |
||||
<orderEntry type="module" module-name="ItemGUI" /> |
||||
<orderEntry type="module" module-name="widgets" /> |
||||
</component> |
||||
<component name="RetroweaverPlugin"> |
||||
<setting name="active" value="true" /> |
||||
<setting name="verbose" value="false" /> |
||||
<setting name="verifyrefs" value="true" /> |
||||
<setting name="targetJDK" value="1.4" /> |
||||
</component> |
||||
</module> |
@ -0,0 +1,81 @@ |
||||
<!DOCTYPE ui [ |
||||
<!ELEMENT viewer (id,presentation,componentFactory,componentBinding)> |
||||
<!ELEMENT componentBinding (actionBinding*, modelBinding*, sameNames?)> |
||||
<!ELEMENT sameNames (#PCDATA)> |
||||
<!ELEMENT actionBinding (componentName, browseWeb)> |
||||
<!ELEMENT modelBinding (componentName, key)> |
||||
<!ELEMENT browseWeb (#PCDATA)> |
||||
<!ELEMENT presentation (Name)> |
||||
<!ELEMENT sizePolicy (srinkable)> |
||||
<!ELEMENT prototype (property)+> |
||||
<!ELEMENT property (key,value)> |
||||
<!ELEMENT srinkable (#PCDATA)> |
||||
<!ELEMENT componentFactory (className,instanceFieldName)> |
||||
<!ELEMENT mainColumns (column)+> |
||||
<!ELEMENT column (key,sizePolicy?)> |
||||
<!ELEMENT ui (viewers,mainProperty,mainColumns,prototype)> |
||||
<!ELEMENT viewers (viewer)+> |
||||
<!ELEMENT id (#PCDATA)> |
||||
<!ELEMENT key (#PCDATA)> |
||||
<!ELEMENT componentName (#PCDATA)> |
||||
<!ELEMENT className (#PCDATA)> |
||||
<!ELEMENT instanceFieldName (#PCDATA)> |
||||
<!ELEMENT Name (#PCDATA)> |
||||
<!ELEMENT mainProperty (#PCDATA)> |
||||
<!ELEMENT value (#PCDATA)> |
||||
<!ELEMENT browseActionKey (#PCDATA)> |
||||
]> |
||||
<ui> |
||||
<viewers> |
||||
<viewer> |
||||
<id>system</id> |
||||
<presentation> |
||||
<Name>System</Name> |
||||
</presentation> |
||||
<componentFactory> |
||||
<className>com.almworks.explorer.view.SystemView</className> |
||||
<instanceFieldName>myWholePanel</instanceFieldName> |
||||
</componentFactory> |
||||
</viewer> |
||||
</viewers> |
||||
<mainProperty>name</mainProperty> |
||||
<mainColumns> |
||||
<column> |
||||
<key>name</key> |
||||
</column> |
||||
<column> |
||||
<key>type</key> |
||||
</column> |
||||
<column> |
||||
<key>description</key> |
||||
</column> |
||||
<column> |
||||
<key>key</key> |
||||
</column> |
||||
<column> |
||||
<key>ID</key> |
||||
</column> |
||||
</mainColumns> |
||||
<prototype> |
||||
<property> |
||||
<key>name</key> |
||||
<value>Array of references</value> |
||||
</property> |
||||
<property> |
||||
<key>type</key> |
||||
<value>Value Type</value> |
||||
</property> |
||||
<property> |
||||
<key>description</key> |
||||
<value>Values of this type are arrays of values of type Reference</value> |
||||
</property> |
||||
<property> |
||||
<key>key</key> |
||||
<value>2.3.4.5.6.7.</value> |
||||
</property> |
||||
<property> |
||||
<key>ID</key> |
||||
<value>tracker:whatever:address:could:be:here</value> |
||||
</property> |
||||
</prototype> |
||||
</ui> |
@ -0,0 +1,2 @@ |
||||
sumtable.configurations.duplicateName.name={1}{0,choice,0#|0< {0}} |
||||
sumtable.configurations.duplicateName.applied=(.*)\\s(\\d+) |
@ -0,0 +1 @@ |
||||
sysprop.window.seedoc.text=See system properties reference |
@ -0,0 +1,57 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.application.tree.ConnectionNode; |
||||
import com.almworks.api.application.tree.GenericNode; |
||||
import com.almworks.api.engine.Connection; |
||||
import com.almworks.api.search.TextSearchUtils; |
||||
import com.almworks.util.commons.FunctionE; |
||||
import com.almworks.util.ui.actions.ActionContext; |
||||
import com.almworks.util.ui.actions.CantPerformException; |
||||
import com.almworks.util.ui.actions.SimpleAction; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
|
||||
public abstract class ConnectionAction extends SimpleAction { |
||||
public static final FunctionE<ActionContext, Connection, CantPerformException> SELECTED_NODE = |
||||
new FunctionE<ActionContext, Connection, CantPerformException>() { |
||||
@Override |
||||
public Connection invoke(ActionContext context) throws CantPerformException { |
||||
final List<GenericNode> nodes = context.getSourceCollection(GenericNode.NAVIGATION_NODE); |
||||
return getConnection(nodes); |
||||
} |
||||
}; |
||||
|
||||
public static final FunctionE<ActionContext, Connection, CantPerformException> PARENT_NODE = |
||||
new FunctionE<ActionContext, Connection, CantPerformException>() { |
||||
@Override |
||||
public Connection invoke(ActionContext context) throws CantPerformException { |
||||
final Collection<GenericNode> |
||||
nodes = TextSearchUtils.escalateToConnectionNodes(context.getSourceCollection(GenericNode.NAVIGATION_NODE)); |
||||
return getConnection(nodes); |
||||
} |
||||
}; |
||||
|
||||
private static Connection getConnection(Collection<GenericNode> nodes) { |
||||
if(nodes == null || nodes.size() != 1) { |
||||
return null; |
||||
} |
||||
final GenericNode node = nodes.iterator().next(); |
||||
if(node instanceof ConnectionNode) { |
||||
return node.getConnection(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private final FunctionE<ActionContext, Connection, CantPerformException> myConnectionExtractor; |
||||
|
||||
protected ConnectionAction(@Nullable String name, FunctionE<ActionContext, Connection, CantPerformException> connectionExtractor) { |
||||
super(name); |
||||
myConnectionExtractor = connectionExtractor; |
||||
} |
||||
|
||||
protected Connection extractConnection(ActionContext context) throws CantPerformException { |
||||
return myConnectionExtractor.invoke(context); |
||||
} |
||||
} |
@ -0,0 +1,117 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.application.ItemWrapper; |
||||
import com.almworks.api.application.LoadedItemServices; |
||||
import com.almworks.api.engine.Connection; |
||||
import com.almworks.integers.LongArray; |
||||
import com.almworks.integers.LongList; |
||||
import com.almworks.integers.LongListIterator; |
||||
import com.almworks.integers.WritableLongList; |
||||
import com.almworks.items.api.DBReader; |
||||
import com.almworks.items.api.Database; |
||||
import com.almworks.items.api.ReadTransaction; |
||||
import com.almworks.items.sync.util.SyncUtils; |
||||
import com.almworks.util.Terms; |
||||
import com.almworks.util.commons.Procedure; |
||||
import com.almworks.util.exec.ThreadGate; |
||||
import com.almworks.util.i18n.Local; |
||||
import com.almworks.util.ui.UIUtil; |
||||
import com.almworks.util.ui.actions.*; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.KeyEvent; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import static org.almworks.util.Collections15.hashMap; |
||||
|
||||
public class CopyIdAndSummaryAction extends SimpleAction { |
||||
public CopyIdAndSummaryAction() { |
||||
super("C&opy ID && Summary"); |
||||
setDefaultPresentation(PresentationKey.SHORT_DESCRIPTION, "Copy ID and Summary of selected " + Terms.ref_artifacts + " to clipboard"); |
||||
setDefaultPresentation(PresentationKey.ENABLE, EnableState.DISABLED); |
||||
setDefaultPresentation(PresentationKey.SHORTCUT, |
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); |
||||
watchRole(ItemWrapper.ITEM_WRAPPER); |
||||
} |
||||
|
||||
public static Map<Connection, LongList> getItemsGroupedByConnection(ActionContext context) throws CantPerformException { |
||||
List<ItemWrapper> wrappers = context.getSourceCollection(ItemWrapper.ITEM_WRAPPER); |
||||
Map<Connection, LongList> result = hashMap(); |
||||
for (ItemWrapper wrapper : wrappers) { |
||||
LoadedItemServices services = wrapper.services(); |
||||
if (services.isDeleted()) |
||||
continue; |
||||
Connection connection = wrapper.getConnection(); |
||||
if (connection != null && !connection.getState().getValue().isDegrading()) { |
||||
WritableLongList target = (WritableLongList) result.get(connection); |
||||
if (target == null) { |
||||
target = new LongArray(); |
||||
result.put(connection, target); |
||||
} |
||||
target.add(services.getItem()); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
final Map<Connection, LongList> map = getItemsGroupedByConnection(context); |
||||
context.getSourceObject(Database.ROLE).readForeground(new ReadTransaction<StringBuilder>() { |
||||
public StringBuilder transaction(DBReader reader) { |
||||
StringBuilder result = new StringBuilder(); |
||||
boolean multiple = false; |
||||
for (Map.Entry<Connection, LongList> e : map.entrySet()) { |
||||
Connection connection = e.getKey(); |
||||
for (LongListIterator i = e.getValue().iterator(); i.hasNext();) { |
||||
String s = connection.getExternalIdSummaryString(SyncUtils.readTrunk(reader, i.nextValue())); |
||||
if (s != null && s.length() > 0) { |
||||
if (result.length() > 0) { |
||||
result.append('\n'); |
||||
multiple = true; |
||||
} |
||||
result.append(s); |
||||
} |
||||
} |
||||
} |
||||
if (multiple) |
||||
result.append('\n'); |
||||
return result; |
||||
} |
||||
}).onSuccess(ThreadGate.AWT, new Procedure<StringBuilder>() { |
||||
public void invoke(StringBuilder arg) { |
||||
if (arg.length() == 0) |
||||
return; |
||||
UIUtil.copyToClipboard(arg.toString()); |
||||
Toolkit.getDefaultToolkit().beep(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
int count = 0; |
||||
try { |
||||
List<ItemWrapper> wrappers = context.getSourceCollection(ItemWrapper.ITEM_WRAPPER); |
||||
for (ItemWrapper wrapper : wrappers) { |
||||
if (wrapper.services().isDeleted()) |
||||
return; |
||||
Connection connection = wrapper.getConnection(); |
||||
if (connection == null || connection.getState().getValue().isDegrading()) |
||||
return; |
||||
count++; |
||||
} |
||||
context.setEnabled(count > 0); |
||||
} finally { |
||||
if (count != 1) { |
||||
context.putPresentationProperty(PresentationKey.SHORT_DESCRIPTION, |
||||
Local.parse("Copy to clipboard IDs and summaries of the selected " + Terms |
||||
.ref_artifacts)); |
||||
} else { |
||||
context.putPresentationProperty(PresentationKey.SHORT_DESCRIPTION, |
||||
Local.parse("Copy to clipboard ID and summary of the selected " + Terms |
||||
.ref_artifact)); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.application.tree.GenericNode; |
||||
import com.almworks.api.application.tree.TreeNodeFactory; |
||||
import com.almworks.util.components.ACollectionComponent; |
||||
import com.almworks.util.ui.actions.*; |
||||
|
||||
import javax.swing.*; |
||||
|
||||
/** |
||||
* @author dyoma |
||||
*/ |
||||
class CreateChildNodeAction extends SimpleAction { |
||||
private final TreeNodeFactory.NodeType myNodeType; |
||||
|
||||
private CreateChildNodeAction(Icon icon, TreeNodeFactory.NodeType type) { |
||||
super((String)null, icon); |
||||
myNodeType = type; |
||||
watchRole(GenericNode.NAVIGATION_NODE); |
||||
setDefaultPresentation(PresentationKey.ENABLE, EnableState.INVISIBLE); |
||||
} |
||||
|
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
ComponentContext<ACollectionComponent> componentContext = |
||||
context.getComponentContext(ACollectionComponent.class, GenericNode.NAVIGATION_NODE); |
||||
TreeNodeFactory nodeFactory = context.getSourceObject(TreeNodeFactory.TREE_NODE_FACTORY); |
||||
GenericNode parent = getGenericNodeImpl(componentContext); |
||||
nodeFactory.createAndEditNode(parent, myNodeType, context); |
||||
} |
||||
|
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
context.getSourceObject(TreeNodeFactory.TREE_NODE_FACTORY); |
||||
ComponentContext<ACollectionComponent> componentContext = |
||||
context.getComponentContext(ACollectionComponent.class, GenericNode.NAVIGATION_NODE); |
||||
GenericNode node = getGenericNodeImpl(componentContext); |
||||
if (node.allowsChildren(myNodeType)) |
||||
context.setEnabled(EnableState.ENABLED); |
||||
} |
||||
|
||||
public static CreateChildNodeAction create(TreeNodeFactory.NodeType type) { |
||||
return new CreateChildNodeAction(null, type); |
||||
} |
||||
|
||||
public static CreateChildNodeAction create(Icon icon, TreeNodeFactory.NodeType type) { |
||||
return new CreateChildNodeAction(icon, type); |
||||
} |
||||
|
||||
public static GenericNode getGenericNodeImpl(ComponentContext<?> context) throws CantPerformException { |
||||
return context.getSourceObject(GenericNode.NAVIGATION_NODE); |
||||
} |
||||
} |
@ -0,0 +1,247 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.actions.ItemActionUtils; |
||||
import com.almworks.api.application.ItemWrapper; |
||||
import com.almworks.api.engine.Engine; |
||||
import com.almworks.api.engine.ItemSyncProblem; |
||||
import com.almworks.api.engine.Synchronizer; |
||||
import com.almworks.integers.LongArray; |
||||
import com.almworks.integers.LongList; |
||||
import com.almworks.integers.LongListIterator; |
||||
import com.almworks.integers.WritableLongListIterator; |
||||
import com.almworks.items.api.DBOperationCancelledException; |
||||
import com.almworks.items.api.DBReader; |
||||
import com.almworks.items.sync.*; |
||||
import com.almworks.items.sync.util.ItemValues; |
||||
import com.almworks.items.sync.util.SyncUtils; |
||||
import com.almworks.util.AppBook; |
||||
import com.almworks.util.Terms; |
||||
import com.almworks.util.commons.Condition; |
||||
import com.almworks.util.exec.ThreadGate; |
||||
import com.almworks.util.i18n.LText1; |
||||
import com.almworks.util.i18n.LText2; |
||||
import com.almworks.util.i18n.Local; |
||||
import com.almworks.util.images.Icons; |
||||
import com.almworks.util.ui.DialogsUtil; |
||||
import com.almworks.util.ui.actions.*; |
||||
import gnu.trove.TLongObjectHashMap; |
||||
import org.almworks.util.Log; |
||||
import util.concurrent.SynchronizedBoolean; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author dyoma |
||||
*/ |
||||
public class DiscardLocalChangesAction extends SimpleAction { |
||||
private static final String PREFIX = "Application.Actions."; |
||||
private static final Condition<ItemWrapper> DISCARDABLE_ITEM = new Condition<ItemWrapper>() { |
||||
public boolean isAccepted(ItemWrapper wrapper) { |
||||
return wrapper.getDBStatus().isDiscardable(); |
||||
} |
||||
}; |
||||
|
||||
public DiscardLocalChangesAction() { |
||||
super("Discard &Changes", Icons.ACTION_DISCARD); |
||||
} |
||||
|
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
context.watchModifiableRole(SyncManager.MODIFIABLE); |
||||
ItemActionUtils.basicUpdate(context, true); |
||||
|
||||
SyncManager syncMan = context.getSourceObject(SyncManager.ROLE); |
||||
List<ItemWrapper> items = getItemWrappers(context); |
||||
int count = items.size(); |
||||
for (ItemWrapper item : items) { |
||||
if (syncMan.findLock(item.getItem()) != null || item.services().isLockedForUpload()) { |
||||
count--; |
||||
} else { |
||||
LongList slaves = item.getMetaInfo().getSlavesToDiscard(item); |
||||
if (syncMan.findAnyLock(slaves) != null) count--; |
||||
} |
||||
} |
||||
context.setEnabled(count > 0); |
||||
} |
||||
|
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
final SyncManager syncMan = context.getSourceObject(SyncManager.ROLE); |
||||
|
||||
final LongArray items = new LongArray(); |
||||
int discarding = 0, removing = 0; |
||||
for(final ItemWrapper iw : getItemWrappers(context)) { |
||||
final long item = iw.getItem(); |
||||
if(syncMan.findLock(item) == null) { |
||||
items.add(item); |
||||
if(iw.getDBStatus() == ItemWrapper.DBStatus.DB_NEW) { |
||||
removing++; |
||||
} else { |
||||
discarding++; |
||||
} |
||||
} |
||||
} |
||||
CantPerformException.ensure(!items.isEmpty()); |
||||
|
||||
final Component component = context.getComponent(); |
||||
final String confirmationMsg = createConfirmationMessage(discarding, removing); |
||||
final Synchronizer sync = context.getSourceObject(Engine.ROLE).getSynchronizer(); |
||||
|
||||
CantPerformException.ensure( |
||||
CantPerformException.ensureNotNull(syncMan.prepareEdit(items)).start(new MyDiscardFactory(component, confirmationMsg, sync))); |
||||
} |
||||
|
||||
private static EditCommit createDiscardCommit(final LongArray items, final Synchronizer sync, final SynchronizedBoolean alive) { |
||||
return new EditCommit() { |
||||
@Override |
||||
public void performCommit(EditDrain drain) { |
||||
for (WritableLongListIterator i = items.iterator(); i.hasNext();) { |
||||
long primary = i.nextValue(); |
||||
boolean discardSucceeded = drain.discardChanges(primary); |
||||
if (!discardSucceeded) { |
||||
Log.warn("discard did not succeed for item " + primary); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onCommitFinished(boolean success) { |
||||
if(success) { |
||||
ThreadGate.AWT_QUEUED.execute(new Runnable() { public void run() { |
||||
for (WritableLongListIterator i = items.iterator(); i.hasNext();) { |
||||
removeProblem(i.nextValue(), sync); |
||||
} |
||||
}}); |
||||
} else { |
||||
alive.set(false); |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public String toString() { |
||||
return "DiscardLocalChangesAction"; |
||||
} |
||||
|
||||
private static boolean confirmDiscard(Component component, String message) { |
||||
Component c = component == null ? null : SwingUtilities.getWindowAncestor(component); |
||||
return (DialogsUtil.YES_OPTION == DialogsUtil.askUser(c, message, "Discard Changes", DialogsUtil.YES_NO_OPTION)); |
||||
} |
||||
|
||||
private static String createConfirmationMessage(int discardCount, int removeCount) { |
||||
final String message; |
||||
if(discardCount > 0 && removeCount == 0) { |
||||
message = DISCARD_CONFIRM().format(discardCount); |
||||
} else if (discardCount == 0 && removeCount > 0) { |
||||
message = REMOVE_CONFIRM().format(removeCount); |
||||
} else { |
||||
message = MIXED_CONFIRM().format(discardCount, removeCount); |
||||
} |
||||
return message; |
||||
} |
||||
|
||||
private static LText1<Integer> REMOVE_CONFIRM() { |
||||
return AppBook.text(PREFIX + "REMOVE_CONFIRM", Local.parse( |
||||
"Are you sure you want to remove {0,choice,1#this $(" + Terms.key_artifact + ")|1<these {0,number} " + |
||||
Terms.ref_artifacts + "}?"), 0); |
||||
} |
||||
|
||||
private static LText1<Integer> DISCARD_CONFIRM() { |
||||
return AppBook.text(PREFIX + "DISCARD_CONFIRM", Local.parse( |
||||
"Are you sure you want to discard {0,choice,1#changes|1<the changes to {0,number} " + Terms.ref_artifacts + "}?"), |
||||
0); |
||||
} |
||||
|
||||
private static LText2<Integer, Integer> MIXED_CONFIRM() { |
||||
return AppBook.text(PREFIX + "MIXED_CONFIRM", Local.parse( |
||||
"Are you sure you want to discard the changes to {0,number} " + Terms.ref_artifacts + " and to remove " + |
||||
"{1, number} new " + Terms.ref_artifacts + "?"), 0, 0); |
||||
} |
||||
|
||||
private static List<ItemWrapper> getItemWrappers(ActionContext context) throws CantPerformException { |
||||
List<ItemWrapper> wrappers = context.getSourceCollection(ItemWrapper.ITEM_WRAPPER); |
||||
return DISCARDABLE_ITEM.filterList(wrappers); |
||||
} |
||||
|
||||
private static void removeProblem(long item, Synchronizer synchronizer) { |
||||
if (synchronizer != null) { |
||||
for (ItemSyncProblem problem : synchronizer.getItemProblems(item)) { |
||||
problem.disappear(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static class DiscardEditor implements ItemEditor { |
||||
private final SynchronizedBoolean myAlive; |
||||
private final Component myComponent; |
||||
private final String myConfirmationMsg; |
||||
private final LongArray myItemsToDiscard; |
||||
private final Synchronizer mySync; |
||||
private final EditControl myEditControl; |
||||
|
||||
public DiscardEditor(Component component, String confirmationMsg, LongArray itemsToDiscard, Synchronizer sync, EditControl editControl, SynchronizedBoolean aliveFlag) { |
||||
myComponent = component; |
||||
myConfirmationMsg = confirmationMsg; |
||||
myItemsToDiscard = itemsToDiscard; |
||||
mySync = sync; |
||||
myEditControl = editControl; |
||||
myAlive = aliveFlag; |
||||
} |
||||
|
||||
@Override |
||||
public void showEditor() throws CantPerformException { |
||||
if (!confirmDiscard(myComponent, myConfirmationMsg)) throw new CantPerformExceptionSilently("Rejected"); |
||||
ThreadGate.AWT_QUEUED.execute(new Runnable() { public void run() { |
||||
myEditControl.commit(createDiscardCommit(myItemsToDiscard, mySync, myAlive)); |
||||
}}); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAlive() { |
||||
return myAlive.get(); |
||||
} |
||||
|
||||
@Override |
||||
public void activate() throws CantPerformException {} |
||||
|
||||
@Override |
||||
public void onEditReleased() {} |
||||
|
||||
@Override |
||||
public void onItemsChanged(TLongObjectHashMap<ItemValues> newValues) {} |
||||
} |
||||
|
||||
|
||||
private static class MyDiscardFactory implements EditorFactory { |
||||
private final Component myComponent; |
||||
private final String myConfirmationMsg; |
||||
private final Synchronizer mySync; |
||||
private final SynchronizedBoolean myAliveFlag = new SynchronizedBoolean(true); |
||||
|
||||
public MyDiscardFactory(Component component, String confirmationMsg, Synchronizer sync) { |
||||
myComponent = component; |
||||
myConfirmationMsg = confirmationMsg; |
||||
mySync = sync; |
||||
} |
||||
|
||||
@Override |
||||
public void editCancelled() { |
||||
myAliveFlag.set(false); |
||||
} |
||||
|
||||
@Override |
||||
public ItemEditor prepareEdit(DBReader reader, final EditPrepare prepare) throws DBOperationCancelledException { |
||||
LongArray primaryItems = LongArray.copy(prepare.getItems()); |
||||
final LongArray itemsToDiscard = new LongArray(primaryItems.size()); |
||||
for (LongListIterator i = primaryItems.iterator(); i.hasNext();) { |
||||
long primary = i.nextValue(); |
||||
LongList subtree = SyncUtils.getSlavesSubtree(reader, primary); |
||||
if (prepare.addItems(subtree)) { |
||||
itemsToDiscard.add(primary); |
||||
} |
||||
} |
||||
if (itemsToDiscard.isEmpty()) return null; |
||||
return new DiscardEditor(myComponent, myConfirmationMsg, itemsToDiscard, mySync, prepare.getControl(), myAliveFlag); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,145 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.actions.ItemActionUtils; |
||||
import com.almworks.api.application.*; |
||||
import com.almworks.api.engine.Connection; |
||||
import com.almworks.api.engine.Engine; |
||||
import com.almworks.api.engine.SyncTask; |
||||
import com.almworks.api.engine.Synchronizer; |
||||
import com.almworks.integers.LongList; |
||||
import com.almworks.integers.LongListIterator; |
||||
import com.almworks.util.L; |
||||
import com.almworks.util.Terms; |
||||
import com.almworks.util.collections.MultiMap; |
||||
import com.almworks.util.collections.PrimitiveUtils; |
||||
import com.almworks.util.collections.SimpleModifiable; |
||||
import com.almworks.util.commons.Condition; |
||||
import com.almworks.util.images.Icons; |
||||
import com.almworks.util.ui.actions.*; |
||||
|
||||
import java.util.List; |
||||
|
||||
class DownloadItemAction extends SimpleAction { |
||||
private static final Condition<ItemWrapper> CAN_DOWNLOAD = new CanDownload(); |
||||
private static final Condition<ItemWrapper> HAS_DETAILS = new HasDetails(); |
||||
|
||||
private static final String DOWNLOAD_SHORT = L.actionName("&Download All Details"); |
||||
private static final String RELOAD_SHORT = L.actionName("Reload All &Details"); |
||||
|
||||
private static final String DOWNLOAD_LONG = L.tooltip("Download all details for selected " + Terms.ref_artifacts); |
||||
private static final String RELOAD_LONG = L.tooltip("Re-download all details for selected " + Terms.ref_artifacts); |
||||
|
||||
private static final String DOWNLOADING = L.actionName("Downloading\u2026"); |
||||
private static final String RELOADING = L.actionName("Reloading\u2026"); |
||||
|
||||
private final Synchronizer mySync; |
||||
private boolean myPreToggle; |
||||
private final SimpleModifiable myPreToggleModifiable = new SimpleModifiable(); |
||||
|
||||
public DownloadItemAction(Engine engine) { |
||||
super(L.actionName(DOWNLOAD_SHORT), Icons.ACTION_UPDATE_ARTIFACT); |
||||
setDefaultPresentation(PresentationKey.ENABLE, EnableState.DISABLED); |
||||
mySync = engine.getSynchronizer(); |
||||
updateOnChange(mySync.getTasksModifiable()); |
||||
updateOnChange(myPreToggleModifiable); |
||||
} |
||||
|
||||
private static MultiMap<Connection, ItemWrapper> getItemWrappersGroupedByConnection(ActionContext context) |
||||
throws CantPerformException |
||||
{ |
||||
List<ItemWrapper> wrappers = context.getSourceCollection(ItemWrapper.ITEM_WRAPPER); |
||||
MultiMap<Connection, ItemWrapper> result = MultiMap.create(); |
||||
for (ItemWrapper wrapper : wrappers) { |
||||
if (wrapper.services().isRemoteDeleted()) |
||||
continue; |
||||
Connection connection = wrapper.getConnection(); |
||||
if (connection != null && !connection.getState().getValue().isDegrading()) { |
||||
result.add(connection, wrapper); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
MultiMap<Connection, ItemWrapper> map = getItemWrappersGroupedByConnection(context); |
||||
for (Connection connection : map.keySet()) { |
||||
LongList items = PrimitiveUtils.selectThenCollect(CAN_DOWNLOAD, ItemWrapper.GET_ITEM, map.getAll(connection)); |
||||
if (!items.isEmpty()) { |
||||
connection.downloadItemDetails(items); |
||||
myPreToggle = true; |
||||
} |
||||
} |
||||
if (myPreToggle) myPreToggleModifiable.fireChanged(); |
||||
} |
||||
|
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
final List<ItemWrapper> wrappers = ItemActionUtils.basicUpdate(context, false); |
||||
|
||||
boolean enabled = false; |
||||
String name = DOWNLOAD_SHORT; |
||||
String ongoing = DOWNLOADING; |
||||
String expl = DOWNLOAD_LONG; |
||||
|
||||
final List<ItemWrapper> updatable = CAN_DOWNLOAD.filterList(wrappers); |
||||
if(!updatable.isEmpty()) { |
||||
enabled = true; |
||||
|
||||
final List<ItemWrapper> reloadable = HAS_DETAILS.filterList(updatable); |
||||
if(reloadable.size() == updatable.size()) { |
||||
name = RELOAD_SHORT; |
||||
ongoing = RELOADING; |
||||
expl = RELOAD_LONG; |
||||
} |
||||
} |
||||
|
||||
context.setEnabled(enabled); |
||||
|
||||
if(areBeingDownloaded(wrappers) || getPreToggledAndClear()) { |
||||
context.putPresentationProperty(PresentationKey.NAME, ongoing); |
||||
context.putPresentationProperty(PresentationKey.SMALL_ICON, Icons.ACTION_UPDATING_ITEM.getIcon()); |
||||
} else { |
||||
context.putPresentationProperty(PresentationKey.NAME, name); |
||||
context.putPresentationProperty(PresentationKey.SMALL_ICON, Icons.ACTION_UPDATE_ARTIFACT); |
||||
} |
||||
context.putPresentationProperty(PresentationKey.SHORT_DESCRIPTION, expl); |
||||
} |
||||
|
||||
public boolean areBeingDownloaded(List<ItemWrapper> wrappers) { |
||||
LongList items = PrimitiveUtils.collect(UiItem.GET_ITEM, wrappers); |
||||
tasks: |
||||
for (SyncTask task : mySync.getTasks().copyCurrent()) { |
||||
if (SyncTask.State.isWorking(task.getState().getValue())) { |
||||
for (LongListIterator i = items.iterator(); i.hasNext();) { |
||||
if (task.getSpecificActivityForItem(i.nextValue(), null) != SyncTask.SpecificItemActivity.DOWNLOAD) |
||||
continue tasks; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public boolean getPreToggledAndClear() { |
||||
boolean ret = myPreToggle; |
||||
myPreToggle = false; |
||||
return ret; |
||||
} |
||||
|
||||
private static class CanDownload extends Condition<ItemWrapper> { |
||||
public boolean isAccepted(ItemWrapper wrapper) { |
||||
if(wrapper == null) { |
||||
return false; |
||||
} |
||||
LoadedItem.DBStatus status = wrapper.getDBStatus(); |
||||
return status != LoadedItem.DBStatus.DB_NEW && status != LoadedItem.DBStatus.DB_CONNECTION_NOT_READY; |
||||
} |
||||
} |
||||
|
||||
private static class HasDetails extends Condition<ItemWrapper> { |
||||
@Override |
||||
public boolean isAccepted(ItemWrapper value) { |
||||
ItemDownloadStage stage = ItemDownloadStageKey.retrieveValue(value); |
||||
return stage == ItemDownloadStage.STALE || stage == ItemDownloadStage.FULL; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.application.tree.GenericNode; |
||||
import com.almworks.api.engine.Connection; |
||||
import com.almworks.util.commons.FunctionE; |
||||
import com.almworks.util.ui.actions.*; |
||||
|
||||
/** |
||||
* "Edit Connection" menu action. |
||||
*/ |
||||
public class EditConnectionAction extends ConnectionAction { |
||||
public static final EditConnectionAction POPUP = new EditConnectionAction(SELECTED_NODE); |
||||
public static final EditConnectionAction MAIN = new EditConnectionAction(PARENT_NODE); |
||||
|
||||
public EditConnectionAction(FunctionE<ActionContext, Connection, CantPerformException> connectionExtractor) { |
||||
super("&Edit Connection Settings\u2026", connectionExtractor); |
||||
setDefaultPresentation(PresentationKey.SHORT_DESCRIPTION, "Change connection configuration"); |
||||
} |
||||
|
||||
@Override |
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
context.watchRole(GenericNode.NAVIGATION_NODE); |
||||
final Connection connection = extractConnection(context); |
||||
if(connection == null || connection.getProvider().isEditingConnection(connection)) { |
||||
context.setEnabled(EnableState.INVISIBLE); |
||||
} else { |
||||
context.setEnabled(EnableState.ENABLED); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
final Connection connection = extractConnection(context); |
||||
if(connection != null) { |
||||
connection.getProvider().showEditConnectionWizard(connection); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.api.application.tree.GenericNode; |
||||
import com.almworks.api.application.tree.LazyDistributionExpanderNode; |
||||
import com.almworks.util.L; |
||||
import com.almworks.util.ui.actions.*; |
||||
|
||||
// todo remove
|
||||
public class ExpandLazyDistributionAction extends SimpleAction { |
||||
public ExpandLazyDistributionAction() { |
||||
super("Expand Distribution"); |
||||
setDefaultPresentation(PresentationKey.ENABLE, EnableState.INVISIBLE); |
||||
setDefaultPresentation(PresentationKey.SHORT_DESCRIPTION, L.tooltip("Fill distribution with values")); |
||||
watchRole(GenericNode.NAVIGATION_NODE); |
||||
} |
||||
|
||||
protected void customUpdate(UpdateContext context) throws CantPerformException { |
||||
GenericNode node = context.getSourceObject(GenericNode.NAVIGATION_NODE); |
||||
context.setEnabled(node instanceof LazyDistributionExpanderNode ? EnableState.ENABLED : EnableState.INVISIBLE); |
||||
} |
||||
|
||||
protected void doPerform(ActionContext context) throws CantPerformException { |
||||
GenericNode node = context.getSourceObject(GenericNode.NAVIGATION_NODE); |
||||
if (node instanceof LazyDistributionExpanderNode) |
||||
((LazyDistributionExpanderNode) node).expand(); |
||||
} |
||||
} |
@ -0,0 +1,572 @@ |
||||
package com.almworks.actions; |
||||
|
||||
import com.almworks.actions.console.actionsource.ActionGroup; |
||||
import com.almworks.actions.console.actionsource.ConsoleActionsComponent; |
||||
import com.almworks.actions.distribution.CreateDistribution; |
||||
import com.almworks.api.application.ExplorerComponent; |
||||
import com.almworks.api.application.LifeMode; |
||||
import com.almworks.api.application.qb.FilterNode; |
||||
import com.almworks.api.application.qb.QueryBuilderComponent; |
||||
import com.almworks.api.application.tree.*; |
||||
import com.almworks.api.application.viewer.CommentsController; |
||||
import com.almworks.api.engine.Connection; |
||||
import com.almworks.api.engine.Engine; |
||||
import com.almworks.api.engine.QueryUrlInfo; |
||||
import com.almworks.api.explorer.TableController; |
||||
import com.almworks.api.gui.MainMenu; |
||||
import com.almworks.explorer.ExplorerComponentImpl; |
||||
import com.almworks.explorer.ShowHideDistributionTableAction; |
||||
import com.almworks.explorer.ShowHideNavigationAreaAction; |
||||
import com.almworks.explorer.qbuilder.filter.BinaryCommutative; |
||||
import com.almworks.explorer.tree.ExcludeDisributionNodeAction; |
||||
import com.almworks.explorer.tree.NavigationTree; |
||||
import com.almworks.explorer.tree.OutboxNode; |
||||
import com.almworks.explorer.tree.TemporaryQueriesNode; |
||||
import com.almworks.integers.LongList; |
||||
import com.almworks.sysprop.SystemPropertiesAction; |
||||
import com.almworks.tags.AddRemoveFavoritesAction; |
||||
import com.almworks.tags.ImportTagsAction; |
||||
import com.almworks.tags.NewTagAction; |
||||
import com.almworks.tags.TagItemsAction; |
||||
import com.almworks.util.*; |
||||
import com.almworks.util.commons.Condition; |
||||
import com.almworks.util.commons.Procedure; |
||||
import com.almworks.util.commons.Procedure2; |
||||
import com.almworks.util.components.TreeModelAdapter; |
||||
import com.almworks.util.components.tabs.TabActions; |
||||
import com.almworks.util.exec.ThreadGate; |
||||
import com.almworks.util.files.ExternalBrowser; |
||||
import com.almworks.util.i18n.Local; |
||||
import com.almworks.util.images.Icons; |
||||
import com.almworks.util.model.BasicScalarModel; |
||||
import com.almworks.util.threads.Bottleneck; |
||||
import com.almworks.util.threads.Threads; |
||||
import com.almworks.util.ui.actions.*; |
||||
import com.almworks.util.ui.swing.Shortcuts; |
||||
import org.almworks.util.Collections15; |
||||
import org.picocontainer.Startable; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.event.TreeModelEvent; |
||||
import javax.swing.tree.DefaultTreeModel; |
||||
import java.awt.event.InputEvent; |
||||
import java.awt.event.KeyEvent; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author : Dyoma |
||||
*/ |
||||
public class ExplorerActions implements Startable { |
||||
private final ActionRegistry myRegistry; |
||||
private final ConsoleActionsComponent myActionConsole; |
||||
private final Engine myEngine; |
||||
|
||||
public ExplorerActions(ActionRegistry registry, Engine engine, ConsoleActionsComponent actionConsole) { |
||||
myRegistry = registry; |
||||
myActionConsole = actionConsole; |
||||
myEngine = engine; |
||||
} |
||||
|
||||
public void start() { |
||||
// myRegistry.registerAction(MainMenu.Edit.EDIT_ITEM, new EditPrimaryItemAction()); replaced in JC3
|
||||
myRegistry.registerAction(MainMenu.Edit.VIEW_PROBLEMS, new ShowItemProblemAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.DOWNLOAD, new DownloadItemAction(myEngine)); |
||||
myRegistry.registerAction(MainMenu.Edit.UPLOAD, new UploadItemAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.DISCARD, new DiscardLocalChangesAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.RENAME, new RenameAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.NEW_FOLDER, createCreateFolderAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.ADD_TO_FAVORITES, new AddRemoveFavoritesAction(true)); |
||||
myRegistry.registerAction(MainMenu.Edit.REMOVE_FROM_FAVORITES, new AddRemoveFavoritesAction(false)); |
||||
myRegistry.registerAction(MainMenu.Edit.TAG, new TagItemsAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.NEW_TAG, new NewTagAction(false)); |
||||
myRegistry.registerAction(MainMenu.Edit.COPY_ID_SUMMARY, new CopyIdAndSummaryAction()); |
||||
|
||||
myRegistry.registerAction(MainMenu.Search.NEW_QUERY, createCreateNewQueryAction()); |
||||
myRegistry.registerAction(MainMenu.Search.NEW_DISTRIBUTION, CreateDistribution.createAction()); |
||||
myRegistry.registerAction(MainMenu.Search.EXCLUDE_FROM_DISTRIBUTION, new ExcludeDisributionNodeAction()); |
||||
myRegistry.registerAction(MainMenu.Search.EDIT_QUERY, new EditQueryAction()); |
||||
myRegistry.registerAction(MainMenu.Search.RUN_QUERY, new RunQueryAction()); |
||||
myRegistry.registerAction(MainMenu.Search.RUN_LOCALLY, new ShowCachedResultAction()); |
||||
myRegistry.registerAction(MainMenu.Search.RELOAD_QUERY, new ReloadQueryFromServerAction()); |
||||
myRegistry.registerAction(MainMenu.Search.RUN_QUERY_IN_BROWSER, new OpenQueryInBrowserAction()); |
||||
myRegistry.registerAction(MainMenu.Search.STOP_QUERY, new StopQueryAction()); |
||||
myRegistry.registerAction(MainMenu.Search.CLOSE_CURRENT_TAB, TabActions.createCloseTabAction( |
||||
ExplorerComponentImpl.MAIN_TABS_MANAGER, "Close Current Ta&b", MainMenu.Search.CLOSE_CURRENT_TAB)); |
||||
myRegistry.registerAction(MainMenu.Search.SELECT_NEXT_TAB, TabActions.createForwardTabAction( |
||||
ExplorerComponentImpl.MAIN_TABS_MANAGER, "Select Next Tab", MainMenu.Search.SELECT_NEXT_TAB)); |
||||
myRegistry.registerAction(MainMenu.Search.SELECT_PREV_TAB, TabActions.createBackwardTabAction( |
||||
ExplorerComponentImpl.MAIN_TABS_MANAGER, "Select Previous Tab", MainMenu.Search.SELECT_PREV_TAB)); |
||||
myRegistry.registerAction(MainMenu.Search.OPEN_ITEM_IN_BROWSER, new ViewItemInBrowserAction()); |
||||
|
||||
myRegistry.registerAction(MainMenu.Search.OPEN_ITEM_IN_TAB, new OpenInNewTabAction()); |
||||
myRegistry.registerAction(MainMenu.Search.OPEN_ITEM_IN_FRAME, new OpenInNewFrameAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.SORT_NODES, new SortNodesAction()); |
||||
|
||||
myRegistry.registerAction(MainMenu.File.SHOW_CONNECTION_INFO, ShowConnectionInfo.MAIN); |
||||
myRegistry.registerAction(MainMenu.File.EDIT_CONNECTION, EditConnectionAction.MAIN); |
||||
myRegistry.registerAction(MainMenu.File.RETRY_INITIALIZATION, RetryInitializationAction.MAIN); |
||||
myRegistry.registerAction(MainMenu.File.REMOVE_CONNECTION, RemoveConnectionsAction.create()); |
||||
myRegistry.registerAction(MainMenu.File.UPLOAD_ALL_CHANGES, new UploadLocalChangesAction()); |
||||
|
||||
myRegistry.registerAction(MainMenu.Search.TOP_DUPLICATE_QUERY, new TemporaryDuplicateAction()); |
||||
// myRegistry.registerAction(MainMenu.Search.EXPAND_DISTRIBUTION, new ExpandLazyDistributionAction());
|
||||
// myRegistry.registerAction(MainMenu.Tools.VIEW_NOTE, new ViewNoteAction());
|
||||
|
||||
// myRegistry.registerAction(MainMenu.Edit.DOWNLOAD_DETAILS, new DownloadDetailsAction());
|
||||
// myRegistry.registerAction(MainMenu.Edit.DOWNLOAD_ATTACHMENTS, new DownloadAttachmentsAction());
|
||||
|
||||
myRegistry.registerAction(MainMenu.Windows.FOCUS_NAVIGATION_TREE, |
||||
new FocusComponentRoleAction(NavigationTree.NAVIGATION_JTREE)); |
||||
myRegistry.registerAction(MainMenu.Windows.FOCUS_TABLE, |
||||
new FocusComponentRoleAction(TableController.TABLE_COMPONENT)); |
||||
myRegistry.registerAction(MainMenu.Windows.FOCUS_ITEM_VIEWER, |
||||
new FocusComponentRoleAction(TableController.ARTIFACT_VIWER_COMPONENT)); |
||||
CycleFocusComponentRoleAction cycleFocusAction = new CycleFocusComponentRoleAction(NavigationTree.NAVIGATION_JTREE, |
||||
TableController.TABLE_COMPONENT, TableController.ARTIFACT_VIWER_COMPONENT); |
||||
myRegistry.registerAction(MainMenu.Windows.CYCLE_FOCUS, cycleFocusAction); |
||||
myRegistry.registerAction(MainMenu.Windows.CYCLE_FOCUS_BACK, cycleFocusAction.backwardAction()); |
||||
myRegistry.registerAction(MainMenu.Windows.SHOW_DISTRIBUTION_TABLE, new ShowHideDistributionTableAction()); |
||||
myRegistry.registerAction(MainMenu.Windows.SHOW_NAVIGATION_AREA, ShowHideNavigationAreaAction.INSTANCE); |
||||
myRegistry.registerAction(TabActions.EXPAND_SHRINK_TABS_ID, ShowHideNavigationAreaAction.INSTANCE); |
||||
|
||||
myRegistry.registerAction(MainMenu.Tools.REMOVE_BAD_ISSUE, new RemovePrimaryItemAction(myEngine)); |
||||
myRegistry.registerAction(MainMenu.Tools.IMPORT_TAGS, new ImportTagsAction()); |
||||
myRegistry.registerAction(MainMenu.Tools.SYSTEM_PROPERTIES, new SystemPropertiesAction()); |
||||
|
||||
myRegistry.registerAction(MainMenu.Search.FIND, new ShowHighlightPanelAction()); |
||||
|
||||
if (Env.getBoolean(GlobalProperties.INTERNAL_ACTIONS)) { |
||||
myRegistry.registerAction(MainMenu.Edit.VIEW_CHANGES, new ViewChangesAction()); |
||||
myRegistry.registerAction(MainMenu.Edit.VIEW_ATTRIBUTES, new ViewAttributesAction()); |
||||
myRegistry.registerAction(MainMenu.Tools.VIEW_ITEM_ATTRIBUTES, ViewAttributesAction.createViewItem()); |
||||
myRegistry.registerAction(MainMenu.Edit.VIEW_SHADOWS, new ViewShadowsAction()); |
||||
myRegistry.registerAction(MainMenu.Tools.OPERATION_CONSOLE, ConsoleActionsComponent.OPEN_CONSOLE); |
||||
} |
||||
|
||||
registerHideEmptyQueriesActions(myRegistry); |
||||
|
||||
ToggleLiveModeAction.registerActions(myRegistry); |
||||
RefreshArtifactsTableAction.registerActions(myRegistry); |
||||
// CreateItemHelper.registerActions(myRegistry);
|
||||
// MergePrimaryItemAction.registerActions(myRegistry);
|
||||
CommentsController.registerActions(myRegistry); |
||||
// PrimaryItemEditor.registerActions(myRegistry);
|
||||
// NewArtifactForm.registerActions(myRegistry);
|
||||
// EditorContent.registerActions(myRegistry);
|
||||
registerConsoleActions(); |
||||
} |
||||
|
||||
private void registerConsoleActions() { |
||||
// Navigation tree/connection related actions
|
||||
myActionConsole.addGroup(new ActionGroup.InContext("Connection", GenericNode.NAVIGATION_NODE, |
||||
MainMenu.File.EDIT_CONNECTION, MainMenu.File.SHOW_CONNECTION_INFO, MainMenu.File.RETRY_INITIALIZATION, MainMenu.File.REMOVE_CONNECTION, |
||||
MainMenu.File.DOWNLOAD_CHANGES_QUICK_POPUP, MainMenu.File.RELOAD_CONFIGURATION_POPUP)); |
||||
} |
||||
|
||||
private void registerHideEmptyQueriesActions(ActionRegistry registry) { |
||||
registry.registerAction(MainMenu.Search.HIDE_EMPTY_QUERIES_ON, new HideEmptyQueriesChooserAction("Hide", |
||||
"Hide sub-queries that would show zero " + Terms.ref_artifacts, Boolean.TRUE)); |
||||
registry.registerAction(MainMenu.Search.HIDE_EMPTY_QUERIES_OFF, new HideEmptyQueriesChooserAction("Show", |
||||
"Do not hide sub-queries that would show zero " + Terms.ref_artifacts, Boolean.FALSE)); |
||||
} |
||||
|
||||
private CreateChildNodeAction createCreateNewQueryAction() { |
||||
CreateChildNodeAction action = |
||||
CreateChildNodeAction.create(Icons.ACTION_CREATE_NEW_QUERY, TreeNodeFactory.NodeType.QUERY); |
||||
action.setDefaultPresentation(PresentationKey.NAME, L.actionName("New &" + Terms.Query)); |
||||
action.setDefaultPresentation(PresentationKey.SHORT_DESCRIPTION, L.tooltip("Create a new " + Terms.query)); |
||||
return action; |
||||
} |
||||
|
||||
private CreateChildNodeAction createCreateFolderAction() { |
||||
CreateChildNodeAction action = CreateChildNodeAction.create(TreeNodeFactory.NodeType.FOLDER); |
||||
action.setDefaultPresentation(PresentationKey.NAME, L.actionName("&New " + Terms.Folder)); |
||||
action.setDefaultPresentation(PresentationKey.SHORT_DESCRIPTION, L.tooltip("Create a new " + Terms.folder)); |
||||
return action; |
||||
} |
||||
|
||||
public static <T extends GenericNode> T getSingleNavigationNode(ActionContext context, DataRole<T> role) |
||||
throws CantPerformException |
||||
{ |
||||
try { |
||||
return context.getSourceObject(role); |
||||
} catch (CantPerformException e) { |
||||
GenericNode node = context.getSourceObject(GenericNode.NAVIGATION_NODE); |
||||
if (role.matches(node)) |
||||
return (T) node; |
||||
throw new CantPerformException("No role: " + role + " object: " + node); |
||||
} |
||||
} |
||||
|
||||
|
||||
public void stop() { |
||||
} |
||||
|
||||
private static class HideEmptyQueriesChooserAction extends SimpleAction { |
||||
private Boolean myValue; |
||||
|
||||
public HideEmptyQueriesChooserAction(String name, String description, Boolean value) { |
||||
super(name); |
||||
< |