diff --git a/core/src/main/java/org/apache/hop/core/gui/plugin/GuiPlugin.java b/core/src/main/java/org/apache/hop/core/gui/plugin/GuiPlugin.java index 739770cd756..22da0447422 100644 --- a/core/src/main/java/org/apache/hop/core/gui/plugin/GuiPlugin.java +++ b/core/src/main/java/org/apache/hop/core/gui/plugin/GuiPlugin.java @@ -30,5 +30,7 @@ public @interface GuiPlugin { String id() default ""; // defaults to class name + String name() default ""; + String description() default ""; } diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_de_DE.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_de_DE.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_de_DE.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_AR.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_AR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_AR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_ES.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_ES.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_es_ES.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_fr_FR.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_fr_FR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_fr_FR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_it_IT.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_it_IT.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_it_IT.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ja_JP.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ja_JP.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ja_JP.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ko_KR.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ko_KR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_ko_KR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_nl_NL.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_nl_NL.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_nl_NL.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_no_NO.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_no_NO.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_no_NO.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pl_PL.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pl_PL.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pl_PL.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_BR.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_BR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_BR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_PT.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_PT.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_pt_PT.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_zh_CN.properties b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_zh_CN.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/misc/git/src/main/resources/org/apache/hop/git/config/messages/messages_zh_CN.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_de_DE.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_de_DE.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_de_DE.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_AR.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_AR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_AR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_ES.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_ES.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_es_ES.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_fr_FR.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_fr_FR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_fr_FR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_it_IT.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_it_IT.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_it_IT.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ja_JP.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ja_JP.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ja_JP.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ko_KR.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ko_KR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_ko_KR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_nl_NL.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_nl_NL.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_nl_NL.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_no_NO.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_no_NO.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_no_NO.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pl_PL.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pl_PL.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pl_PL.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_BR.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_BR.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_BR.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_PT.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_PT.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_pt_PT.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_zh_CN.properties b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_zh_CN.properties new file mode 100644 index 00000000000..1692cdb4726 --- /dev/null +++ b/plugins/transforms/repeatfields/src/main/resources/org/apache/hop/pipeline/transforms/repeatfields/messages/messages_zh_CN.properties @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + diff --git a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java index d14d9567279..289fd2ede47 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java +++ b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java @@ -75,6 +75,7 @@ public class PropsUi extends Props { private static final String USE_DOUBLE_CLICK_ON_CANVAS = "UseDoubleClickOnCanvas"; private static final String DRAW_BORDER_AROUND_CANVAS_NAMES = "DrawBorderAroundCanvasNames"; private static final String USE_GLOBAL_FILE_BOOKMARKS = "UseGlobalFileBookmarks"; + private static final String RELOAD_FILES_ON_CHANGE = "ReloadFilesOnChange"; private static final String DARK_MODE = "DarkMode"; private static final String GLOBAL_ZOOMFACTOR = "GlobalZoomFactor"; private static final String MAX_EXECUTION_LOGGING_TEXT_SIZE = "MaxExecutionLoggingTextSize"; @@ -403,6 +404,15 @@ public boolean openLastFile() { return !NO.equalsIgnoreCase(open); } + public void setReloadingFilesOnChange(boolean reload) { + setProperty(RELOAD_FILES_ON_CHANGE, reload ? YES : NO); + } + + public boolean isReloadingFilesOnChange() { + String reload = getProperty(RELOAD_FILES_ON_CHANGE); + return YES.equalsIgnoreCase(reload); // Default = OFF + } + public void setAutoSave(boolean autosave) { setProperty(STRING_AUTO_SAVE, autosave ? YES : NO); } diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/ConfigurationDialog.java b/ui/src/main/java/org/apache/hop/ui/core/dialog/ConfigurationDialog.java index 6a294836d45..5c8138949f5 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/dialog/ConfigurationDialog.java +++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/ConfigurationDialog.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import lombok.Setter; import org.apache.hop.IExecutionConfiguration; import org.apache.hop.base.AbstractMeta; import org.apache.hop.core.Const; @@ -56,7 +57,7 @@ public abstract class ConfigurationDialog extends Dialog { protected AbstractMeta abstractMeta; - protected IExecutionConfiguration configuration; + @Setter protected IExecutionConfiguration configuration; protected TableView wVariables; protected boolean retval; protected Shell shell; @@ -192,13 +193,6 @@ protected void getParamsData() { wParams.optWidth(true); } - /** - * @param configuration the configuration to set - */ - public void setConfiguration(IExecutionConfiguration configuration) { - this.configuration = configuration; - } - protected void mainLayout(String shellTitle, Image img) { shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX); PropsUi.setLook(shell); diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/MessageDialogWithToggle.java b/ui/src/main/java/org/apache/hop/ui/core/dialog/MessageDialogWithToggle.java index 3321c3dfdab..7bf05a8b72d 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/dialog/MessageDialogWithToggle.java +++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/MessageDialogWithToggle.java @@ -144,6 +144,7 @@ public int open() { }); } BaseTransformDialog.positionBottomButtons(shell, buttons, margin, wToggle); + shell.setDefaultButton(buttons[0]); BaseTransformDialog.setSize(shell); shell.pack(); diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java index 50c472cf883..d3dcf2a520a 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java +++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java @@ -159,14 +159,15 @@ private Control addCompositeWidgets( GuiElementType elementType = guiElements.getType(); - // Add the label on the left-hand side... - // For metadata, the label is handled in the meta selection line widget below + // Add the label above the control (left-aligned) + // For metadata, checkbox, button, and link, the label is handled in the widget itself // if (StringUtils.isNotEmpty(guiElements.getLabel()) && elementType != GuiElementType.METADATA && elementType != GuiElementType.BUTTON - && elementType != GuiElementType.LINK) { - label = new Label(parent, SWT.RIGHT | SWT.SINGLE); + && elementType != GuiElementType.LINK + && elementType != GuiElementType.CHECKBOX) { + label = new Label(parent, SWT.LEFT); PropsUi.setLook(label); label.setText(Const.NVL(guiElements.getLabel(), "")); if (StringUtils.isNotEmpty(guiElements.getToolTip())) { @@ -174,12 +175,12 @@ private Control addCompositeWidgets( } FormData fdLabel = new FormData(); fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); if (lastControl == null) { fdLabel.top = new FormAttachment(0, PropsUi.getMargin()); } else { fdLabel.top = new FormAttachment(lastControl, PropsUi.getMargin() + extraVerticalMargin); } - fdLabel.right = new FormAttachment(props.getMiddlePct(), -PropsUi.getMargin()); label.setLayoutData(fdLabel); labelsMap.put(guiElements.getId(), label); } @@ -434,11 +435,17 @@ private Control getCheckboxControl( Control control; Button button = new Button(parent, SWT.CHECK | SWT.LEFT); PropsUi.setLook(button); + // Set the label text on the checkbox itself + button.setText(Const.NVL(guiElements.getLabel(), "")); + if (StringUtils.isNotEmpty(guiElements.getToolTip())) { + button.setToolTipText(guiElements.getToolTip()); + } widgetsMap.put(guiElements.getId(), button); addModifyListener(button, guiElements.getId()); control = button; - layoutControlBetweenLabelAndRightControl(props, lastControl, label, control, null); + // Checkboxes are laid out below the last control, full width + layoutControlBelowLast(props, lastControl, control); return control; } @@ -563,7 +570,8 @@ private void layoutControlOnRight( FormData fdControl = new FormData(); fdControl.right = new FormAttachment(100, 0); if (label != null) { - fdControl.top = new FormAttachment(label, 0, SWT.CENTER); + // Control goes on the right, aligned with the row below the label + fdControl.top = new FormAttachment(label, PropsUi.getMargin() / 2); } else { if (lastControl != null) { fdControl.top = new FormAttachment(lastControl, PropsUi.getMargin()); @@ -578,19 +586,21 @@ private void layoutControlBetweenLabelAndRightControl( PropsUi props, Control lastControl, Label label, Control control, Control rightControl) { FormData fdControl = new FormData(); if (label != null) { - fdControl.left = new FormAttachment(props.getMiddlePct(), 0); + // Control goes below the label, full width (or next to right control) + fdControl.left = new FormAttachment(0, 0); if (rightControl == null) { fdControl.right = new FormAttachment(100, 0); } else { - fdControl.right = new FormAttachment(rightControl, -5); + fdControl.right = new FormAttachment(rightControl, -PropsUi.getMargin()); } - fdControl.top = new FormAttachment(label, 0, SWT.CENTER); + fdControl.top = new FormAttachment(label, PropsUi.getMargin() / 2); } else { - fdControl.left = new FormAttachment(props.getMiddlePct(), 0); + // No label, control goes full width + fdControl.left = new FormAttachment(0, 0); if (rightControl == null) { fdControl.right = new FormAttachment(100, 0); } else { - fdControl.right = new FormAttachment(rightControl, -5); + fdControl.right = new FormAttachment(rightControl, -PropsUi.getMargin()); } if (lastControl != null) { fdControl.top = new FormAttachment(lastControl, PropsUi.getMargin()); diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java index cf0b1a9675c..80611e0814a 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java @@ -29,6 +29,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.vfs2.FileObject; import org.apache.hop.core.RowMetaAndData; +import org.apache.hop.core.config.HopConfig; import org.apache.hop.core.exception.HopException; import org.apache.hop.core.row.IRowMeta; import org.apache.hop.core.row.RowMeta; @@ -37,11 +38,14 @@ import org.apache.hop.core.vfs.HopVfs; import org.apache.hop.history.AuditEvent; import org.apache.hop.history.AuditManager; +import org.apache.hop.i18n.BaseMessages; import org.apache.hop.pipeline.PipelineMeta; import org.apache.hop.pipeline.PipelineSvgPainter; +import org.apache.hop.ui.core.PropsUi; import org.apache.hop.ui.core.dialog.BaseDialog; import org.apache.hop.ui.core.dialog.ErrorDialog; import org.apache.hop.ui.core.dialog.MessageBox; +import org.apache.hop.ui.core.dialog.MessageDialogWithToggle; import org.apache.hop.ui.core.dialog.SelectRowDialog; import org.apache.hop.ui.core.gui.HopNamespace; import org.apache.hop.ui.hopgui.HopGui; @@ -59,6 +63,7 @@ public class HopGuiFileDelegate { + private static final Class PKG = BaseDialog.class; public static final String CONST_ERROR = "Error"; private final HopGui hopGui; @@ -266,6 +271,47 @@ public boolean fileExit() { if (!saveGuardAllFiles()) { return false; } + + // Check if we should ask the user for confirmation before exiting + // + PropsUi props = PropsUi.getInstance(); + if (props.showExitWarning()) { + String title = BaseMessages.getString(PKG, "EnterOptionsDialog.AskOnExit.Label"); + String message = BaseMessages.getString(PKG, "EnterOptionsDialog.AskOnExit.ConfirmMessage"); + String toggleLabel = + BaseMessages.getString(PKG, "EnterOptionsDialog.AskOnExit.DoNotAskAgain"); + String[] buttonLabels = { + BaseMessages.getString(PKG, "System.Button.Yes"), + BaseMessages.getString(PKG, "System.Button.No") + }; + + MessageDialogWithToggle dialog = + new MessageDialogWithToggle( + hopGui.getShell(), + title, + message, + SWT.ICON_QUESTION, + buttonLabels, + toggleLabel, + false); + int answer = dialog.open(); + + // If user checked "Do not ask this again", disable the exit warning + if (dialog.getToggleState()) { + props.setExitWarningShown(false); + try { + HopConfig.getInstance().saveToFile(); + } catch (Exception e) { + new ErrorDialog(hopGui.getActiveShell(), CONST_ERROR, "Error saving configuration", e); + } + } + + // Return code 0 is Yes, 1 is No + if (answer != 0) { + return false; // User chose not to exit + } + } + // Also save all the open files in a list // hopGui.auditDelegate.writeLastOpenFiles(); diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileRefreshDelegate.java b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileRefreshDelegate.java index 03450d82251..0f1d838cf69 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileRefreshDelegate.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileRefreshDelegate.java @@ -80,6 +80,9 @@ public void fileDeleted(FileChangeEvent arg0) throws Exception { // saved in the file system // public void register(String fileName, IHopFileTypeHandler fileTypeHandler) { + if (!hopGui.getProps().isReloadingFilesOnChange()) { + return; + } try { FileObject file = HopVfs.getFileObject(fileName); fileMonitor.addFile(file); @@ -91,6 +94,9 @@ public void register(String fileName, IHopFileTypeHandler fileTypeHandler) { } public void remove(String fileName) { + if (!hopGui.getProps().isReloadingFilesOnChange()) { + return; + } try { FileObject file = HopVfs.getFileObject(fileName); fileName = file.getPublicURIString(); diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/ConfigurationPerspective.java b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/ConfigurationPerspective.java index 2b833b79049..067310b558a 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/ConfigurationPerspective.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/ConfigurationPerspective.java @@ -19,26 +19,50 @@ import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import lombok.Getter; import org.apache.hop.core.Props; import org.apache.hop.core.gui.plugin.GuiPlugin; import org.apache.hop.core.gui.plugin.GuiRegistry; import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut; import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut; import org.apache.hop.core.gui.plugin.tab.GuiTabItem; +import org.apache.hop.core.logging.LogChannel; import org.apache.hop.core.search.ISearchable; +import org.apache.hop.i18n.BaseMessages; import org.apache.hop.ui.core.PropsUi; import org.apache.hop.ui.core.dialog.ErrorDialog; +import org.apache.hop.ui.core.gui.GuiResource; +import org.apache.hop.ui.core.widget.TableView; import org.apache.hop.ui.hopgui.HopGui; import org.apache.hop.ui.hopgui.context.IGuiContextHandler; import org.apache.hop.ui.hopgui.perspective.HopPerspectivePlugin; import org.apache.hop.ui.hopgui.perspective.IHopPerspective; +import org.apache.hop.ui.hopgui.perspective.configuration.tabs.ConfigPluginOptionsTab; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; @HopPerspectivePlugin( id = "160-HopConfigurationPerspective", @@ -49,15 +73,20 @@ @GuiPlugin(description = "i18n::HopConfigurationPerspective.GuiPlugin.Description") public class ConfigurationPerspective implements IHopPerspective { + private static final Class PKG = ConfigurationPerspective.class; public static final String CONFIG_PERSPECTIVE_TABS = "ConfigurationPerspective.Tabs.ID"; private HopGui hopGui; private Composite composite; public CTabFolder configTabs; - private static ConfigurationPerspective instance; - - public static ConfigurationPerspective getInstance() { - return instance; - } + private Tree categoryTree; + private Map categoryTabs = new HashMap<>(); + private List tabInstances = new ArrayList<>(); // Store tab instances for refreshing + private Map originalBackgrounds = new HashMap<>(); + private Map originalFonts = new HashMap<>(); + private List highlightedControls = new ArrayList<>(); + private String currentSearchText = ""; // Track current search for re-applying highlights + private Color highlightColor; // Custom neutral highlight color + @Getter private static ConfigurationPerspective instance; public ConfigurationPerspective() { instance = this; @@ -82,7 +111,30 @@ public void activate() { @Override public void perspectiveActivated() { - // Do nothing + // Reload values in all tabs when perspective is activated + // This ensures that changes made outside the dialog (e.g., "Do not ask this again") are + // reflected + reloadAllTabValues(); + } + + /** Reload values in all tabs that support it */ + private void reloadAllTabValues() { + for (Object tabInstance : tabInstances) { + try { + // Use reflection to call reloadValues() if it exists + java.lang.reflect.Method reloadMethod = tabInstance.getClass().getMethod("reloadValues"); + reloadMethod.invoke(tabInstance); + } catch (NoSuchMethodException e) { + // Tab doesn't have a reloadValues method, skip it + } catch (Exception e) { + LogChannel.GENERAL.logError( + "Error message", + "Error reloading values for tab " + + tabInstance.getClass().getName() + + ": " + + e.getMessage()); + } + } } @Override @@ -95,22 +147,130 @@ public void initialize(HopGui hopGui, Composite parent) { this.hopGui = hopGui; composite = new Composite(parent, SWT.NONE); - composite.setLayout(new FillLayout()); PropsUi.setLook(composite); + FormLayout formLayout = new FormLayout(); + formLayout.marginWidth = 0; + formLayout.marginHeight = 0; + composite.setLayout(formLayout); + + // Create a neutral highlight color (light blue-gray) + highlightColor = GuiResource.getInstance().getColorLightBlue(); + + // Search box at the top + Text searchBox = + new Text(composite, SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL | SWT.BORDER); + PropsUi.setLook(searchBox); + searchBox.setMessage(BaseMessages.getString(PKG, "HopConfigurationperspective.Search.Text")); + FormData fdSearchBox = new FormData(); + fdSearchBox.left = new FormAttachment(0, PropsUi.getMargin()); + fdSearchBox.top = new FormAttachment(0, PropsUi.getMargin()); + fdSearchBox.right = new FormAttachment(100, -PropsUi.getMargin()); + searchBox.setLayoutData(fdSearchBox); + searchBox.addListener(SWT.Modify, e -> filterSettings(searchBox.getText())); + + // SashForm for tree on left and content on right + SashForm sashForm = new SashForm(composite, SWT.HORIZONTAL | SWT.SMOOTH); + PropsUi.setLook(sashForm); + FormData fdSashForm = new FormData(); + fdSashForm.left = new FormAttachment(0, 0); + fdSashForm.top = new FormAttachment(searchBox, PropsUi.getMargin()); + fdSashForm.right = new FormAttachment(100, 0); + fdSashForm.bottom = new FormAttachment(100, 0); + sashForm.setLayoutData(fdSashForm); + + // Left side: Tree navigation + Composite treeComposite = new Composite(sashForm, SWT.BORDER); + PropsUi.setLook(treeComposite); + treeComposite.setLayout(new FillLayout()); - configTabs = new CTabFolder(composite, SWT.BORDER); + categoryTree = new Tree(treeComposite, SWT.SINGLE | SWT.V_SCROLL); + PropsUi.setLook(categoryTree, Props.WIDGET_STYLE_TREE); + categoryTree.addListener( + SWT.Selection, + e -> { + TreeItem[] selection = categoryTree.getSelection(); + if (selection.length > 0) { + TreeItem selectedItem = selection[0]; + + // Check if this is a plugin sub-item + Boolean isPlugin = (Boolean) selectedItem.getData("isPlugin"); + if (isPlugin != null && isPlugin) { + // This is a plugin sub-item + String pluginName = (String) selectedItem.getData("pluginName"); + + // First, show the Plugins tab (with highlighting) + TreeItem parentItem = selectedItem.getParentItem(); + if (parentItem != null) { + showCategory(parentItem.getText(), false); // Don't apply highlighting yet + + // Then show the specific plugin settings + CTabItem pluginTab = categoryTabs.get(parentItem.getText()); + if (pluginTab != null) { + Control tabControl = pluginTab.getControl(); + if (tabControl instanceof Composite tabComposite) { + Composite pluginComposite = findPluginComposite(tabComposite); + if (pluginComposite != null) { + ConfigPluginOptionsTab.showConfigPluginSettings(pluginName, pluginComposite); + + // Re-apply highlighting if there's an active search + if (currentSearchText != null && !currentSearchText.trim().isEmpty()) { + applyHighlightingToCurrentTab(); + } + } + } + } + } + } else { + // Regular category item (with highlighting) + String categoryName = selectedItem.getText(); + showCategory(categoryName, true); + + // If it's the Plugins category (parent), show instructions + if (categoryName.equalsIgnoreCase("Plugins") || categoryName.contains("plugin")) { + CTabItem pluginTab = categoryTabs.get(categoryName); + if (pluginTab != null) { + Control tabControl = pluginTab.getControl(); + if (tabControl instanceof Composite tabComposite) { + Composite pluginComposite = findPluginComposite(tabComposite); + if (pluginComposite != null) { + ConfigPluginOptionsTab.showPluginInstructions(pluginComposite); + } + } + } + } + } + } + }); + + // Right side: Content area - just use the tab folder directly + configTabs = new CTabFolder(sashForm, SWT.BORDER); PropsUi.setLook(configTabs, Props.WIDGET_STYLE_TAB); - configTabs.setMaximized(true); + // Hide the tab bar, we'll use the tree for navigation + configTabs.setTabHeight(0); + + // Load all setting tabs + loadSettingCategories(); + + sashForm.setWeights(20, 80); + + composite.layout(); + } + + private void loadSettingCategories() { GuiRegistry guiRegistry = GuiRegistry.getInstance(); List tabsList = guiRegistry.getGuiTabsMap().get(CONFIG_PERSPECTIVE_TABS); if (tabsList != null) { tabsList.sort(Comparator.comparing(GuiTabItem::getId)); + + // Invoke all tab methods to populate configTabs for (GuiTabItem tabItem : tabsList) { try { Object object = tabItem.getMethod().getDeclaringClass().getConstructor().newInstance(); tabItem.getMethod().invoke(object, configTabs); + // Store the tab instance so we can reload its values later + tabInstances.add(object); } catch (Exception e) { new ErrorDialog( hopGui.getShell(), @@ -121,17 +281,510 @@ public void initialize(HopGui hopGui, Composite parent) { e); } } - if (configTabs.getItemCount() > 0) { - configTabs.setSelection(0); + + // Now create tree items for each tab + CTabItem[] allTabs = configTabs.getItems(); + for (CTabItem tab : allTabs) { + String categoryName = tab.getText(); + + // Check if this is the Plugins tab - we'll handle it specially + if (categoryName.equalsIgnoreCase("Plugins") || categoryName.contains("plugin")) { + // Create parent Plugins tree item + TreeItem pluginsTreeItem = new TreeItem(categoryTree, SWT.NONE); + pluginsTreeItem.setText(categoryName); + if (tab.getImage() != null) { + pluginsTreeItem.setImage(tab.getImage()); + } + + // Store the tab for the parent item + categoryTabs.put(categoryName, tab); + + // Try to find plugin sub-items by looking at the tab's control + // If it's a ConfigPluginOptionsTab, it has a list of plugins + expandPluginsIntoTree(tab, pluginsTreeItem); + } else { + // Regular tree item + TreeItem treeItem = new TreeItem(categoryTree, SWT.NONE); + treeItem.setText(categoryName); + if (tab.getImage() != null) { + treeItem.setImage(tab.getImage()); + } + + // Store mapping from category name to tab + categoryTabs.put(categoryName, tab); + } + } + + // Select first item by default + if (categoryTree.getItemCount() > 0) { + categoryTree.setSelection(categoryTree.getItem(0)); + showCategory(categoryTree.getItem(0).getText()); + } + } + } + + private void expandPluginsIntoTree(CTabItem pluginTab, TreeItem parentItem) { + // Store the parent tab mapping + categoryTabs.put(parentItem.getText(), pluginTab); + + // Get plugin names from ConfigPluginOptionsTab + java.util.Set pluginNames = ConfigPluginOptionsTab.getPluginNames(); + + if (pluginNames != null && !pluginNames.isEmpty()) { + // Create a tree item for each plugin + for (String pluginName : pluginNames) { + TreeItem pluginItem = new TreeItem(parentItem, SWT.NONE); + pluginItem.setText(pluginName); + + // Store the plugin name for later use + pluginItem.setData("pluginName", pluginName); + pluginItem.setData("isPlugin", true); + } + + // Expand the parent by default + parentItem.setExpanded(true); + } + } + + private void showCategory(String categoryName) { + showCategory(categoryName, true); + } + + private void showCategory(String categoryName, boolean applyHighlighting) { + // Find the corresponding tab + CTabItem tab = categoryTabs.get(categoryName); + if (tab != null && !tab.isDisposed()) { + configTabs.setSelection(tab); + configTabs.layout(); + + // Re-apply highlighting if there's an active search and it's requested + if (applyHighlighting && currentSearchText != null && !currentSearchText.trim().isEmpty()) { + applyHighlightingToCurrentTab(); + } + } + } + + private void filterSettings(String searchText) { + // Store the current search text + currentSearchText = searchText != null ? searchText : ""; + + // Clear previous highlights + clearHighlights(); + + if (searchText == null || searchText.trim().isEmpty()) { + // Show all categories + for (TreeItem item : categoryTree.getItems()) { + item.setForeground(null); + // Reset children too + for (TreeItem child : item.getItems()) { + child.setForeground(null); + } + } + return; + } + + final String lowerSearch = searchText.toLowerCase(); + Color grayColor = hopGui.getDisplay().getSystemColor(SWT.COLOR_GRAY); + + TreeItem firstMatch = null; + + // Search through all categories and their content + for (TreeItem item : categoryTree.getItems()) { + String categoryName = item.getText().toLowerCase(); + boolean categoryMatches = categoryName.contains(lowerSearch); + boolean hasMatchingContent = false; + + // Check if category name matches + if (categoryMatches) { + item.setForeground(null); + if (firstMatch == null) { + firstMatch = item; + } + } + + // Search within the category's content + CTabItem tab = categoryTabs.get(item.getText()); + if (tab != null && !tab.isDisposed()) { + Control control = tab.getControl(); + if (control instanceof Composite controlComposite) { + List matches = searchInComposite(controlComposite, lowerSearch); + if (!matches.isEmpty()) { + hasMatchingContent = true; + if (firstMatch == null) { + firstMatch = item; + } + + // Highlight the matched controls + for (Control match : matches) { + highlightControl(match, highlightColor); + } + } + } + } + + // Handle plugin sub-items - need to search their content too + boolean hasMatchingPlugin = false; + for (TreeItem childItem : item.getItems()) { + String pluginName = childItem.getText(); + String pluginNameLower = pluginName.toLowerCase(); + boolean pluginMatches = pluginNameLower.contains(lowerSearch); + + // Also search within the plugin's content + boolean pluginContentMatches = searchInPluginContent(pluginName, lowerSearch); + + if (pluginMatches || pluginContentMatches) { + childItem.setForeground(null); + hasMatchingPlugin = true; + if (firstMatch == null) { + firstMatch = childItem; + } + } else { + childItem.setForeground(grayColor); + } + } + + // Set tree item appearance based on matches + if (categoryMatches || hasMatchingContent || hasMatchingPlugin) { + item.setForeground(null); + item.setExpanded(true); // Expand if has matching children + } else { + item.setForeground(grayColor); + } + } + + // Navigate to first match + if (firstMatch != null) { + categoryTree.setSelection(firstMatch); + categoryTree.showSelection(); + + // Trigger selection to show the content + Event event = new Event(); + event.item = firstMatch; + categoryTree.notifyListeners(SWT.Selection, event); + + // After navigation, search and highlight in the displayed content + // Use timerExec with delay to ensure plugin content is fully loaded + hopGui + .getDisplay() + .timerExec( + 100, // 100ms delay to allow plugin content to render + () -> { + // Get the currently displayed tab + CTabItem selectedTab = configTabs.getSelection(); + if (selectedTab != null && !selectedTab.isDisposed()) { + Control control = selectedTab.getControl(); + if (control instanceof Composite controlComposite && !control.isDisposed()) { + List matches = searchInComposite(controlComposite, lowerSearch); + for (Control match : matches) { + if (!match.isDisposed()) { + highlightControl(match, highlightColor); + } + } + // Force a redraw to show highlights + if (!control.isDisposed()) { + control.redraw(); + } + } + } + }); + } + } + + private boolean searchInPluginContent(String pluginName, String searchText) { + // Create a temporary composite to load the plugin's settings + Composite tempComposite = new Composite(composite, SWT.NONE); + tempComposite.setLayout(new FillLayout()); + + try { + // Load the plugin settings into the temp composite + ConfigPluginOptionsTab.showConfigPluginSettings(pluginName, tempComposite); + + // Force layout to ensure all widgets are created + tempComposite.layout(true, true); + + // Search within the composite + List matches = searchInComposite(tempComposite, searchText); + + // Clean up + tempComposite.dispose(); + + return !matches.isEmpty(); + } catch (Exception e) { + // If there's an error loading the plugin, assume no match + if (!tempComposite.isDisposed()) { + tempComposite.dispose(); + } + return false; + } + } + + private List searchInComposite(Composite composite, String searchText) { + List matches = new ArrayList<>(); + + if (composite == null || composite.isDisposed()) { + return matches; + } + + // Don't match anything if search text is empty + if (searchText == null || searchText.trim().isEmpty()) { + return matches; + } + + try { + for (Control control : composite.getChildren()) { + if (control == null || control.isDisposed()) { + continue; + } + + boolean controlMatches = false; + + // Check labels + if (control instanceof Label labelControl) { + Label label = labelControl; + String text = label.getText(); + if (text != null && text.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + } + } + // Check text fields + else if (control instanceof Text textControl) { + Text text = textControl; + String value = text.getText(); + if (value != null && value.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + } + } + // Check buttons + else if (control instanceof Button button) { + String text = button.getText(); + if (text != null && text.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + } + } + // Check tables (both SWT Table and Hop TableView) + else if (control instanceof Table table) { + // Search through all table items + for (TableItem item : table.getItems()) { + for (int i = 0; i < table.getColumnCount(); i++) { + String cellText = item.getText(i); + if (cellText != null && cellText.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + break; + } + } + if (controlMatches) { + break; + } + } + } + // Check Hop's TableView widget + else if (control instanceof TableView tableView) { + Table table = tableView.table; + // Search through all table items + for (TableItem item : table.getItems()) { + for (int i = 0; i < table.getColumnCount(); i++) { + String cellText = item.getText(i); + if (cellText != null && cellText.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + break; + } + } + if (controlMatches) { + break; + } + } + } + + // Check tooltips + if (!controlMatches) { + String tooltip = control.getToolTipText(); + if (tooltip != null && tooltip.toLowerCase().contains(searchText)) { + matches.add(control); + controlMatches = true; + } + } + + // Recursively search in child composites (including ScrolledComposite) + if (control instanceof ScrolledComposite sc) { + Control content = sc.getContent(); + if (content instanceof Composite composite1) { + matches.addAll(searchInComposite(composite1, searchText)); + } + } else if (control instanceof Composite composite1) { + matches.addAll(searchInComposite(composite1, searchText)); + } + } + } catch (Exception e) { + // Silently ignore any errors during search + } + + return matches; + } + + private void highlightControl(Control control, Color highlightColor) { + if (control == null || control.isDisposed()) { + return; + } + + // Special handling for tables - highlight matching rows + if (control instanceof Table table) { + highlightTableRows(table, highlightColor); + return; + } else if (control instanceof TableView tableView) { + highlightTableRows(tableView.table, highlightColor); + return; + } + + // Store original background if not already stored + originalBackgrounds.computeIfAbsent(control, k -> control.getBackground()); + + // Store original font if not already stored + originalBackgrounds.computeIfAbsent(control, k -> control.getBackground()); + + // Apply highlight + control.setBackground(highlightColor); + + // Make text bold if it's a label + if (control instanceof Label || control instanceof Text) { + Font currentFont = control.getFont(); + if (currentFont != null) { + FontData[] fontData = currentFont.getFontData(); + for (FontData fd : fontData) { + fd.setStyle(fd.getStyle() | SWT.BOLD); + } + Font boldFont = new Font(control.getDisplay(), fontData); + control.setFont(boldFont); + } + } + + highlightedControls.add(control); + } + + private void highlightTableRows(Table table, Color highlightColor) { + if (table == null + || table.isDisposed() + || currentSearchText == null + || currentSearchText.trim().isEmpty()) { + return; + } + + String lowerSearch = currentSearchText.toLowerCase(); + + // Search through all table items and highlight matching rows + for (TableItem item : table.getItems()) { + boolean rowMatches = false; + + // Check all columns in the row + for (int i = 0; i < table.getColumnCount(); i++) { + String cellText = item.getText(i); + if (cellText != null && cellText.toLowerCase().contains(lowerSearch)) { + rowMatches = true; + break; + } + } + + // Highlight the row if it matches + if (rowMatches) { + // Store original background using the table as key (we'll restore all rows when clearing) + if (!originalBackgrounds.containsKey(table)) { + originalBackgrounds.put(table, table.getBackground()); + } + + item.setBackground(highlightColor); + + // Track this table for cleanup + if (!highlightedControls.contains(table)) { + highlightedControls.add(table); + } + } + } + } + + private void clearHighlights() { + for (Control control : highlightedControls) { + if (!control.isDisposed()) { + // Special handling for tables - need to clear all row backgrounds + if (control instanceof Table table) { + for (TableItem item : table.getItems()) { + if (!item.isDisposed()) { + item.setBackground(null); // Reset to default + } + } + } else { + // Restore original background + Color originalBg = originalBackgrounds.get(control); + if (originalBg != null) { + control.setBackground(originalBg); + } + + // Restore original font + Font originalFont = originalFonts.get(control); + if (originalFont != null) { + // Dispose the bold font before restoring + Font currentFont = control.getFont(); + control.setFont(originalFont); + if (currentFont != null && !currentFont.equals(originalFont)) { + currentFont.dispose(); + } + } + } } } - configTabs.layout(); + highlightedControls.clear(); + originalBackgrounds.clear(); + originalFonts.clear(); + } + + private void applyHighlightingToCurrentTab() { + if (currentSearchText == null || currentSearchText.trim().isEmpty()) { + return; + } + + final String lowerSearch = currentSearchText.toLowerCase(); + + // Use timerExec with delay to ensure content is fully loaded (important for plugin content) + hopGui + .getDisplay() + .timerExec( + 100, // 100ms delay to allow plugin content to render + () -> { + // Clear existing highlights first + clearHighlights(); + + // Get the currently displayed tab + CTabItem selectedTab = configTabs.getSelection(); + if (selectedTab != null && !selectedTab.isDisposed()) { + Control control = selectedTab.getControl(); + if (control instanceof Composite composite1 && !control.isDisposed()) { + List matches = searchInComposite(composite1, lowerSearch); + for (Control match : matches) { + if (!match.isDisposed()) { + highlightControl(match, highlightColor); + } + } + // Force a redraw to show highlights + if (!control.isDisposed()) { + control.redraw(); + } + } + } + }); } public void showSystemVariablesTab() { - for (CTabItem tabItem : configTabs.getItems()) { - // Do nothing + // Navigate to system variables in the tree + for (TreeItem item : categoryTree.getItems()) { + if (item.getText().toLowerCase().contains("variable")) { + categoryTree.setSelection(item); + showCategory(item.getText()); + break; + } } } @@ -140,6 +793,30 @@ public Control getControl() { return composite; } + private Composite findPluginComposite(Composite parent) { + // Recursively search for the plugin composite + for (Control child : parent.getChildren()) { + if (child instanceof Composite childComposite) { + // Check if this is the plugin composite by checking its layout data + Object layoutData = childComposite.getLayoutData(); + if (layoutData instanceof FormData fd + && fd.left != null + && fd.right != null + && fd.top != null + && fd.bottom != null) { + // The plugin composite has specific layout characteristics + return childComposite; + } + // Recursively search children + Composite found = findPluginComposite(childComposite); + if (found != null) { + return found; + } + } + } + return null; + } + @Override public List getSearchables() { return new ArrayList<>(); diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGeneralOptionsTab.java b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGeneralOptionsTab.java index 22874f94fb1..449ab1e15b7 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGeneralOptionsTab.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGeneralOptionsTab.java @@ -26,7 +26,6 @@ import org.apache.hop.ui.core.PropsUi; import org.apache.hop.ui.core.dialog.BaseDialog; import org.apache.hop.ui.core.dialog.ErrorDialog; -import org.apache.hop.ui.core.dialog.MessageBox; import org.apache.hop.ui.core.gui.GuiResource; import org.apache.hop.ui.hopgui.HopGui; import org.apache.hop.ui.hopgui.perspective.configuration.ConfigurationPerspective; @@ -43,7 +42,10 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.ExpandBar; +import org.eclipse.swt.widgets.ExpandItem; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; @@ -55,24 +57,60 @@ public class ConfigGeneralOptionsTab { private Text wDefaultPreview; private Button wUseCache; private Button wOpenLast; + private Button wReloadFileOnChange; private Button wAutoSave; private Button wAutoSplit; private Button wCopyDistribute; private Button wExitWarning; private Button wToolTip; private Button wResolveVarsInTips; - private Button wHelpTip; - private Button wbUseDoubleClick; - private Button wbDrawBorderAroundCanvasNames; private Button wbUseGlobalFileBookmarks; private Button wSortFieldByName; private Text wMaxExecutionLoggingTextSize; + private boolean isReloading = false; // Flag to prevent saving during reload + public ConfigGeneralOptionsTab() { // This instance is created in the GuiPlugin system by calling this constructor, after which it // calls the addGeneralOptionsTab() method. } + /** + * Reload values from PropsUi into the widgets. This is useful when values are changed outside of + * the options dialog (e.g., "Do not ask this again" checkboxes). + */ + public void reloadValues() { + if (wDefaultPreview == null || wDefaultPreview.isDisposed()) { + return; // Tab not yet initialized or already disposed + } + + // Set flag to prevent saveValues from being triggered during reload + isReloading = true; + + try { + PropsUi props = PropsUi.getInstance(); + + // Reload all checkbox and text values from PropsUi + wDefaultPreview.setText(Integer.toString(props.getDefaultPreviewSize())); + wUseCache.setSelection(props.useDBCache()); + wOpenLast.setSelection(props.openLastFile()); + wReloadFileOnChange.setSelection(props.isReloadingFilesOnChange()); + wAutoSave.setSelection(!props.getAutoSave()); // Inverted logic + wCopyDistribute.setSelection(props.showCopyOrDistributeWarning()); + wExitWarning.setSelection(props.showExitWarning()); + wAutoSplit.setSelection(!props.getAutoSplit()); // Inverted logic + wToolTip.setSelection(props.showToolTips()); + wResolveVarsInTips.setSelection(props.resolveVariablesInToolTips()); + wSortFieldByName.setSelection(props.isSortFieldByName()); + wbUseGlobalFileBookmarks.setSelection(props.useGlobalFileBookmarks()); + wMaxExecutionLoggingTextSize.setText( + Integer.toString(props.getMaxExecutionLoggingTextSize())); + } finally { + // Always reset the flag + isReloading = false; + } + } + @GuiTab( id = "10000-config-perspective-general-options-tab", parentId = ConfigurationPerspective.CONFIG_PERSPECTIVE_TABS, @@ -81,7 +119,6 @@ public void addGeneralOptionsTab(CTabFolder wTabFolder) { Shell shell = wTabFolder.getShell(); PropsUi props = PropsUi.getInstance(); int margin = PropsUi.getMargin(); - int middle = props.getMiddlePct(); CTabItem wGeneralTab = new CTabItem(wTabFolder, SWT.NONE); wGeneralTab.setImage(GuiResource.getInstance().getImageOptions()); @@ -99,23 +136,19 @@ public void addGeneralOptionsTab(CTabFolder wTabFolder) { wGeneralComp.setLayout(generalLayout); // The name of the Hop configuration filename - Label wlFilename = new Label(wGeneralComp, SWT.RIGHT); - wlFilename.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.ConfigFilename.Label")); - PropsUi.setLook(wlFilename); - FormData fdlFilename = new FormData(); - fdlFilename.left = new FormAttachment(0, 0); - fdlFilename.right = new FormAttachment(middle, -margin); - fdlFilename.top = new FormAttachment(0, margin); - wlFilename.setLayoutData(fdlFilename); - Text wFilename = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT); - wFilename.setText(Const.NVL(HopConfig.getInstance().getConfigFilename(), "")); - wFilename.setEditable(false); - PropsUi.setLook(wFilename); - FormData fdFilename = new FormData(); - fdFilename.left = new FormAttachment(middle, 0); - fdFilename.right = new FormAttachment(100, 0); - fdFilename.top = new FormAttachment(0, margin); - wFilename.setLayoutData(fdFilename); + Control[] filenameControls = + createTextField( + wGeneralComp, + "EnterOptionsDialog.ConfigFilename.Label", + null, + Const.NVL(HopConfig.getInstance().getConfigFilename(), ""), + null, + margin); + Text wFilename = (Text) filenameControls[1]; + // Disable the field if config folder is set via HOP_CONFIG_FOLDER environment variable + String configFolder = System.getProperty("HOP_CONFIG_FOLDER"); + boolean isConfigFromEnv = configFolder != null && !configFolder.trim().isEmpty(); + wFilename.setEnabled(!isConfigFromEnv); Control lastControl = wFilename; // Explain HOP_CONFIG @@ -124,378 +157,302 @@ public void addGeneralOptionsTab(CTabFolder wTabFolder) { BaseMessages.getString(PKG, "EnterOptionsDialog.WhatIsHopConfigSize.Label")); PropsUi.setLook(wlWhatIsHopConfig); FormData fdlWhatIsHopConfig = new FormData(); - fdlWhatIsHopConfig.left = new FormAttachment(middle, 0); + fdlWhatIsHopConfig.left = new FormAttachment(0, 0); fdlWhatIsHopConfig.right = new FormAttachment(100, 0); fdlWhatIsHopConfig.top = new FormAttachment(lastControl, margin); wlWhatIsHopConfig.setLayoutData(fdlWhatIsHopConfig); lastControl = wlWhatIsHopConfig; // The default preview size - Label wlDefaultPreview = new Label(wGeneralComp, SWT.RIGHT); - wlDefaultPreview.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.DefaultPreviewSize.Label")); - PropsUi.setLook(wlDefaultPreview); - FormData fdlDefaultPreview = new FormData(); - fdlDefaultPreview.left = new FormAttachment(0, 0); - fdlDefaultPreview.right = new FormAttachment(middle, -margin); - fdlDefaultPreview.top = new FormAttachment(lastControl, margin); - wlDefaultPreview.setLayoutData(fdlDefaultPreview); - wDefaultPreview = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wDefaultPreview.setText(Integer.toString(props.getDefaultPreviewSize())); - PropsUi.setLook(wDefaultPreview); - FormData fdDefaultPreview = new FormData(); - fdDefaultPreview.left = new FormAttachment(middle, 0); - fdDefaultPreview.right = new FormAttachment(100, 0); - fdDefaultPreview.top = new FormAttachment(wlDefaultPreview, 0, SWT.CENTER); - wDefaultPreview.setLayoutData(fdDefaultPreview); - wDefaultPreview.addListener(SWT.Modify, this::saveValues); + Control[] previewControls = + createTextField( + wGeneralComp, + "EnterOptionsDialog.DefaultPreviewSize.Label", + null, + Integer.toString(props.getDefaultPreviewSize()), + lastControl, + margin); + wDefaultPreview = (Text) previewControls[1]; + // Add hint text for empty field + wDefaultPreview.setMessage(BaseMessages.getString(PKG, "EnterOptionsDialog.EnterNumber.Hint")); + // Add validation to only allow integer values + wDefaultPreview.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; // Reject the input if it's not a valid integer + } + }); lastControl = wDefaultPreview; // Use DB Cache? - Label wlUseCache = new Label(wGeneralComp, SWT.RIGHT); - wlUseCache.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.UseDatabaseCache.Label")); - PropsUi.setLook(wlUseCache); - FormData fdlUseCache = new FormData(); - fdlUseCache.left = new FormAttachment(0, 0); - fdlUseCache.top = new FormAttachment(lastControl, margin); - fdlUseCache.right = new FormAttachment(middle, -margin); - wlUseCache.setLayoutData(fdlUseCache); - wUseCache = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wUseCache); - wUseCache.setSelection(props.useDBCache()); - FormData fdUseCache = new FormData(); - fdUseCache.left = new FormAttachment(middle, 0); - fdUseCache.top = new FormAttachment(wlUseCache, 0, SWT.CENTER); - fdUseCache.right = new FormAttachment(100, 0); - wUseCache.setLayoutData(fdUseCache); - wUseCache.addListener(SWT.Selection, this::saveValues); - lastControl = wlUseCache; + wUseCache = + createCheckbox( + wGeneralComp, + "EnterOptionsDialog.UseDatabaseCache.Label", + null, + props.useDBCache(), + lastControl, + margin); + lastControl = wUseCache; // Auto load last file at startup? - Label wlOpenLast = new Label(wGeneralComp, SWT.RIGHT); - wlOpenLast.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.OpenLastFileStartup.Label")); - PropsUi.setLook(wlOpenLast); - FormData fdlOpenLast = new FormData(); - fdlOpenLast.left = new FormAttachment(0, 0); - fdlOpenLast.top = new FormAttachment(lastControl, margin); - fdlOpenLast.right = new FormAttachment(middle, -margin); - wlOpenLast.setLayoutData(fdlOpenLast); - wOpenLast = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wOpenLast); - wOpenLast.setSelection(props.openLastFile()); - FormData fdOpenLast = new FormData(); - fdOpenLast.left = new FormAttachment(middle, 0); - fdOpenLast.top = new FormAttachment(wlOpenLast, 0, SWT.CENTER); - fdOpenLast.right = new FormAttachment(100, 0); - wOpenLast.setLayoutData(fdOpenLast); - wOpenLast.addListener(SWT.Selection, this::saveValues); - lastControl = wlOpenLast; - - // Auto save changed files? - Label wlAutoSave = new Label(wGeneralComp, SWT.RIGHT); - wlAutoSave.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.AutoSave.Label")); - PropsUi.setLook(wlAutoSave); - FormData fdlAutoSave = new FormData(); - fdlAutoSave.left = new FormAttachment(0, 0); - fdlAutoSave.top = new FormAttachment(lastControl, margin); - fdlAutoSave.right = new FormAttachment(middle, -margin); - wlAutoSave.setLayoutData(fdlAutoSave); - wAutoSave = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wAutoSave); - wAutoSave.setSelection(props.getAutoSave()); - FormData fdAutoSave = new FormData(); - fdAutoSave.left = new FormAttachment(middle, 0); - fdAutoSave.top = new FormAttachment(wlAutoSave, 0, SWT.CENTER); - fdAutoSave.right = new FormAttachment(100, 0); - wAutoSave.setLayoutData(fdAutoSave); - wAutoSave.addListener(SWT.Selection, this::saveValues); - lastControl = wlAutoSave; - - // Automatically split hops? - Label wlAutoSplit = new Label(wGeneralComp, SWT.RIGHT); - wlAutoSplit.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.AutoSplitHops.Label")); - PropsUi.setLook(wlAutoSplit); - FormData fdlAutoSplit = new FormData(); - fdlAutoSplit.left = new FormAttachment(0, 0); - fdlAutoSplit.top = new FormAttachment(lastControl, margin); - fdlAutoSplit.right = new FormAttachment(middle, -margin); - wlAutoSplit.setLayoutData(fdlAutoSplit); - wAutoSplit = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wAutoSplit); - wAutoSplit.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.AutoSplitHops.Tooltip")); - wAutoSplit.setSelection(props.getAutoSplit()); - FormData fdAutoSplit = new FormData(); - fdAutoSplit.left = new FormAttachment(middle, 0); - fdAutoSplit.top = new FormAttachment(wlAutoSplit, 0, SWT.CENTER); - fdAutoSplit.right = new FormAttachment(100, 0); - wAutoSplit.setLayoutData(fdAutoSplit); - wAutoSplit.addListener(SWT.Selection, this::saveValues); - lastControl = wlAutoSplit; + wOpenLast = + createCheckbox( + wGeneralComp, + "EnterOptionsDialog.OpenLastFileStartup.Label", + null, + props.openLastFile(), + lastControl, + margin); + lastControl = wOpenLast; + + // Reload file if changed on filesystem? + wReloadFileOnChange = + createCheckbox( + wGeneralComp, + "EnterOptionsDialog.ReloadFileOnChange.Label", + "EnterOptionsDialog.ReloadFileOnChange.ToolTip", + props.isReloadingFilesOnChange(), + lastControl, + margin); + lastControl = wReloadFileOnChange; + + // Sort field by name + wSortFieldByName = + createCheckbox( + wGeneralComp, + "EnterOptionsDialog.SortFieldByName.Label", + "EnterOptionsDialog.SortFieldByName.ToolTip", + props.isSortFieldByName(), + lastControl, + 2 * margin); + lastControl = wSortFieldByName; + + // Use global file bookmarks? + wbUseGlobalFileBookmarks = + createCheckbox( + wGeneralComp, + "EnterOptionsDialog.UseGlobalFileBookmarks.Label", + null, + props.useGlobalFileBookmarks(), + lastControl, + margin); + lastControl = wbUseGlobalFileBookmarks; + + // Maximum execution logging text size + Control[] loggingControls = + createTextField( + wGeneralComp, + "EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.Label", + "EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.ToolTip", + Integer.toString(props.getMaxExecutionLoggingTextSize()), + lastControl, + 2 * margin); + wMaxExecutionLoggingTextSize = (Text) loggingControls[1]; + // Add hint text for empty field + wMaxExecutionLoggingTextSize.setMessage( + BaseMessages.getString(PKG, "EnterOptionsDialog.EnterNumber.Hint")); + // Add validation to only allow integer values + wMaxExecutionLoggingTextSize.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; // Reject the input if it's not a valid integer + } + }); + lastControl = wMaxExecutionLoggingTextSize; + + // Confirmation dialogs section (at the bottom) - using ExpandBar + ExpandBar expandBar = new ExpandBar(wGeneralComp, SWT.V_SCROLL); + PropsUi.setLook(expandBar); + + FormData fdExpandBar = new FormData(); + fdExpandBar.left = new FormAttachment(0, 0); + fdExpandBar.right = new FormAttachment(100, 0); + fdExpandBar.top = new FormAttachment(lastControl, 2 * margin); + expandBar.setLayoutData(fdExpandBar); + + // Create expandable item for confirmation dialogs + Composite confirmationContent = new Composite(expandBar, SWT.NONE); + PropsUi.setLook(confirmationContent); + FormLayout contentLayout = new FormLayout(); + contentLayout.marginWidth = PropsUi.getFormMargin(); + contentLayout.marginHeight = PropsUi.getFormMargin(); + confirmationContent.setLayout(contentLayout); + + // Exit warning checkbox inside the expandable content + Control lastConfirmControl = null; + wExitWarning = + createCheckbox( + confirmationContent, + "EnterOptionsDialog.AskOnExit.Label", + null, + props.showExitWarning(), + lastConfirmControl, + margin); + lastConfirmControl = wExitWarning; // Show warning for copy / distribute... - Label wlCopyDistrib = new Label(wGeneralComp, SWT.RIGHT); - wlCopyDistrib.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.CopyOrDistributeDialog.Label")); - PropsUi.setLook(wlCopyDistrib); - FormData fdlCopyDistrib = new FormData(); - fdlCopyDistrib.left = new FormAttachment(0, 0); - fdlCopyDistrib.top = new FormAttachment(lastControl, margin); - fdlCopyDistrib.right = new FormAttachment(middle, -margin); - wlCopyDistrib.setLayoutData(fdlCopyDistrib); - wCopyDistribute = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wCopyDistribute); - wCopyDistribute.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.CopyOrDistributeDialog.Tooltip")); - wCopyDistribute.setSelection(props.showCopyOrDistributeWarning()); - FormData fdCopyDistrib = new FormData(); - fdCopyDistrib.left = new FormAttachment(middle, 0); - fdCopyDistrib.top = new FormAttachment(wlCopyDistrib, 0, SWT.CENTER); - fdCopyDistrib.right = new FormAttachment(100, 0); - wCopyDistribute.setLayoutData(fdCopyDistrib); - wCopyDistribute.addListener(SWT.Selection, this::saveValues); - lastControl = wlCopyDistrib; - - // Show exit warning? - Label wlExitWarning = new Label(wGeneralComp, SWT.RIGHT); - wlExitWarning.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.AskOnExit.Label")); - PropsUi.setLook(wlExitWarning); - FormData fdlExitWarning = new FormData(); - fdlExitWarning.left = new FormAttachment(0, 0); - fdlExitWarning.top = new FormAttachment(lastControl, margin); - fdlExitWarning.right = new FormAttachment(middle, -margin); - wlExitWarning.setLayoutData(fdlExitWarning); - wExitWarning = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wExitWarning); - wExitWarning.setSelection(props.showExitWarning()); - FormData fdExitWarning = new FormData(); - fdExitWarning.left = new FormAttachment(middle, 0); - fdExitWarning.top = new FormAttachment(wlExitWarning, 0, SWT.CENTER); - fdExitWarning.right = new FormAttachment(100, 0); - wExitWarning.setLayoutData(fdExitWarning); - wExitWarning.addListener(SWT.Selection, this::saveValues); - lastControl = wlExitWarning; - - // Clear custom parameters. (from transform) - Label wlClearCustom = new Label(wGeneralComp, SWT.RIGHT); - wlClearCustom.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ClearCustomParameters.Label")); - PropsUi.setLook(wlClearCustom); - FormData fdlClearCustom = new FormData(); - fdlClearCustom.left = new FormAttachment(0, 0); - fdlClearCustom.top = new FormAttachment(lastControl, margin + 10); - fdlClearCustom.right = new FormAttachment(middle, -margin); - wlClearCustom.setLayoutData(fdlClearCustom); - - Button wClearCustom = new Button(wGeneralComp, SWT.PUSH); - PropsUi.setLook(wClearCustom); - FormData fdClearCustom = layoutResetOptionButton(wClearCustom); - fdClearCustom.width = fdClearCustom.width + 6; - fdClearCustom.height = fdClearCustom.height + 18; - fdClearCustom.left = new FormAttachment(middle, 0); - fdClearCustom.top = new FormAttachment(wlClearCustom, 0, SWT.CENTER); - wClearCustom.setLayoutData(fdClearCustom); - wClearCustom.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ClearCustomParameters.Tooltip")); - wClearCustom.addListener( + wCopyDistribute = + createCheckbox( + confirmationContent, + "EnterOptionsDialog.CopyOrDistributeDialog.Label", + "EnterOptionsDialog.CopyOrDistributeDialog.Tooltip", + props.showCopyOrDistributeWarning(), + lastConfirmControl, + margin); + lastConfirmControl = wCopyDistribute; + + // Show confirmation when splitting hops (inverted logic from autoSplit) + wAutoSplit = + createCheckbox( + confirmationContent, + "EnterOptionsDialog.SplitHopsConfirm.Label", + "EnterOptionsDialog.SplitHopsConfirm.Tooltip", + !props.getAutoSplit(), + lastConfirmControl, + margin); + lastConfirmControl = wAutoSplit; + + // Show confirmation to save file when starting pipeline or workflow (inverted logic from + // autoSave) + wAutoSave = + createCheckbox( + confirmationContent, + "EnterOptionsDialog.SaveConfirm.Label", + "EnterOptionsDialog.SaveConfirm.Tooltip", + !props.getAutoSave(), + lastConfirmControl, + margin); + lastConfirmControl = wAutoSave; + + // Reset button - enables all confirmation dialogs + Control[] resetButtonControls = + createButton( + confirmationContent, + "EnterOptionsDialog.ResetConfirmations.Label", + "EnterOptionsDialog.ResetConfirmations.Tooltip", + lastConfirmControl, + 2 * margin); + Button wResetConfirmations = (Button) resetButtonControls[0]; + wResetConfirmations.addListener( SWT.Selection, e -> { - MessageBox mb = new MessageBox(shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION); - mb.setMessage( - BaseMessages.getString(PKG, "EnterOptionsDialog.ClearCustomParameters.Question")); - mb.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.ClearCustomParameters.Title")); - int id = mb.open(); - if (id == SWT.YES) { - try { - props.clearCustomParameters(); - saveValues(null); - MessageBox ok = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION); - ok.setMessage( - BaseMessages.getString( - PKG, "EnterOptionsDialog.ClearCustomParameters.Confirmation")); - ok.open(); - } catch (Exception ex) { - new ErrorDialog( - shell, "Error", "Error clearing custom parameters, saving config file", ex); - } + // Enable all confirmation checkboxes + wExitWarning.setSelection(true); + wCopyDistribute.setSelection(true); + wAutoSplit.setSelection(true); + wAutoSave.setSelection(true); + + // Clear all custom parameters to re-enable transform warnings + try { + props.clearCustomParameters(); + } catch (Exception ex) { + new ErrorDialog(shell, "Error", "Error clearing transform warning preferences", ex); } - }); - lastControl = wClearCustom; - // Sort field by name - Label wlSortFieldByName = new Label(wGeneralComp, SWT.RIGHT); - wlSortFieldByName.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.SortFieldByName.Label")); - wlSortFieldByName.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.SortFieldByName.ToolTip")); - PropsUi.setLook(wlSortFieldByName); - FormData fdlSortFieldByName = new FormData(); - fdlSortFieldByName.left = new FormAttachment(0, 0); - fdlSortFieldByName.right = new FormAttachment(middle, -margin); - fdlSortFieldByName.top = new FormAttachment(lastControl, 2 * margin); - wlSortFieldByName.setLayoutData(fdlSortFieldByName); - wSortFieldByName = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wSortFieldByName); - FormData fdSortFieldByName = new FormData(); - fdSortFieldByName.left = new FormAttachment(middle, 0); - fdSortFieldByName.right = new FormAttachment(100, -margin); - fdSortFieldByName.top = new FormAttachment(wlSortFieldByName, 0, SWT.CENTER); - wSortFieldByName.setLayoutData(fdSortFieldByName); - wSortFieldByName.setSelection(props.isSortFieldByName()); - wSortFieldByName.addListener(SWT.Selection, this::saveValues); - lastControl = wSortFieldByName; + // Trigger save to persist the changes + saveValues(null); + }); - // Tooltips - Label wlToolTip = new Label(wGeneralComp, SWT.RIGHT); - wlToolTip.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.ToolTipsEnabled.Label")); - PropsUi.setLook(wlToolTip); - FormData fdlToolTip = new FormData(); - fdlToolTip.left = new FormAttachment(0, 0); - fdlToolTip.top = new FormAttachment(lastControl, margin); - fdlToolTip.right = new FormAttachment(middle, -margin); - wlToolTip.setLayoutData(fdlToolTip); - wToolTip = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wToolTip); - wToolTip.setSelection(props.showToolTips()); - FormData fdbToolTip = new FormData(); - fdbToolTip.left = new FormAttachment(middle, 0); - fdbToolTip.top = new FormAttachment(wlToolTip, 0, SWT.CENTER); - fdbToolTip.right = new FormAttachment(100, 0); - wToolTip.setLayoutData(fdbToolTip); - wToolTip.addListener(SWT.Selection, this::saveValues); - lastControl = wlToolTip; + // Create the expand item + ExpandItem confirmationItem = new ExpandItem(expandBar, SWT.NONE); + confirmationItem.setText( + BaseMessages.getString(PKG, "EnterOptionsDialog.Section.ConfirmationDialogs")); + confirmationItem.setControl(confirmationContent); + confirmationItem.setHeight(confirmationContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + confirmationItem.setExpanded(true); // Start expanded + + // Tooltips section - using ExpandBar + Composite tooltipsContent = new Composite(expandBar, SWT.NONE); + PropsUi.setLook(tooltipsContent); + FormLayout tooltipsLayout = new FormLayout(); + tooltipsLayout.marginWidth = PropsUi.getFormMargin(); + tooltipsLayout.marginHeight = PropsUi.getFormMargin(); + tooltipsContent.setLayout(tooltipsLayout); + + // Display tooltips checkbox + Control lastTooltipControl = null; + wToolTip = + createCheckbox( + tooltipsContent, + "EnterOptionsDialog.ToolTipsEnabled.Label", + null, + props.showToolTips(), + lastTooltipControl, + margin); + lastTooltipControl = wToolTip; // Resolve variables in tooltips - // - Label wlResolveVarInTips = new Label(wGeneralComp, SWT.RIGHT); - wlResolveVarInTips.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ResolveVarsInTips.Label")); - PropsUi.setLook(wlResolveVarInTips); - FormData fdlResolveVarInTips = new FormData(); - fdlResolveVarInTips.left = new FormAttachment(0, 0); - fdlResolveVarInTips.top = new FormAttachment(lastControl, margin); - fdlResolveVarInTips.right = new FormAttachment(middle, -margin); - wlResolveVarInTips.setLayoutData(fdlResolveVarInTips); - wResolveVarsInTips = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wResolveVarsInTips); - wResolveVarsInTips.setSelection(props.resolveVariablesInToolTips()); - FormData fdbResolveVarInTips = new FormData(); - fdbResolveVarInTips.left = new FormAttachment(middle, 0); - fdbResolveVarInTips.top = new FormAttachment(wlResolveVarInTips, 0, SWT.CENTER); - fdbResolveVarInTips.right = new FormAttachment(100, 0); - wResolveVarsInTips.setLayoutData(fdbResolveVarInTips); - wResolveVarsInTips.addListener(SWT.Selection, this::saveValues); - lastControl = wlResolveVarInTips; - - // Help tool tips - Label wlHelpTip = new Label(wGeneralComp, SWT.RIGHT); - wlHelpTip.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.HelpToolTipsEnabled.Label")); - PropsUi.setLook(wlHelpTip); - FormData fdlHelpTip = new FormData(); - fdlHelpTip.left = new FormAttachment(0, 0); - fdlHelpTip.top = new FormAttachment(lastControl, margin); - fdlHelpTip.right = new FormAttachment(middle, -margin); - wlHelpTip.setLayoutData(fdlHelpTip); - wHelpTip = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wHelpTip); - wHelpTip.setSelection(props.isShowingHelpToolTips()); - FormData fdbHelpTip = new FormData(); - fdbHelpTip.left = new FormAttachment(middle, 0); - fdbHelpTip.top = new FormAttachment(wlHelpTip, 0, SWT.CENTER); - fdbHelpTip.right = new FormAttachment(100, 0); - wHelpTip.setLayoutData(fdbHelpTip); - wHelpTip.addListener(SWT.Selection, this::saveValues); - lastControl = wlHelpTip; - - // Use double click on the canvas - // - Label wlUseDoubleClick = new Label(wGeneralComp, SWT.RIGHT); - wlUseDoubleClick.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.UseDoubleClickOnCanvas.Label")); - PropsUi.setLook(wlUseDoubleClick); - FormData fdlUseDoubleClick = new FormData(); - fdlUseDoubleClick.left = new FormAttachment(0, 0); - fdlUseDoubleClick.top = new FormAttachment(lastControl, margin); - fdlUseDoubleClick.right = new FormAttachment(middle, -margin); - wlUseDoubleClick.setLayoutData(fdlUseDoubleClick); - wbUseDoubleClick = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wbUseDoubleClick); - wbUseDoubleClick.setSelection(props.useDoubleClick()); - FormData fdbUseDoubleClick = new FormData(); - fdbUseDoubleClick.left = new FormAttachment(middle, 0); - fdbUseDoubleClick.top = new FormAttachment(wlUseDoubleClick, 0, SWT.CENTER); - fdbUseDoubleClick.right = new FormAttachment(100, 0); - wbUseDoubleClick.setLayoutData(fdbUseDoubleClick); - wbUseDoubleClick.addListener(SWT.Selection, this::saveValues); - lastControl = wlUseDoubleClick; - - // Use double click on the canvas - // - Label wlDrawBorderAroundCanvasNames = new Label(wGeneralComp, SWT.RIGHT); - wlDrawBorderAroundCanvasNames.setText( - BaseMessages.getString( - PKG, "EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label")); - PropsUi.setLook(wlDrawBorderAroundCanvasNames); - FormData fdlDrawBorderAroundCanvasNames = new FormData(); - fdlDrawBorderAroundCanvasNames.left = new FormAttachment(0, 0); - fdlDrawBorderAroundCanvasNames.top = new FormAttachment(lastControl, margin); - fdlDrawBorderAroundCanvasNames.right = new FormAttachment(middle, -margin); - wlDrawBorderAroundCanvasNames.setLayoutData(fdlDrawBorderAroundCanvasNames); - wbDrawBorderAroundCanvasNames = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wbDrawBorderAroundCanvasNames); - wbDrawBorderAroundCanvasNames.setSelection(props.useDoubleClick()); - FormData fdbDrawBorderAroundCanvasNames = new FormData(); - fdbDrawBorderAroundCanvasNames.left = new FormAttachment(middle, 0); - fdbDrawBorderAroundCanvasNames.top = - new FormAttachment(wlDrawBorderAroundCanvasNames, 0, SWT.CENTER); - fdbDrawBorderAroundCanvasNames.right = new FormAttachment(100, 0); - wbDrawBorderAroundCanvasNames.setLayoutData(fdbDrawBorderAroundCanvasNames); - wbDrawBorderAroundCanvasNames.addListener(SWT.Selection, this::saveValues); - lastControl = wlDrawBorderAroundCanvasNames; + wResolveVarsInTips = + createCheckbox( + tooltipsContent, + "EnterOptionsDialog.ResolveVarsInTips.Label", + null, + props.resolveVariablesInToolTips(), + lastTooltipControl, + margin); + lastTooltipControl = wResolveVarsInTips; + + // Reset button - enables all tooltip options + Control[] resetTooltipControls = + createButton( + tooltipsContent, + "EnterOptionsDialog.ResetTooltips.Label", + "EnterOptionsDialog.ResetTooltips.Tooltip", + lastTooltipControl, + 2 * margin); + Button wResetTooltips = (Button) resetTooltipControls[0]; + wResetTooltips.addListener( + SWT.Selection, + e -> { + // Enable all tooltip checkboxes + wToolTip.setSelection(true); + wResolveVarsInTips.setSelection(true); - // Use global file bookmarks? - Label wlUseGlobalFileBookmarks = new Label(wGeneralComp, SWT.RIGHT); - wlUseGlobalFileBookmarks.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.UseGlobalFileBookmarks.Label")); - PropsUi.setLook(wlUseGlobalFileBookmarks); - FormData fdlUseGlobalFileBookmarks = new FormData(); - fdlUseGlobalFileBookmarks.left = new FormAttachment(0, 0); - fdlUseGlobalFileBookmarks.top = new FormAttachment(lastControl, margin); - fdlUseGlobalFileBookmarks.right = new FormAttachment(middle, -margin); - wlUseGlobalFileBookmarks.setLayoutData(fdlUseGlobalFileBookmarks); - wbUseGlobalFileBookmarks = new Button(wGeneralComp, SWT.CHECK); - PropsUi.setLook(wbUseGlobalFileBookmarks); - wbUseGlobalFileBookmarks.setSelection(props.useGlobalFileBookmarks()); - FormData fdbUseGlobalFileBookmarks = new FormData(); - fdbUseGlobalFileBookmarks.left = new FormAttachment(middle, 0); - fdbUseGlobalFileBookmarks.top = new FormAttachment(wlUseGlobalFileBookmarks, 0, SWT.CENTER); - fdbUseGlobalFileBookmarks.right = new FormAttachment(100, 0); - wbUseGlobalFileBookmarks.setLayoutData(fdbUseGlobalFileBookmarks); - wbUseGlobalFileBookmarks.addListener(SWT.Selection, this::saveValues); - lastControl = wlUseGlobalFileBookmarks; + // Trigger save to persist the changes + saveValues(null); + }); - // The default preview size - Label wlMaxExecutionLoggingTextSize = new Label(wGeneralComp, SWT.RIGHT); - wlMaxExecutionLoggingTextSize.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.Label")); - PropsUi.setLook(wlMaxExecutionLoggingTextSize); - FormData fdlMaxExecutionLoggingTextSize = new FormData(); - fdlMaxExecutionLoggingTextSize.left = new FormAttachment(0, 0); - fdlMaxExecutionLoggingTextSize.right = new FormAttachment(middle, -margin); - fdlMaxExecutionLoggingTextSize.top = new FormAttachment(lastControl, 2 * margin); - wlMaxExecutionLoggingTextSize.setLayoutData(fdlMaxExecutionLoggingTextSize); - wMaxExecutionLoggingTextSize = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wMaxExecutionLoggingTextSize.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.ToolTip")); - wMaxExecutionLoggingTextSize.setText(Integer.toString(props.getMaxExecutionLoggingTextSize())); - PropsUi.setLook(wMaxExecutionLoggingTextSize); - FormData fdMaxExecutionLoggingTextSize = new FormData(); - fdMaxExecutionLoggingTextSize.left = new FormAttachment(middle, 0); - fdMaxExecutionLoggingTextSize.right = new FormAttachment(100, 0); - fdMaxExecutionLoggingTextSize.top = - new FormAttachment(wlMaxExecutionLoggingTextSize, 0, SWT.CENTER); - wMaxExecutionLoggingTextSize.setLayoutData(fdMaxExecutionLoggingTextSize); - wMaxExecutionLoggingTextSize.addListener(SWT.Modify, this::saveValues); + // Create the tooltips expand item + ExpandItem tooltipsItem = new ExpandItem(expandBar, SWT.NONE); + tooltipsItem.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.Section.Tooltips")); + tooltipsItem.setControl(tooltipsContent); + tooltipsItem.setHeight(tooltipsContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + tooltipsItem.setExpanded(true); // Start expanded + + // Add expand/collapse listeners for space reclamation + expandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wGeneralComp.isDisposed() && !sGeneralComp.isDisposed()) { + wGeneralComp.layout(); + sGeneralComp.setMinHeight( + wGeneralComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + expandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wGeneralComp.isDisposed() && !sGeneralComp.isDisposed()) { + wGeneralComp.layout(); + sGeneralComp.setMinHeight( + wGeneralComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); FormData fdGeneralComp = new FormData(); fdGeneralComp.left = new FormAttachment(0, 0); @@ -518,44 +475,180 @@ public void addGeneralOptionsTab(CTabFolder wTabFolder) { } /** - * Setting the layout of a Reset option button. Either a button image is set - if existing - * - or a text. + * Creates a text field with label above it. * - * @param button The button + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param initialValue The initial text value + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return An array containing [Label, Text] controls */ - private FormData layoutResetOptionButton(Button button) { - FormData fd = new FormData(); - Image editButton = GuiResource.getInstance().getImageResetOption(); - if (editButton != null) { - button.setImage(editButton); + private Control[] createTextField( + Composite parent, + String labelKey, + String tooltipKey, + String initialValue, + Control lastControl, + int margin) { + // Label above + Label label = new Label(parent, SWT.LEFT); + PropsUi.setLook(label); + label.setText(BaseMessages.getString(PKG, labelKey)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdLabel.top = new FormAttachment(lastControl, margin); + } else { + fdLabel.top = new FormAttachment(0, margin); + } + label.setLayoutData(fdLabel); + + // Text field below label + Text text = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + PropsUi.setLook(text); + text.setText(initialValue); + if (tooltipKey != null) { + text.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + text.addListener(SWT.Modify, this::saveValues); + + FormData fdText = new FormData(); + fdText.left = new FormAttachment(0, 0); + fdText.right = new FormAttachment(100, 0); + fdText.top = new FormAttachment(label, margin / 2); + text.setLayoutData(fdText); + + return new Control[] {label, text}; + } + + /** + * Creates a button with image in front and text label behind it (like checkboxes). + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return An array containing [Button, Label] controls + */ + private Control[] createButton( + Composite parent, String labelKey, String tooltipKey, Control lastControl, int margin) { + // Button with image + Button button = new Button(parent, SWT.PUSH); + PropsUi.setLook(button); + + // Try to set image, otherwise use text + Image buttonImage = GuiResource.getInstance().getImageResetOption(); + if (buttonImage != null) { + button.setImage(buttonImage); button.setBackground(GuiResource.getInstance().getColorWhite()); - fd.width = editButton.getBounds().width + 20; - fd.height = editButton.getBounds().height; } else { button.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.Button.Reset")); } - button.setToolTipText(BaseMessages.getString(PKG, "EnterOptionsDialog.Button.Reset.Tooltip")); - return fd; + if (tooltipKey != null) { + button.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + + // Calculate proper button height based on image and zoom factor + int buttonHeight = (int) (32 * PropsUi.getInstance().getZoomFactor()); + if (buttonImage != null) { + // Ensure button is at least as tall as the image with some padding + buttonHeight = Math.max(buttonHeight, buttonImage.getBounds().height + 8); + } + + FormData fdButton = new FormData(); + fdButton.left = new FormAttachment(0, 0); + fdButton.height = buttonHeight; + if (lastControl != null) { + fdButton.top = new FormAttachment(lastControl, margin); + } else { + fdButton.top = new FormAttachment(0, margin); + } + button.setLayoutData(fdButton); + + // Label with text behind the button + Label label = new Label(parent, SWT.LEFT); + PropsUi.setLook(label); + label.setText(BaseMessages.getString(PKG, labelKey)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(button, margin); + fdLabel.top = new FormAttachment(button, 0, SWT.CENTER); + fdLabel.right = new FormAttachment(100, 0); + label.setLayoutData(fdLabel); + + return new Control[] {button, label}; + } + + /** + * Creates a checkbox with the checkbox in front of the label text (not centered). + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param selected Whether the checkbox is initially selected + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return The created Button (checkbox) + */ + private Button createCheckbox( + Composite parent, + String labelKey, + String tooltipKey, + boolean selected, + Control lastControl, + int margin) { + Button checkbox = new Button(parent, SWT.CHECK); + PropsUi.setLook(checkbox); + checkbox.setText(BaseMessages.getString(PKG, labelKey)); + if (tooltipKey != null) { + checkbox.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + checkbox.setSelection(selected); + checkbox.addListener(SWT.Selection, this::saveValues); + + FormData fdCheckbox = new FormData(); + fdCheckbox.left = new FormAttachment(0, 0); + fdCheckbox.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdCheckbox.top = new FormAttachment(lastControl, margin); + } else { + fdCheckbox.top = new FormAttachment(0, margin); + } + checkbox.setLayoutData(fdCheckbox); + + return checkbox; } private void saveValues(Event event) { + // Don't save if we're currently reloading values + if (isReloading) { + return; + } + PropsUi props = PropsUi.getInstance(); props.setDefaultPreviewSize( Const.toInt(wDefaultPreview.getText(), props.getDefaultPreviewSize())); props.setUseDBCache(wUseCache.getSelection()); props.setOpenLastFile(wOpenLast.getSelection()); - props.setAutoSave(wAutoSave.getSelection()); - props.setAutoSplit(wAutoSplit.getSelection()); + props.setReloadingFilesOnChange(wReloadFileOnChange.getSelection()); + props.setAutoSave( + !wAutoSave + .getSelection()); // Inverted: checkbox is "show confirmation", property is "auto save" + props.setAutoSplit( + !wAutoSplit + .getSelection()); // Inverted: checkbox is "show confirmation", property is "auto split" props.setShowCopyOrDistributeWarning(wCopyDistribute.getSelection()); props.setExitWarningShown(wExitWarning.getSelection()); props.setShowToolTips(wToolTip.getSelection()); props.setResolveVariablesInToolTips(wResolveVarsInTips.getSelection()); props.setSortFieldByName(wSortFieldByName.getSelection()); - props.setShowingHelpToolTips(wHelpTip.getSelection()); - props.setUseDoubleClickOnCanvas(wbUseDoubleClick.getSelection()); - props.setDrawBorderAroundCanvasNames(wbDrawBorderAroundCanvasNames.getSelection()); props.setUseGlobalFileBookmarks(wbUseGlobalFileBookmarks.getSelection()); props.setMaxExecutionLoggingTextSize( Const.toInt( diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java index 19cacdb75b2..769f3f7249b 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigGuiOptionsTab.java @@ -52,6 +52,8 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.ExpandBar; +import org.eclipse.swt.widgets.ExpandItem; import org.eclipse.swt.widgets.FontDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; @@ -60,6 +62,8 @@ @GuiPlugin public class ConfigGuiOptionsTab { private static final Class PKG = BaseDialog.class; + public static final String ENTER_OPTIONS_DIALOG_ENTER_NUMBER_HINT = + "EnterOptionsDialog.EnterNumber.Hint"; private FontData defaultFontData; private Font defaultFont; @@ -80,16 +84,101 @@ public class ConfigGuiOptionsTab { private Text wMiddlePct; private Combo wGlobalZoom; private Text wGridSize; + private Label wlGridSize; // Label for grid size (for enable/disable) private Button wDarkMode; private Button wShowCanvasGrid; private Button wHideViewport; + private Button wUseDoubleClick; + private Button wDrawBorderAroundCanvasNames; private Button wHideMenuBar; private Button wShowTableViewToolbar; private Combo wDefaultLocale; + private boolean isReloading = false; // Flag to prevent saving during reload + private boolean isInitializing = false; // Flag to prevent saving during initialization + public ConfigGuiOptionsTab() { // This instance is created in the GuiPlugin system by calling this constructor, after which it - // calls the addGeneralOptionsTab() method. + // calls the addGuiOptionsTab() method. + } + + /** + * Reload values from PropsUi into the widgets. This is useful when values are changed outside of + * the options dialog. + */ + public void reloadValues() { + if (wIconSize == null || wIconSize.isDisposed()) { + return; // Tab not yet initialized or already disposed + } + + // Set flag to prevent saveValues from being triggered during reload + isReloading = true; + + try { + PropsUi props = PropsUi.getInstance(); + + // Reload all values from PropsUi + defaultFontData = props.getDefaultFont(); + fixedFontData = props.getFixedFont(); + graphFontData = props.getGraphFont(); + noteFontData = props.getNoteFont(); + + // Recreate fonts + Shell shell = wIconSize.getShell(); + Display display = shell.getDisplay(); + if (defaultFont != null && !defaultFont.isDisposed()) { + defaultFont.dispose(); + } + defaultFont = new Font(display, defaultFontData); + if (fixedFont != null && !fixedFont.isDisposed()) { + fixedFont.dispose(); + } + fixedFont = new Font(display, fixedFontData); + if (graphFont != null && !graphFont.isDisposed()) { + graphFont.dispose(); + } + graphFont = new Font(display, graphFontData); + if (noteFont != null && !noteFont.isDisposed()) { + noteFont.dispose(); + } + noteFont = new Font(display, noteFontData); + + // Redraw canvases + wDefaultCanvas.redraw(); + wFixedCanvas.redraw(); + wGraphCanvas.redraw(); + wNoteCanvas.redraw(); + + // Reload text fields and checkboxes + wIconSize.setText(Integer.toString(props.getIconSize())); + wLineWidth.setText(Integer.toString(props.getLineWidth())); + wMiddlePct.setText(Integer.toString(props.getMiddlePct())); + wGridSize.setText(Integer.toString(props.getCanvasGridSize())); + wShowCanvasGrid.setSelection(props.isShowCanvasGridEnabled()); + + wHideViewport.setSelection(!props.isHideViewportEnabled()); // Inverted logic + wUseDoubleClick.setSelection(props.useDoubleClick()); + wDrawBorderAroundCanvasNames.setSelection(props.isBorderDrawnAroundCanvasNames()); + wHideMenuBar.setSelection(props.isHidingMenuBar()); + wShowTableViewToolbar.setSelection(props.isShowTableViewToolbar()); + wDarkMode.setSelection(props.isDarkMode()); + + // Reload global zoom + String globalZoomFactor = Integer.toString((int) (props.getGlobalZoomFactor() * 100)) + '%'; + wGlobalZoom.setText(globalZoomFactor); + + // Reload default locale + int idxDefault = + Const.indexOfString( + LanguageChoice.getInstance().getDefaultLocale().toString(), + GlobalMessages.localeCodes); + if (idxDefault >= 0) { + wDefaultLocale.select(idxDefault); + } + } finally { + // Always reset the flag + isReloading = false; + } } @GuiTab( @@ -97,11 +186,12 @@ public ConfigGuiOptionsTab() { parentId = ConfigurationPerspective.CONFIG_PERSPECTIVE_TABS, description = "GUI options tab") public void addGuiOptionsTab(CTabFolder wTabFolder) { + // Set initialization flag to prevent saving during widget creation + isInitializing = true; + Shell shell = wTabFolder.getShell(); PropsUi props = PropsUi.getInstance(); int margin = PropsUi.getMargin(); - int middle = props.getMiddlePct(); - int h = (int) (40 * props.getZoomFactor()); CTabItem wLookTab = new CTabItem(wTabFolder, SWT.NONE); wLookTab.setFont(GuiResource.getInstance().getFontDefault()); @@ -119,6 +209,7 @@ public void addGuiOptionsTab(CTabFolder wTabFolder) { lookLayout.marginHeight = PropsUi.getFormMargin(); wLookComp.setLayout(lookLayout); + // Initialize fonts defaultFontData = props.getDefaultFont(); defaultFont = new Font(shell.getDisplay(), defaultFontData); fixedFontData = props.getFixedFont(); @@ -128,410 +219,476 @@ public void addGuiOptionsTab(CTabFolder wTabFolder) { noteFontData = props.getNoteFont(); noteFont = new Font(shell.getDisplay(), noteFontData); - // Default font - int nr = 0; - { - Label wlDFont = new Label(wLookComp, SWT.RIGHT); - wlDFont.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.DefaultFont.Label")); - PropsUi.setLook(wlDFont); - FormData fdlDFont = new FormData(); - fdlDFont.left = new FormAttachment(0, 0); - fdlDFont.right = new FormAttachment(middle, -margin); - fdlDFont.top = new FormAttachment(0, margin + 10); - wlDFont.setLayoutData(fdlDFont); - - Button wdDFont = new Button(wLookComp, SWT.PUSH | SWT.CENTER); - PropsUi.setLook(wdDFont); - FormData fddDFont = layoutResetOptionButton(wdDFont); - fddDFont.right = new FormAttachment(100, 0); - fddDFont.top = new FormAttachment(0, margin); - fddDFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wdDFont.setLayoutData(fddDFont); - wdDFont.addListener(SWT.Selection, e -> resetDefaultFont(shell)); - - Button wbDFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wbDFont); - FormData fdbDFont = layoutEditOptionButton(wbDFont); - fdbDFont.right = new FormAttachment(wdDFont, -margin); - fdbDFont.top = new FormAttachment(0, nr * h + margin); - fdbDFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wbDFont.setLayoutData(fdbDFont); - wbDFont.addListener(SWT.Selection, e -> editDefaultFont(shell)); - - wDefaultCanvas = new Canvas(wLookComp, SWT.BORDER); - PropsUi.setLook(wDefaultCanvas); - FormData fdDFont = new FormData(); - fdDFont.left = new FormAttachment(middle, 0); - fdDFont.right = new FormAttachment(wbDFont, -margin); - fdDFont.top = new FormAttachment(0, margin); - fdDFont.bottom = new FormAttachment(0, h); - wDefaultCanvas.setLayoutData(fdDFont); - wDefaultCanvas.addPaintListener(this::paintDefaultFont); - wDefaultCanvas.addListener(SWT.MouseDown, e -> editDefaultFont(shell)); - } - - // Fixed font - nr++; - { - Label wlFFont = new Label(wLookComp, SWT.RIGHT); - wlFFont.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.FixedWidthFont.Label")); - PropsUi.setLook(wlFFont); - FormData fdlFFont = new FormData(); - fdlFFont.left = new FormAttachment(0, 0); - fdlFFont.right = new FormAttachment(middle, -margin); - fdlFFont.top = new FormAttachment(0, nr * h + margin + 10); - wlFFont.setLayoutData(fdlFFont); - - Button wdFFont = new Button(wLookComp, SWT.PUSH | SWT.CENTER); - PropsUi.setLook(wdFFont); - FormData fddFFont = layoutResetOptionButton(wdFFont); - fddFFont.right = new FormAttachment(100, 0); - fddFFont.top = new FormAttachment(0, nr * h + margin); - fddFFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wdFFont.setLayoutData(fddFFont); - wdFFont.addListener(SWT.Selection, e -> resetFixedFont(shell)); - - Button wbFFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wbFFont); - FormData fdbFFont = layoutEditOptionButton(wbFFont); - fdbFFont.right = new FormAttachment(wdFFont, -margin); - fdbFFont.top = new FormAttachment(0, nr * h + margin); - fdbFFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wbFFont.setLayoutData(fdbFFont); - wbFFont.addListener(SWT.Selection, e -> editFixedFont(shell)); - - wFixedCanvas = new Canvas(wLookComp, SWT.BORDER); - PropsUi.setLook(wFixedCanvas); - FormData fdFFont = new FormData(); - fdFFont.left = new FormAttachment(middle, 0); - fdFFont.right = new FormAttachment(wbFFont, -margin); - fdFFont.top = new FormAttachment(0, nr * h + margin); - fdFFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wFixedCanvas.setLayoutData(fdFFont); - wFixedCanvas.addPaintListener(this::paintFixedFont); - wFixedCanvas.addListener(SWT.MouseDown, e -> editFixedFont(shell)); - } - - // Graph font - nr++; - { - Label wlGFont = new Label(wLookComp, SWT.RIGHT); - wlGFont.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.GraphFont.Label")); - PropsUi.setLook(wlGFont); - FormData fdlGFont = new FormData(); - fdlGFont.left = new FormAttachment(0, 0); - fdlGFont.right = new FormAttachment(middle, -margin); - fdlGFont.top = new FormAttachment(0, nr * h + margin + 10); - wlGFont.setLayoutData(fdlGFont); - - Button wdGFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wdGFont); - - FormData fddGFont = layoutResetOptionButton(wdGFont); - fddGFont.right = new FormAttachment(100, 0); - fddGFont.top = new FormAttachment(0, nr * h + margin); - fddGFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wdGFont.setLayoutData(fddGFont); - wdGFont.addListener(SWT.Selection, e -> resetGraphFont(shell, props)); - - Button wbGFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wbGFont); - - FormData fdbGFont = layoutEditOptionButton(wbGFont); - fdbGFont.right = new FormAttachment(wdGFont, -margin); - fdbGFont.top = new FormAttachment(0, nr * h + margin); - fdbGFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wbGFont.setLayoutData(fdbGFont); - wbGFont.addListener(SWT.Selection, e -> editGraphFont(shell)); - - wGraphCanvas = new Canvas(wLookComp, SWT.BORDER); - PropsUi.setLook(wGraphCanvas); - FormData fdGFont = new FormData(); - fdGFont.left = new FormAttachment(middle, 0); - fdGFont.right = new FormAttachment(wbGFont, -margin); - fdGFont.top = new FormAttachment(0, nr * h + margin); - fdGFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wGraphCanvas.setLayoutData(fdGFont); - wGraphCanvas.addPaintListener(this::drawGraphFont); - wGraphCanvas.addListener(SWT.MouseDown, e -> editGraphFont(shell)); - } - - // Note font - nr++; - { - Label wlNFont = new Label(wLookComp, SWT.RIGHT); - wlNFont.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.NoteFont.Label")); - PropsUi.setLook(wlNFont); - FormData fdlNFont = new FormData(); - fdlNFont.left = new FormAttachment(0, 0); - fdlNFont.right = new FormAttachment(middle, -margin); - fdlNFont.top = new FormAttachment(0, nr * h + margin + 10); - wlNFont.setLayoutData(fdlNFont); - - Button wdNFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wdNFont); - FormData fddNFont = layoutResetOptionButton(wdNFont); - fddNFont.right = new FormAttachment(100, 0); - fddNFont.top = new FormAttachment(0, nr * h + margin); - fddNFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wdNFont.setLayoutData(fddNFont); - wdNFont.addListener(SWT.Selection, e -> resetNoteFont(e, props, shell.getDisplay())); - - Button wbNFont = new Button(wLookComp, SWT.PUSH); - PropsUi.setLook(wbNFont); - FormData fdbNFont = layoutEditOptionButton(wbNFont); - fdbNFont.right = new FormAttachment(wdNFont, -margin); - fdbNFont.top = new FormAttachment(0, nr * h + margin); - fdbNFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wbNFont.setLayoutData(fdbNFont); - wbNFont.addListener(SWT.Selection, e -> editNoteFont(shell)); - - wNoteCanvas = new Canvas(wLookComp, SWT.BORDER); - PropsUi.setLook(wNoteCanvas); - FormData fdNFont = new FormData(); - fdNFont.left = new FormAttachment(middle, 0); - fdNFont.right = new FormAttachment(wbNFont, -margin); - fdNFont.top = new FormAttachment(0, nr * h + margin); - fdNFont.bottom = new FormAttachment(0, (nr + 1) * h + margin); - wNoteCanvas.setLayoutData(fdNFont); - wNoteCanvas.addPaintListener(this::paintNoteFont); - wNoteCanvas.addListener(SWT.MouseDown, e -> editNoteFont(shell)); - } - - // IconSize line - Label wlIconSize = new Label(wLookComp, SWT.RIGHT); - wlIconSize.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.IconSize.Label")); - PropsUi.setLook(wlIconSize); - FormData fdlIconSize = new FormData(); - fdlIconSize.left = new FormAttachment(0, 0); - fdlIconSize.right = new FormAttachment(middle, -margin); - fdlIconSize.top = new FormAttachment(wNoteCanvas, margin); - wlIconSize.setLayoutData(fdlIconSize); - wIconSize = new Text(wLookComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wIconSize.setText(Integer.toString(props.getIconSize())); - PropsUi.setLook(wIconSize); - FormData fdIconSize = new FormData(); - fdIconSize.left = new FormAttachment(middle, 0); - fdIconSize.right = new FormAttachment(100, -margin); - fdIconSize.top = new FormAttachment(wlIconSize, 0, SWT.CENTER); - wIconSize.setLayoutData(fdIconSize); - wIconSize.addListener(SWT.Modify, e -> saveValues()); - - // LineWidth line - Label wlLineWidth = new Label(wLookComp, SWT.RIGHT); - wlLineWidth.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.LineWidth.Label")); - PropsUi.setLook(wlLineWidth); - FormData fdlLineWidth = new FormData(); - fdlLineWidth.left = new FormAttachment(0, 0); - fdlLineWidth.right = new FormAttachment(middle, -margin); - fdlLineWidth.top = new FormAttachment(wIconSize, margin); - wlLineWidth.setLayoutData(fdlLineWidth); - wLineWidth = new Text(wLookComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wLineWidth.setText(Integer.toString(props.getLineWidth())); - PropsUi.setLook(wLineWidth); - FormData fdLineWidth = new FormData(); - fdLineWidth.left = new FormAttachment(middle, 0); - fdLineWidth.right = new FormAttachment(100, -margin); - fdLineWidth.top = new FormAttachment(wlLineWidth, 0, SWT.CENTER); - wLineWidth.setLayoutData(fdLineWidth); - wLineWidth.addListener(SWT.Modify, e -> saveValues()); - - // MiddlePct line - Label wlMiddlePct = new Label(wLookComp, SWT.RIGHT); - wlMiddlePct.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.DialogMiddlePercentage.Label")); - PropsUi.setLook(wlMiddlePct); - FormData fdlMiddlePct = new FormData(); - fdlMiddlePct.left = new FormAttachment(0, 0); - fdlMiddlePct.right = new FormAttachment(middle, -margin); - fdlMiddlePct.top = new FormAttachment(wLineWidth, margin); - wlMiddlePct.setLayoutData(fdlMiddlePct); - wMiddlePct = new Text(wLookComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wMiddlePct.setText(Integer.toString(props.getMiddlePct())); - PropsUi.setLook(wMiddlePct); - FormData fdMiddlePct = new FormData(); - fdMiddlePct.left = new FormAttachment(middle, 0); - fdMiddlePct.right = new FormAttachment(100, -margin); - fdMiddlePct.top = new FormAttachment(wlMiddlePct, 0, SWT.CENTER); - wMiddlePct.setLayoutData(fdMiddlePct); - wMiddlePct.addListener(SWT.Modify, e -> saveValues()); - - // Global Zoom - Label wlGlobalZoom = new Label(wLookComp, SWT.RIGHT); - wlGlobalZoom.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.GlobalZoom.Label")); - PropsUi.setLook(wlGlobalZoom); - FormData fdlGlobalZoom = new FormData(); - fdlGlobalZoom.left = new FormAttachment(0, 0); - fdlGlobalZoom.right = new FormAttachment(middle, -margin); - fdlGlobalZoom.top = new FormAttachment(wMiddlePct, margin); - wlGlobalZoom.setLayoutData(fdlGlobalZoom); - wGlobalZoom = new Combo(wLookComp, SWT.SINGLE | SWT.READ_ONLY | SWT.LEFT | SWT.BORDER); - wGlobalZoom.setItems(PropsUi.getGlobalZoomFactorLevels()); - PropsUi.setLook(wGlobalZoom); - FormData fdGlobalZoom = new FormData(); - fdGlobalZoom.left = new FormAttachment(middle, 0); - fdGlobalZoom.right = new FormAttachment(100, -margin); - fdGlobalZoom.top = new FormAttachment(wlGlobalZoom, 0, SWT.CENTER); - wGlobalZoom.setLayoutData(fdGlobalZoom); - // set the current value - String globalZoomFactor = Integer.toString((int) (props.getGlobalZoomFactor() * 100)) + '%'; - wGlobalZoom.setText(globalZoomFactor); - wGlobalZoom.addListener(SWT.Modify, e -> saveValues()); - - // GridSize line - Label wlGridSize = new Label(wLookComp, SWT.RIGHT); - wlGridSize.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.GridSize.Label")); - wlGridSize.setToolTipText(BaseMessages.getString(PKG, "EnterOptionsDialog.GridSize.ToolTip")); - PropsUi.setLook(wlGridSize); - FormData fdlGridSize = new FormData(); - fdlGridSize.left = new FormAttachment(0, 0); - fdlGridSize.right = new FormAttachment(middle, -margin); - fdlGridSize.top = new FormAttachment(wGlobalZoom, margin); - wlGridSize.setLayoutData(fdlGridSize); - wGridSize = new Text(wLookComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - wGridSize.setText(Integer.toString(props.getCanvasGridSize())); - wGridSize.setToolTipText(BaseMessages.getString(PKG, "EnterOptionsDialog.GridSize.ToolTip")); - PropsUi.setLook(wGridSize); - FormData fdGridSize = new FormData(); - fdGridSize.left = new FormAttachment(middle, 0); - fdGridSize.right = new FormAttachment(100, -margin); - fdGridSize.top = new FormAttachment(wlGridSize, 0, SWT.CENTER); - wGridSize.setLayoutData(fdGridSize); - wGridSize.addListener(SWT.Modify, e -> saveValues()); - - // Show Canvas Grid - Label wlShowCanvasGrid = new Label(wLookComp, SWT.RIGHT); - wlShowCanvasGrid.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ShowCanvasGrid.Label")); - wlShowCanvasGrid.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ShowCanvasGrid.ToolTip")); - PropsUi.setLook(wlShowCanvasGrid); - FormData fdlShowCanvasGrid = new FormData(); - fdlShowCanvasGrid.left = new FormAttachment(0, 0); - fdlShowCanvasGrid.right = new FormAttachment(middle, -margin); - fdlShowCanvasGrid.top = new FormAttachment(wGridSize, margin); - wlShowCanvasGrid.setLayoutData(fdlShowCanvasGrid); - wShowCanvasGrid = new Button(wLookComp, SWT.CHECK); - PropsUi.setLook(wShowCanvasGrid); - wShowCanvasGrid.setSelection(props.isShowCanvasGridEnabled()); - FormData fdShowCanvasGrid = new FormData(); - fdShowCanvasGrid.left = new FormAttachment(middle, 0); - fdShowCanvasGrid.right = new FormAttachment(100, -margin); - fdShowCanvasGrid.top = new FormAttachment(wlShowCanvasGrid, 0, SWT.CENTER); - wShowCanvasGrid.setLayoutData(fdShowCanvasGrid); - wShowCanvasGrid.addListener(SWT.Selection, e -> saveValues()); - - // Hide Viewport - Label wlHideViewport = new Label(wLookComp, SWT.RIGHT); - wlHideViewport.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.ShowViewport.Label")); - wlHideViewport.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ShowViewport.ToolTip")); - PropsUi.setLook(wlHideViewport); - FormData fdlHideViewport = new FormData(); - fdlHideViewport.left = new FormAttachment(0, 0); - fdlHideViewport.right = new FormAttachment(middle, -margin); - fdlHideViewport.top = new FormAttachment(wShowCanvasGrid, margin); - wlHideViewport.setLayoutData(fdlHideViewport); - wHideViewport = new Button(wLookComp, SWT.CHECK); - PropsUi.setLook(wHideViewport); - wHideViewport.setSelection(props.isHideViewportEnabled()); - FormData fdHideViewport = new FormData(); - fdHideViewport.left = new FormAttachment(middle, 0); - fdHideViewport.right = new FormAttachment(100, -margin); - fdHideViewport.top = new FormAttachment(wlHideViewport, 0, SWT.CENTER); - wHideViewport.setLayoutData(fdHideViewport); - wHideViewport.addListener(SWT.Selection, e -> saveValues()); - - // Hide menu bar? - Label wlHideMenuBar = new Label(wLookComp, SWT.RIGHT); - wlHideMenuBar.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.HideMenuBar.Label")); - wlHideMenuBar.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.HideMenuBar.ToolTip")); - PropsUi.setLook(wlHideMenuBar); - FormData fdlHideMenuBar = new FormData(); - fdlHideMenuBar.left = new FormAttachment(0, 0); - fdlHideMenuBar.right = new FormAttachment(middle, -margin); - fdlHideMenuBar.top = new FormAttachment(wHideViewport, 2 * margin); - wlHideMenuBar.setLayoutData(fdlHideMenuBar); - wHideMenuBar = new Button(wLookComp, SWT.CHECK); - PropsUi.setLook(wHideMenuBar); - wHideMenuBar.setSelection(props.isHidingMenuBar()); - FormData fdHideMenuBar = new FormData(); - fdHideMenuBar.left = new FormAttachment(middle, 0); - fdHideMenuBar.right = new FormAttachment(100, -margin); - fdHideMenuBar.top = new FormAttachment(wlHideMenuBar, 0, SWT.CENTER); - wHideMenuBar.setLayoutData(fdHideMenuBar); - wHideMenuBar.addListener(SWT.Selection, e -> saveValues()); - - // Show tableview tool bar ? - Label wlShowTableViewToolbar = new Label(wLookComp, SWT.RIGHT); - wlShowTableViewToolbar.setText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ShowTableViewToolbar.Label")); - wlShowTableViewToolbar.setToolTipText( - BaseMessages.getString(PKG, "EnterOptionsDialog.ShowTableViewToolbar.ToolTip")); - PropsUi.setLook(wlShowTableViewToolbar); - FormData fdlShowTableViewToolbar = new FormData(); - fdlShowTableViewToolbar.left = new FormAttachment(0, 0); - fdlShowTableViewToolbar.right = new FormAttachment(middle, -margin); - fdlShowTableViewToolbar.top = new FormAttachment(wHideMenuBar, 2 * margin); - wlShowTableViewToolbar.setLayoutData(fdlShowTableViewToolbar); - wShowTableViewToolbar = new Button(wLookComp, SWT.CHECK); - PropsUi.setLook(wShowTableViewToolbar); - wShowTableViewToolbar.setSelection(props.isShowTableViewToolbar()); - FormData fdShowTableViewToolbar = new FormData(); - fdShowTableViewToolbar.left = new FormAttachment(middle, 0); - fdShowTableViewToolbar.right = new FormAttachment(100, -margin); - fdShowTableViewToolbar.top = new FormAttachment(wlShowTableViewToolbar, 0, SWT.CENTER); - wShowTableViewToolbar.setLayoutData(fdShowTableViewToolbar); - wShowTableViewToolbar.addListener(SWT.Selection, e -> saveValues()); - - // Is Dark Mode enabled - Label wlDarkMode = new Label(wLookComp, SWT.RIGHT); - wlDarkMode.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.DarkMode.Label")); - PropsUi.setLook(wlDarkMode); - FormData fdlDarkMode = new FormData(); - fdlDarkMode.left = new FormAttachment(0, 0); - fdlDarkMode.top = new FormAttachment(wShowTableViewToolbar, 2 * margin); - fdlDarkMode.right = new FormAttachment(middle, -margin); - wlDarkMode.setLayoutData(fdlDarkMode); - wDarkMode = new Button(wLookComp, SWT.CHECK); - wDarkMode.setSelection(props.isDarkMode()); - PropsUi.setLook(wDarkMode); - FormData fdDarkMode = new FormData(); - fdDarkMode.left = new FormAttachment(middle, 0); - fdDarkMode.top = new FormAttachment(wlDarkMode, 0, SWT.CENTER); - fdDarkMode.right = new FormAttachment(100, 0); - wDarkMode.setLayoutData(fdDarkMode); - wlDarkMode.setEnabled(Const.isWindows()); - wDarkMode.setEnabled(Const.isWindows()); - wDarkMode.addListener(SWT.Selection, e -> saveValues()); - - // DefaultLocale line - Label wlDefaultLocale = new Label(wLookComp, SWT.RIGHT); - wlDefaultLocale.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.DefaultLocale.Label")); - PropsUi.setLook(wlDefaultLocale); - FormData fdlDefaultLocale = new FormData(); - fdlDefaultLocale.left = new FormAttachment(0, 0); - fdlDefaultLocale.right = new FormAttachment(middle, -margin); - fdlDefaultLocale.top = new FormAttachment(wlDarkMode, 2 * margin); - wlDefaultLocale.setLayoutData(fdlDefaultLocale); - wDefaultLocale = new Combo(wLookComp, SWT.SINGLE | SWT.READ_ONLY | SWT.LEFT | SWT.BORDER); - wDefaultLocale.setItems(GlobalMessages.localeDescr); - PropsUi.setLook(wDefaultLocale); - FormData fdDefaultLocale = new FormData(); - fdDefaultLocale.left = new FormAttachment(middle, 0); - fdDefaultLocale.right = new FormAttachment(100, -margin); - fdDefaultLocale.top = new FormAttachment(wlDefaultLocale, 0, SWT.CENTER); - wDefaultLocale.setLayoutData(fdDefaultLocale); - wDefaultLocale.addListener(SWT.Modify, e -> saveValues()); - - // language selections... + // Track the last control for vertical positioning + org.eclipse.swt.widgets.Control lastControl = null; + + // Preferred language - at the top + org.eclipse.swt.widgets.Control[] defaultLocaleControls = + createComboField( + wLookComp, + "EnterOptionsDialog.DefaultLocale.Label", + null, + GlobalMessages.localeDescr, + lastControl, + margin); + wDefaultLocale = (Combo) defaultLocaleControls[1]; int idxDefault = Const.indexOfString( LanguageChoice.getInstance().getDefaultLocale().toString(), GlobalMessages.localeCodes); if (idxDefault >= 0) { wDefaultLocale.select(idxDefault); } + lastControl = wDefaultLocale; + + // Hide menu bar - at the top + wHideMenuBar = + createCheckbox( + wLookComp, + "EnterOptionsDialog.HideMenuBar.Label", + "EnterOptionsDialog.HideMenuBar.ToolTip", + props.isHidingMenuBar(), + lastControl, + margin); + lastControl = wHideMenuBar; + + // Dark mode (Windows only) + wDarkMode = + createCheckbox( + wLookComp, + "EnterOptionsDialog.DarkMode.Label", + null, + props.isDarkMode(), + lastControl, + margin); + wDarkMode.setEnabled(Const.isWindows()); + lastControl = wDarkMode; + + // General appearance section - using ExpandBar + ExpandBar appearanceExpandBar = new ExpandBar(wLookComp, SWT.V_SCROLL); + PropsUi.setLook(appearanceExpandBar); + + FormData fdAppearanceExpandBar = new FormData(); + fdAppearanceExpandBar.left = new FormAttachment(0, 0); + fdAppearanceExpandBar.right = new FormAttachment(100, 0); + fdAppearanceExpandBar.top = new FormAttachment(lastControl, 2 * margin); + appearanceExpandBar.setLayoutData(fdAppearanceExpandBar); + + // Create expandable item for appearance + Composite appearanceContent = new Composite(appearanceExpandBar, SWT.NONE); + PropsUi.setLook(appearanceContent); + FormLayout appearanceLayout = new FormLayout(); + appearanceLayout.marginWidth = PropsUi.getFormMargin(); + appearanceLayout.marginHeight = PropsUi.getFormMargin(); + appearanceContent.setLayout(appearanceLayout); + + // Appearance controls inside the expandable content + org.eclipse.swt.widgets.Control lastAppearanceControl = null; + + // Global zoom (at the top) + org.eclipse.swt.widgets.Control[] globalZoomControls = + createComboField( + appearanceContent, + "EnterOptionsDialog.GlobalZoom.Label", + null, + PropsUi.getGlobalZoomFactorLevels(), + lastAppearanceControl, + margin); + wGlobalZoom = (Combo) globalZoomControls[1]; + String globalZoomFactor = Integer.toString((int) (props.getGlobalZoomFactor() * 100)) + '%'; + wGlobalZoom.setText(globalZoomFactor); + lastAppearanceControl = wGlobalZoom; + + // Icon size + org.eclipse.swt.widgets.Control[] iconSizeControls = + createTextField( + appearanceContent, + "EnterOptionsDialog.IconSize.Label", + null, + Integer.toString(props.getIconSize()), + lastAppearanceControl, + margin); + wIconSize = (Text) iconSizeControls[1]; + wIconSize.setMessage(BaseMessages.getString(PKG, ENTER_OPTIONS_DIALOG_ENTER_NUMBER_HINT)); + wIconSize.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; + } + }); + lastAppearanceControl = wIconSize; + + // Line width + org.eclipse.swt.widgets.Control[] lineWidthControls = + createTextField( + appearanceContent, + "EnterOptionsDialog.LineWidth.Label", + null, + Integer.toString(props.getLineWidth()), + lastAppearanceControl, + margin); + wLineWidth = (Text) lineWidthControls[1]; + wLineWidth.setMessage(BaseMessages.getString(PKG, ENTER_OPTIONS_DIALOG_ENTER_NUMBER_HINT)); + wLineWidth.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; + } + }); + lastAppearanceControl = wLineWidth; + + // Dialog middle percentage + org.eclipse.swt.widgets.Control[] middlePctControls = + createTextField( + appearanceContent, + "EnterOptionsDialog.DialogMiddlePercentage.Label", + null, + Integer.toString(props.getMiddlePct()), + lastAppearanceControl, + margin); + wMiddlePct = (Text) middlePctControls[1]; + wMiddlePct.setMessage(BaseMessages.getString(PKG, ENTER_OPTIONS_DIALOG_ENTER_NUMBER_HINT)); + wMiddlePct.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; + } + }); + + // Create the general appearance expand item + ExpandItem appearanceItem = new ExpandItem(appearanceExpandBar, SWT.NONE); + appearanceItem.setText( + BaseMessages.getString(PKG, "EnterOptionsDialog.Section.GeneralAppearance")); + appearanceItem.setControl(appearanceContent); + appearanceItem.setHeight(appearanceContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + appearanceItem.setExpanded(true); // Start expanded + + // Add expand/collapse listeners for space reclamation + appearanceExpandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + appearanceExpandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + + lastControl = appearanceExpandBar; + + // Fonts section - using ExpandBar + ExpandBar fontsExpandBar = new ExpandBar(wLookComp, SWT.V_SCROLL); + PropsUi.setLook(fontsExpandBar); + + FormData fdFontsExpandBar = new FormData(); + fdFontsExpandBar.left = new FormAttachment(0, 0); + fdFontsExpandBar.right = new FormAttachment(100, 0); + fdFontsExpandBar.top = new FormAttachment(lastControl, 2 * margin); + fontsExpandBar.setLayoutData(fdFontsExpandBar); + + // Create expandable item for fonts + Composite fontsContent = new Composite(fontsExpandBar, SWT.NONE); + PropsUi.setLook(fontsContent); + FormLayout fontsLayout = new FormLayout(); + fontsLayout.marginWidth = PropsUi.getFormMargin(); + fontsLayout.marginHeight = PropsUi.getFormMargin(); + fontsContent.setLayout(fontsLayout); + + // Fonts inside the expandable content + org.eclipse.swt.widgets.Control lastFontControl = null; + + // Default font + org.eclipse.swt.widgets.Control[] defaultFontControls = + createFontPicker( + fontsContent, "EnterOptionsDialog.DefaultFont.Label", shell, lastFontControl, margin); + wDefaultCanvas = (Canvas) defaultFontControls[0]; + wDefaultCanvas.addPaintListener(this::paintDefaultFont); + wDefaultCanvas.addListener(SWT.MouseDown, e -> editDefaultFont(shell)); + Button wbDefaultFont = (Button) defaultFontControls[1]; + wbDefaultFont.addListener(SWT.Selection, e -> editDefaultFont(shell)); + Button wdDefaultFont = (Button) defaultFontControls[2]; + wdDefaultFont.addListener(SWT.Selection, e -> resetDefaultFont(shell)); + lastFontControl = wDefaultCanvas; + + // Fixed width font + org.eclipse.swt.widgets.Control[] fixedFontControls = + createFontPicker( + fontsContent, + "EnterOptionsDialog.FixedWidthFont.Label", + shell, + lastFontControl, + margin); + wFixedCanvas = (Canvas) fixedFontControls[0]; + wFixedCanvas.addPaintListener(this::paintFixedFont); + wFixedCanvas.addListener(SWT.MouseDown, e -> editFixedFont(shell)); + Button wbFixedFont = (Button) fixedFontControls[1]; + wbFixedFont.addListener(SWT.Selection, e -> editFixedFont(shell)); + Button wdFixedFont = (Button) fixedFontControls[2]; + wdFixedFont.addListener(SWT.Selection, e -> resetFixedFont(shell)); + lastFontControl = wFixedCanvas; + + // Graph font + org.eclipse.swt.widgets.Control[] graphFontControls = + createFontPicker( + fontsContent, "EnterOptionsDialog.GraphFont.Label", shell, lastFontControl, margin); + wGraphCanvas = (Canvas) graphFontControls[0]; + wGraphCanvas.addPaintListener(this::drawGraphFont); + wGraphCanvas.addListener(SWT.MouseDown, e -> editGraphFont(shell)); + Button wbGraphFont = (Button) graphFontControls[1]; + wbGraphFont.addListener(SWT.Selection, e -> editGraphFont(shell)); + Button wdGraphFont = (Button) graphFontControls[2]; + wdGraphFont.addListener(SWT.Selection, e -> resetGraphFont(shell, props)); + lastFontControl = wGraphCanvas; + + // Note font + org.eclipse.swt.widgets.Control[] noteFontControls = + createFontPicker( + fontsContent, "EnterOptionsDialog.NoteFont.Label", shell, lastFontControl, margin); + wNoteCanvas = (Canvas) noteFontControls[0]; + wNoteCanvas.addPaintListener(this::paintNoteFont); + wNoteCanvas.addListener(SWT.MouseDown, e -> editNoteFont(shell)); + Button wbNoteFont = (Button) noteFontControls[1]; + wbNoteFont.addListener(SWT.Selection, e -> editNoteFont(shell)); + Button wdNoteFont = (Button) noteFontControls[2]; + wdNoteFont.addListener(SWT.Selection, e -> resetNoteFont(e, props, shell.getDisplay())); + + // Create the fonts expand item + ExpandItem fontsItem = new ExpandItem(fontsExpandBar, SWT.NONE); + fontsItem.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.Section.Fonts")); + fontsItem.setControl(fontsContent); + fontsItem.setHeight(fontsContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + fontsItem.setExpanded(true); // Start expanded + + // Add expand/collapse listeners for space reclamation + fontsExpandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + fontsExpandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + + lastControl = fontsExpandBar; + + // Pipeline & Workflow canvas section - using ExpandBar + ExpandBar canvasExpandBar = new ExpandBar(wLookComp, SWT.V_SCROLL); + PropsUi.setLook(canvasExpandBar); + + FormData fdCanvasExpandBar = new FormData(); + fdCanvasExpandBar.left = new FormAttachment(0, 0); + fdCanvasExpandBar.right = new FormAttachment(100, 0); + fdCanvasExpandBar.top = new FormAttachment(lastControl, 2 * margin); + canvasExpandBar.setLayoutData(fdCanvasExpandBar); + + // Create expandable item for canvas settings + Composite canvasContent = new Composite(canvasExpandBar, SWT.NONE); + PropsUi.setLook(canvasContent); + FormLayout canvasLayout = new FormLayout(); + canvasLayout.marginWidth = PropsUi.getFormMargin(); + canvasLayout.marginHeight = PropsUi.getFormMargin(); + canvasContent.setLayout(canvasLayout); + + // Show canvas grid checkbox inside the expandable content + org.eclipse.swt.widgets.Control lastCanvasControl = null; + wShowCanvasGrid = + createCheckbox( + canvasContent, + "EnterOptionsDialog.ShowCanvasGrid.Label", + "EnterOptionsDialog.ShowCanvasGrid.ToolTip", + props.isShowCanvasGridEnabled(), + lastCanvasControl, + margin); + lastCanvasControl = wShowCanvasGrid; + + // Grid size - placed under Show canvas grid checkbox + org.eclipse.swt.widgets.Control[] gridSizeControls = + createTextField( + canvasContent, + "EnterOptionsDialog.GridSize.Label", + "EnterOptionsDialog.GridSize.ToolTip", + Integer.toString(props.getCanvasGridSize()), + lastCanvasControl, + margin); + wGridSize = (Text) gridSizeControls[1]; + wlGridSize = (Label) gridSizeControls[0]; + wGridSize.setMessage(BaseMessages.getString(PKG, ENTER_OPTIONS_DIALOG_ENTER_NUMBER_HINT)); + wGridSize.addListener( + SWT.Verify, + e -> { + String currentText = ((Text) e.widget).getText(); + String newText = + currentText.substring(0, e.start) + e.text + currentText.substring(e.end); + if (!newText.isEmpty() && !newText.matches("\\d+")) { + e.doit = false; + } + }); + + lastCanvasControl = wGridSize; + + // Show viewport checkbox (inverted logic from hideViewport) + wHideViewport = + createCheckbox( + canvasContent, + "EnterOptionsDialog.ShowViewport.Label", + "EnterOptionsDialog.ShowViewport.ToolTip", + !props.isHideViewportEnabled(), + lastCanvasControl, + margin); + lastCanvasControl = wHideViewport; + + // Use double click on canvas + wUseDoubleClick = + createCheckbox( + canvasContent, + "EnterOptionsDialog.UseDoubleClickOnCanvas.Label", + null, + props.useDoubleClick(), + lastCanvasControl, + margin); + lastCanvasControl = wUseDoubleClick; + + // Draw border around canvas names + wDrawBorderAroundCanvasNames = + createCheckbox( + canvasContent, + "EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label", + null, + props.isBorderDrawnAroundCanvasNames(), + lastCanvasControl, + margin); + + // Create the expand item + ExpandItem canvasItem = new ExpandItem(canvasExpandBar, SWT.NONE); + canvasItem.setText( + BaseMessages.getString(PKG, "EnterOptionsDialog.Section.PipelineWorkflowCanvas")); + canvasItem.setControl(canvasContent); + canvasItem.setHeight(canvasContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + canvasItem.setExpanded(true); // Start expanded + + // Add expand/collapse listeners for space reclamation + canvasExpandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + canvasExpandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + + lastControl = canvasExpandBar; + + // Tables & grids section - using ExpandBar + ExpandBar tablesExpandBar = new ExpandBar(wLookComp, SWT.V_SCROLL); + PropsUi.setLook(tablesExpandBar); + + FormData fdTablesExpandBar = new FormData(); + fdTablesExpandBar.left = new FormAttachment(0, 0); + fdTablesExpandBar.right = new FormAttachment(100, 0); + fdTablesExpandBar.top = new FormAttachment(lastControl, 2 * margin); + tablesExpandBar.setLayoutData(fdTablesExpandBar); + + // Create expandable item for tables & grids + Composite tablesContent = new Composite(tablesExpandBar, SWT.NONE); + PropsUi.setLook(tablesContent); + FormLayout tablesLayout = new FormLayout(); + tablesLayout.marginWidth = PropsUi.getFormMargin(); + tablesLayout.marginHeight = PropsUi.getFormMargin(); + tablesContent.setLayout(tablesLayout); + + // Show toolbar checkbox inside the expandable content + org.eclipse.swt.widgets.Control lastTablesControl = null; + wShowTableViewToolbar = + createCheckbox( + tablesContent, + "EnterOptionsDialog.ShowTableViewToolbar.Label", + "EnterOptionsDialog.ShowTableViewToolbar.ToolTip", + props.isShowTableViewToolbar(), + lastTablesControl, + margin); + + // Create the expand item + ExpandItem tablesItem = new ExpandItem(tablesExpandBar, SWT.NONE); + tablesItem.setText(BaseMessages.getString(PKG, "EnterOptionsDialog.Section.TablesGrids")); + tablesItem.setControl(tablesContent); + tablesItem.setHeight(tablesContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + tablesItem.setExpanded(true); // Start expanded + + // Add expand/collapse listeners for space reclamation + tablesExpandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + tablesExpandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!wLookComp.isDisposed() && !sLookComp.isDisposed()) { + wLookComp.layout(); + sLookComp.setMinHeight(wLookComp.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); FormData fdLookComp = new FormData(); fdLookComp.left = new FormAttachment(0, 0); @@ -550,6 +707,9 @@ public void addGuiOptionsTab(CTabFolder wTabFolder) { sLookComp.setMinHeight(bounds.height); wLookTab.setControl(sLookComp); + + // Reset initialization flag so saveValues can work normally + isInitializing = false; } private void paintNoteFont(PaintEvent pe) { @@ -726,6 +886,11 @@ private FormData layoutEditOptionButton(Button button) { } private void saveValues() { + // Don't save if we're currently reloading values or initializing widgets + if (isReloading || isInitializing) { + return; + } + PropsUi props = PropsUi.getInstance(); props.setDefaultFont(defaultFontData); @@ -738,7 +903,10 @@ private void saveValues() { props.setCanvasGridSize(Const.toInt(wGridSize.getText(), 1)); props.setGlobalZoomFactor(Const.toDouble(wGlobalZoom.getText().replace("%", ""), 100) / 100); props.setShowCanvasGridEnabled(wShowCanvasGrid.getSelection()); - props.setHideViewportEnabled(wHideViewport.getSelection()); + props.setHideViewportEnabled( + !wHideViewport.getSelection()); // Inverted: checkbox is "show", property is "hide" + props.setUseDoubleClickOnCanvas(wUseDoubleClick.getSelection()); + props.setDrawBorderAroundCanvasNames(wDrawBorderAroundCanvasNames.getSelection()); props.setDarkMode(wDarkMode.getSelection()); props.setHidingMenuBar(wHideMenuBar.getSelection()); props.setShowTableViewToolbar(wShowTableViewToolbar.getSelection()); @@ -759,4 +927,210 @@ private void saveValues() { HopGui.getInstance().getShell(), "Error", "Error saving configuration to file", e); } } + + /** + * Creates a text field with label above it. + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param initialValue The initial text value + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return An array containing [Label, Text] controls + */ + private org.eclipse.swt.widgets.Control[] createTextField( + Composite parent, + String labelKey, + String tooltipKey, + String initialValue, + org.eclipse.swt.widgets.Control lastControl, + int margin) { + // Label above + Label label = new Label(parent, SWT.LEFT); + PropsUi.setLook(label); + label.setText(BaseMessages.getString(PKG, labelKey)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdLabel.top = new FormAttachment(lastControl, margin); + } else { + fdLabel.top = new FormAttachment(0, margin); + } + label.setLayoutData(fdLabel); + + // Text field below label + Text text = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + PropsUi.setLook(text); + text.setText(initialValue); + if (tooltipKey != null) { + text.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + text.addListener(SWT.Modify, e -> saveValues()); + + FormData fdText = new FormData(); + fdText.left = new FormAttachment(0, 0); + fdText.right = new FormAttachment(100, 0); + fdText.top = new FormAttachment(label, margin / 2); + text.setLayoutData(fdText); + + return new org.eclipse.swt.widgets.Control[] {label, text}; + } + + /** + * Creates a combo field with label above it. + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param items The items for the combo + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return An array containing [Label, Combo] controls + */ + private org.eclipse.swt.widgets.Control[] createComboField( + Composite parent, + String labelKey, + String tooltipKey, + String[] items, + org.eclipse.swt.widgets.Control lastControl, + int margin) { + // Label above + Label label = new Label(parent, SWT.LEFT); + PropsUi.setLook(label); + label.setText(BaseMessages.getString(PKG, labelKey)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdLabel.top = new FormAttachment(lastControl, margin); + } else { + fdLabel.top = new FormAttachment(0, margin); + } + label.setLayoutData(fdLabel); + + // Combo field below label + Combo combo = new Combo(parent, SWT.SINGLE | SWT.READ_ONLY | SWT.LEFT | SWT.BORDER); + PropsUi.setLook(combo); + combo.setItems(items); + if (tooltipKey != null) { + combo.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + combo.addListener(SWT.Modify, e -> saveValues()); + + FormData fdCombo = new FormData(); + fdCombo.left = new FormAttachment(0, 0); + fdCombo.right = new FormAttachment(100, 0); + fdCombo.top = new FormAttachment(label, margin / 2); + combo.setLayoutData(fdCombo); + + return new org.eclipse.swt.widgets.Control[] {label, combo}; + } + + /** + * Creates a checkbox with the checkbox in front of the label text. + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param tooltipKey Optional tooltip message key (can be null) + * @param selected Whether the checkbox is initially selected + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return The created Button (checkbox) + */ + private Button createCheckbox( + Composite parent, + String labelKey, + String tooltipKey, + boolean selected, + org.eclipse.swt.widgets.Control lastControl, + int margin) { + Button checkbox = new Button(parent, SWT.CHECK); + PropsUi.setLook(checkbox); + checkbox.setText(BaseMessages.getString(PKG, labelKey)); + if (tooltipKey != null) { + checkbox.setToolTipText(BaseMessages.getString(PKG, tooltipKey)); + } + checkbox.setSelection(selected); + checkbox.addListener(SWT.Selection, e -> saveValues()); + + FormData fdCheckbox = new FormData(); + fdCheckbox.left = new FormAttachment(0, 0); + fdCheckbox.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdCheckbox.top = new FormAttachment(lastControl, margin); + } else { + fdCheckbox.top = new FormAttachment(0, margin); + } + checkbox.setLayoutData(fdCheckbox); + + return checkbox; + } + + /** + * Creates a font picker with label above, canvas preview, and edit/reset buttons. + * + * @param parent The parent composite + * @param labelKey The message key for the label text + * @param shell The shell for opening dialogs + * @param lastControl The last control to attach to + * @param margin The margin to use + * @return An array containing [Canvas, EditButton, ResetButton] controls + */ + private org.eclipse.swt.widgets.Control[] createFontPicker( + Composite parent, + String labelKey, + Shell shell, + org.eclipse.swt.widgets.Control lastControl, + int margin) { + int h = (int) (40 * PropsUi.getInstance().getZoomFactor()); + + // Label above + Label label = new Label(parent, SWT.LEFT); + PropsUi.setLook(label); + label.setText(BaseMessages.getString(PKG, labelKey)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdLabel.top = new FormAttachment(lastControl, margin); + } else { + fdLabel.top = new FormAttachment(0, margin); + } + label.setLayoutData(fdLabel); + + // Reset button (right) + Button resetButton = new Button(parent, SWT.PUSH | SWT.CENTER); + PropsUi.setLook(resetButton); + FormData fdResetButton = layoutResetOptionButton(resetButton); + fdResetButton.right = new FormAttachment(100, 0); + fdResetButton.top = new FormAttachment(label, margin / 2); + fdResetButton.height = h; // Match canvas height + resetButton.setLayoutData(fdResetButton); + + // Edit button (next to reset button) + Button editButton = new Button(parent, SWT.PUSH); + PropsUi.setLook(editButton); + FormData fdEditButton = layoutEditOptionButton(editButton); + fdEditButton.right = new FormAttachment(resetButton, -margin); + fdEditButton.top = new FormAttachment(label, margin / 2); + fdEditButton.height = h; // Match canvas height + editButton.setLayoutData(fdEditButton); + + // Canvas preview (left side) + Canvas canvas = new Canvas(parent, SWT.BORDER); + PropsUi.setLook(canvas); + FormData fdCanvas = new FormData(); + fdCanvas.left = new FormAttachment(0, 0); + fdCanvas.right = new FormAttachment(editButton, -margin); + fdCanvas.top = new FormAttachment(label, margin / 2); + fdCanvas.height = h; + canvas.setLayoutData(fdCanvas); + + return new org.eclipse.swt.widgets.Control[] {canvas, editButton, resetButton}; + } } diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigKeyboardShortcutsTab.java b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigKeyboardShortcutsTab.java new file mode 100644 index 00000000000..5e186c9bd55 --- /dev/null +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigKeyboardShortcutsTab.java @@ -0,0 +1,654 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.hop.ui.hopgui.perspective.configuration.tabs; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.hop.core.Const; +import org.apache.hop.core.gui.plugin.GuiPlugin; +import org.apache.hop.core.gui.plugin.GuiRegistry; +import org.apache.hop.core.gui.plugin.key.KeyboardShortcut; +import org.apache.hop.core.gui.plugin.tab.GuiTab; +import org.apache.hop.core.util.TranslateUtil; +import org.apache.hop.core.util.Utils; +import org.apache.hop.i18n.BaseMessages; +import org.apache.hop.ui.core.PropsUi; +import org.apache.hop.ui.core.dialog.BaseDialog; +import org.apache.hop.ui.core.gui.GuiResource; +import org.apache.hop.ui.hopgui.perspective.configuration.ConfigurationPerspective; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.ExpandBar; +import org.eclipse.swt.widgets.ExpandItem; +import org.eclipse.swt.widgets.Label; + +@GuiPlugin +public class ConfigKeyboardShortcutsTab { + + private static final Class PKG = BaseDialog.class; + + private Font monoFont; + private Color keyBackgroundColor; + + public ConfigKeyboardShortcutsTab() { + // Constructor for GuiPlugin instantiation + } + + @GuiTab( + id = "10150-config-perspective-keyboard-shortcuts-tab", + parentId = ConfigurationPerspective.CONFIG_PERSPECTIVE_TABS, + description = "Keyboard shortcuts tab") + public void addKeyboardShortcutsTab(CTabFolder wTabFolder) { + int margin = PropsUi.getMargin(); + + CTabItem wShortcutsTab = new CTabItem(wTabFolder, SWT.NONE); + wShortcutsTab.setFont(GuiResource.getInstance().getFontDefault()); + wShortcutsTab.setText("Keyboard Shortcuts"); + wShortcutsTab.setImage(GuiResource.getInstance().getImageEdit()); + + // Main scrollable composite + ScrolledComposite scrolledComposite = + new ScrolledComposite(wTabFolder, SWT.V_SCROLL | SWT.H_SCROLL); + scrolledComposite.setLayout(new FormLayout()); + + Composite contentComposite = new Composite(scrolledComposite, SWT.NONE); + PropsUi.setLook(contentComposite); + contentComposite.setLayout(new FormLayout()); + + // Initialize colors and fonts + Display display = wTabFolder.getDisplay(); + keyBackgroundColor = new Color(display, 240, 240, 245); + Color keyBorderColor = new Color(display, 180, 180, 190); + + // Create a monospace font for key labels + FontData[] fontData = display.getSystemFont().getFontData(); + if (fontData.length > 0) { + FontData monoFontData = + new FontData(fontData[0].getName(), fontData[0].getHeight(), SWT.BOLD); + monoFont = new Font(display, monoFontData); + } + + // Get all keyboard shortcuts from the registry + GuiRegistry guiRegistry = GuiRegistry.getInstance(); + Map> shortcutsMap = guiRegistry.getShortCutsMap(); + + // Determine if we're on macOS + boolean isMacOS = Const.isOSX(); + + // Create expandable sections grouped by class + Control lastControl = null; + List classNames = new ArrayList<>(shortcutsMap.keySet()); + Collections.sort(classNames); + + for (String className : classNames) { + List allShortcuts = shortcutsMap.get(className); + if (allShortcuts == null || allShortcuts.isEmpty()) { + continue; + } + + // Filter shortcuts to only show those relevant to the current OS + List shortcuts = new ArrayList<>(); + for (KeyboardShortcut shortcut : allShortcuts) { + if (shortcut.isOsx() == isMacOS) { + shortcuts.add(shortcut); + } + } + + // Skip this class if no relevant shortcuts after filtering + if (shortcuts.isEmpty()) { + continue; + } + + // Sort shortcuts alphabetically by their formatted method names + shortcuts.sort( + (s1, s2) -> + formatMethodName(s1.getParentMethodName()) + .compareTo(formatMethodName(s2.getParentMethodName()))); + + // Create expand bar for this class + ExpandBar expandBar = new ExpandBar(contentComposite, SWT.NONE); + PropsUi.setLook(expandBar); + + // Create the content composite for this expand item + Composite expandContent = new Composite(expandBar, SWT.NONE); + PropsUi.setLook(expandContent); + FormLayout expandLayout = new FormLayout(); + expandLayout.marginWidth = PropsUi.getFormMargin(); + expandLayout.marginHeight = PropsUi.getFormMargin(); + expandContent.setLayout(expandLayout); + + // Add shortcuts to this section + Control lastShortcut = null; + for (KeyboardShortcut shortcut : shortcuts) { + lastShortcut = createShortcutRow(expandContent, shortcut, lastShortcut, margin); + } + + // Set the expand item + ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE); + expandItem.setText(getPluginName(className)); + expandItem.setControl(expandContent); + expandItem.setExpanded(true); + expandItem.setHeight(expandContent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + + // Set the ExpandBar layout (no height constraint) + FormData fdExpandBar = new FormData(); + fdExpandBar.left = new FormAttachment(0, 0); + fdExpandBar.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdExpandBar.top = new FormAttachment(lastControl, margin); + } else { + fdExpandBar.top = new FormAttachment(0, margin); + } + expandBar.setLayoutData(fdExpandBar); + + // Add expand/collapse listeners to this specific expand bar + expandBar.addListener( + SWT.Expand, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!contentComposite.isDisposed() && !scrolledComposite.isDisposed()) { + contentComposite.layout(); + scrolledComposite.setMinHeight( + contentComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + expandBar.addListener( + SWT.Collapse, + e -> + Display.getDefault() + .asyncExec( + () -> { + if (!contentComposite.isDisposed() && !scrolledComposite.isDisposed()) { + contentComposite.layout(); + scrolledComposite.setMinHeight( + contentComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); + } + })); + + lastControl = expandBar; + } + + // Add symbol reference section at the bottom for macOS + if (Const.isOSX()) { + lastControl = addSymbolReference(contentComposite, lastControl, margin); + } + + // Add bottom spacer to ensure proper bottom margin + Label bottomSpacer = new Label(contentComposite, SWT.NONE); + FormData fdBottomSpacer = new FormData(); + fdBottomSpacer.left = new FormAttachment(0, 0); + fdBottomSpacer.top = new FormAttachment(lastControl, margin * 2); + fdBottomSpacer.height = 1; + bottomSpacer.setLayoutData(fdBottomSpacer); + + // Setup scrolled composite + contentComposite.layout(); + contentComposite.pack(); + scrolledComposite.setContent(contentComposite); + scrolledComposite.setExpandHorizontal(true); + scrolledComposite.setExpandVertical(true); + scrolledComposite.setMinWidth(contentComposite.getBounds().width); + scrolledComposite.setMinHeight(contentComposite.getBounds().height); + + // Add dispose listener to clean up resources + scrolledComposite.addDisposeListener( + e -> { + if (monoFont != null && !monoFont.isDisposed()) { + monoFont.dispose(); + } + if (keyBackgroundColor != null && !keyBackgroundColor.isDisposed()) { + keyBackgroundColor.dispose(); + } + if (keyBorderColor != null && !keyBorderColor.isDisposed()) { + keyBorderColor.dispose(); + } + }); + + wShortcutsTab.setControl(scrolledComposite); + } + + /** + * Creates a row displaying a keyboard shortcut. + * + * @param parent The parent composite + * @param shortcut The keyboard shortcut to display + * @param lastControl The last control for positioning + * @param margin The margin to use + * @return The created composite + */ + // Suppress the warning for "useless assignment" on lastKey as it is there for readability and + // further chaining + @SuppressWarnings("java:S1854") + private Control createShortcutRow( + Composite parent, KeyboardShortcut shortcut, Control lastControl, int margin) { + Composite row = new Composite(parent, SWT.NONE); + PropsUi.setLook(row); + row.setLayout(new FormLayout()); + + FormData fdRow = new FormData(); + fdRow.left = new FormAttachment(0, 0); + fdRow.right = new FormAttachment(100, 0); + if (lastControl != null) { + fdRow.top = new FormAttachment(lastControl, margin / 2); + } else { + fdRow.top = new FormAttachment(0, 0); + } + row.setLayoutData(fdRow); + + // Create shortcut keys display on the left - fixed width column + Composite keysComposite = new Composite(row, SWT.NONE); + PropsUi.setLook(keysComposite); + keysComposite.setLayout(new FormLayout()); + + FormData fdKeys = new FormData(); + fdKeys.left = new FormAttachment(0, 0); + fdKeys.top = new FormAttachment(0, 0); + fdKeys.bottom = new FormAttachment(100, 0); + fdKeys.width = 200; // Fixed width for shortcut column + keysComposite.setLayoutData(fdKeys); + + // Add modifier keys and main key + // Standard order: Control, Alt, Command, Shift (Command before Shift for better readability) + boolean isMacOS = Const.isOSX(); + Control lastKey = null; + if (shortcut.isControl()) { + lastKey = createKeyBadge(keysComposite, isMacOS ? "⌃" : "Ctrl", lastKey, margin); + } + if (shortcut.isAlt()) { + lastKey = createKeyBadge(keysComposite, isMacOS ? "⌥" : "Alt", lastKey, margin); + } + if (shortcut.isCommand()) { + lastKey = createKeyBadge(keysComposite, isMacOS ? "⌘" : "Cmd", lastKey, margin); + } + if (shortcut.isShift()) { + lastKey = createKeyBadge(keysComposite, isMacOS ? "⇧" : "Shift", lastKey, margin); + } + + // Add the main key + String keyText = getKeyText(shortcut.getKeyCode()); + if (!Utils.isEmpty(keyText)) { + lastKey = createKeyBadge(keysComposite, keyText, lastKey, margin); + } + + // Add method name on the right - starts at fixed position + Label methodLabel = new Label(row, SWT.LEFT); + PropsUi.setLook(methodLabel); + methodLabel.setText(formatMethodName(shortcut.getParentMethodName())); + + FormData fdMethod = new FormData(); + fdMethod.left = new FormAttachment(keysComposite, margin); + // Vertically center the label - align with badge center + fdMethod.top = new FormAttachment(0, 0); + fdMethod.right = new FormAttachment(100, 0); + methodLabel.setLayoutData(fdMethod); + + return row; + } + + /** + * Creates a styled label that looks like a keyboard key. + * + * @param parent The parent composite + * @param text The key text + * @param lastControl The last control for positioning + * @param margin The margin to use + * @return The created label + */ + private Label createKeyBadge(Composite parent, String text, Control lastControl, int margin) { + Label key = new Label(parent, SWT.CENTER | SWT.BORDER); + key.setText(text); + key.setBackground(keyBackgroundColor); + key.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); + if (monoFont != null) { + key.setFont(monoFont); + } + + FormData fdKey = new FormData(); + if (lastControl != null) { + fdKey.left = new FormAttachment(lastControl, margin / 2); + } else { + fdKey.left = new FormAttachment(0, 0); + } + fdKey.top = new FormAttachment(0, 0); + fdKey.bottom = new FormAttachment(100, 0); + // Set width based on text length - Unicode symbols need less space than text labels + fdKey.width = text.length() <= 1 ? 24 : text.length() * 14 + 12; + key.setLayoutData(fdKey); + + return key; + } + + /** + * Converts a key code to a readable string. + * + * @param keyCode The SWT key code + * @return A readable key string + */ + private String getKeyText(int keyCode) { + boolean isMacOS = Const.isOSX(); + + // Spacebar + if (keyCode == 32 || keyCode == ' ') { + return "Space"; + } + // Character upper + else if (keyCode >= 65 && keyCode <= 90) { + return String.valueOf((char) keyCode); + } + // Character lower + else if (keyCode >= 97 && keyCode <= 122) { + return String.valueOf(Character.toUpperCase((char) keyCode)); + } + // Delete key + else if (keyCode == 127) { + return isMacOS ? "⌫" : "Del"; + } + // Digit + else if ((keyCode >= 48 && keyCode <= 57) || "+-/*".indexOf(keyCode) >= 0) { + return String.valueOf((char) keyCode); + } + + // Special keys (SWT constants) + if ((keyCode & (1 << 24)) != 0) { + switch (keyCode & (0xFFFF)) { + case 1: + return "↑"; + case 2: + return "↓"; + case 3: + return "←"; + case 4: + return "→"; + case 5: + return "PgUp"; + case 6: + return "PgDn"; + case 7: + return "Home"; + case 8: + return "End"; + case 9: + return "Ins"; + case 10: + return "F1"; + case 11: + return "F2"; + case 12: + return "F3"; + case 13: + return "F4"; + case 14: + return "F5"; + case 15: + return "F6"; + case 16: + return "F7"; + case 17: + return "F8"; + case 18: + return "F9"; + case 19: + return "F10"; + case 20: + return "F11"; + case 21: + return "F12"; + case 22: + return "F13"; + case 23: + return "F14"; + case 24: + return "F15"; + case 25: + return "F16"; + case 26: + return "F17"; + case 27: + return "F18"; + case 28: + return "F19"; + case 29: + return "F20"; + default: + break; + } + } + + // ESC key + if (keyCode == SWT.ESC) { + return isMacOS ? "⎋" : "Esc"; + } + + return ""; + } + + /** + * Formats a method name to be more readable. + * + * @param methodName The method name + * @return Formatted method name + */ + private String formatMethodName(String methodName) { + if (Utils.isEmpty(methodName)) { + return ""; + } + + // Convert camelCase to Title Case with spaces + StringBuilder formatted = new StringBuilder(); + for (int i = 0; i < methodName.length(); i++) { + char c = methodName.charAt(i); + if (i == 0) { + formatted.append(Character.toUpperCase(c)); + } else if (Character.isUpperCase(c)) { + formatted.append(' ').append(c); + } else { + formatted.append(c); + } + } + + return formatted.toString(); + } + + /** + * Gets a friendly plugin name from a fully qualified class name. Checks GuiPlugin annotation for + * name or id, otherwise falls back to the simple class name. + * + * @param className The fully qualified class name + * @return The plugin name, id, or simple class name + */ + private String getPluginName(String className) { + if (Utils.isEmpty(className)) { + return ""; + } + + try { + // Try to load the class and get its GuiPlugin annotation + Class clazz = Class.forName(className); + GuiPlugin annotation = clazz.getAnnotation(GuiPlugin.class); + + if (annotation != null) { + // First try to get the name + if (!Utils.isEmpty(annotation.name())) { + String name = TranslateUtil.translate(annotation.name(), clazz); + if (!Utils.isEmpty(name)) { + return name; + } + } + + // Fall back to id if name is empty + if (!Utils.isEmpty(annotation.id())) { + return annotation.id(); + } + } + } catch (Exception e) { + // Fall through to simple class name + } + + // Fallback to simple class name + int lastDot = className.lastIndexOf('.'); + if (lastDot >= 0 && lastDot < className.length() - 1) { + return className.substring(lastDot + 1); + } + return className; + } + + /** + * Adds a reference section showing what special keyboard symbols mean. + * + * @param parent The parent composite + * @param lastControl The last control for positioning + * @param margin The margin to use + * @return The last control created in this section + */ + private Control addSymbolReference(Composite parent, Control lastControl, int margin) { + // Add separator line + Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); + FormData fdSeparator = new FormData(); + fdSeparator.left = new FormAttachment(0, 0); + fdSeparator.right = new FormAttachment(100, 0); + fdSeparator.top = new FormAttachment(lastControl, margin * 2); + separator.setLayoutData(fdSeparator); + + // Add title + Label titleLabel = new Label(parent, SWT.LEFT); + PropsUi.setLook(titleLabel); + titleLabel.setText( + BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.SymbolReference.Title")); + titleLabel.setFont(monoFont); + + FormData fdTitle = new FormData(); + fdTitle.left = new FormAttachment(0, 0); + fdTitle.top = new FormAttachment(separator, margin); + titleLabel.setLayoutData(fdTitle); + + // Add reference items in a compact grid + Composite refComposite = new Composite(parent, SWT.NONE); + PropsUi.setLook(refComposite); + refComposite.setLayout(new FormLayout()); + + FormData fdRefComposite = new FormData(); + fdRefComposite.left = new FormAttachment(0, 0); + fdRefComposite.right = new FormAttachment(100, 0); + fdRefComposite.top = new FormAttachment(titleLabel, margin / 2); + refComposite.setLayoutData(fdRefComposite); + + // Define symbols and their meanings (in priority order) + String[][] symbols = { + {"⌃", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.Control")}, + {"⌥", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.OptionAlt")}, + {"⌘", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.Command")}, + {"⇧", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.Shift")}, + {"⎋", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.Escape")}, + {"⌫", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.Delete")}, + {"↑", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.UpArrow")}, + {"↓", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.DownArrow")}, + {"←", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.LeftArrow")}, + {"→", BaseMessages.getString(PKG, "ConfigKeyboardShortcutsTab.Key.RightArrow")} + }; + + Control lastRef = null; + int itemsPerRow = 5; + int currentItem = 0; + + for (String[] symbol : symbols) { + Composite itemComposite = new Composite(refComposite, SWT.NONE); + PropsUi.setLook(itemComposite); + itemComposite.setLayout(new FormLayout()); + + // Symbol badge + Label symbolLabel = new Label(itemComposite, SWT.CENTER | SWT.BORDER); + symbolLabel.setText(symbol[0]); + symbolLabel.setBackground(keyBackgroundColor); + symbolLabel.setForeground(itemComposite.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); + if (monoFont != null) { + symbolLabel.setFont(monoFont); + } + + FormData fdSymbol = new FormData(); + fdSymbol.left = new FormAttachment(0, 0); + fdSymbol.top = new FormAttachment(0, 0); + fdSymbol.width = 24; + fdSymbol.height = 24; + symbolLabel.setLayoutData(fdSymbol); + + // Description + Label descLabel = new Label(itemComposite, SWT.LEFT); + PropsUi.setLook(descLabel); + descLabel.setText(symbol[1]); + descLabel.setForeground(itemComposite.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); + + FormData fdDesc = new FormData(); + fdDesc.left = new FormAttachment(symbolLabel, margin / 2); + fdDesc.top = new FormAttachment(symbolLabel, 0, SWT.CENTER); + fdDesc.right = new FormAttachment(100, 0); + descLabel.setLayoutData(fdDesc); + + // Layout the item composite to compute its size + itemComposite.layout(true, true); + + // Position the item composite + FormData fdItem = new FormData(); + int column = currentItem % itemsPerRow; + int row = currentItem / itemsPerRow; + + if (column == 0) { + fdItem.left = new FormAttachment(0, 0); + } else { + fdItem.left = new FormAttachment((column * 100) / itemsPerRow, 0); + } + + if (row == 0) { + fdItem.top = new FormAttachment(0, 0); + } else { + // Find the item above this one + fdItem.top = new FormAttachment(lastRef, margin / 2); + } + + itemComposite.setLayoutData(fdItem); + + if (column == itemsPerRow - 1 || currentItem == symbols.length - 1) { + lastRef = itemComposite; + } + + currentItem++; + } + + // Layout the reference composite and update its FormData with computed height + refComposite.layout(true, true); + fdRefComposite.height = refComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; + refComposite.setLayoutData(fdRefComposite); + + return refComposite; + } +} diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigPluginOptionsTab.java b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigPluginOptionsTab.java index d1124b05307..90726fbfd98 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigPluginOptionsTab.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/configuration/tabs/ConfigPluginOptionsTab.java @@ -19,6 +19,8 @@ package org.apache.hop.ui.hopgui.perspective.configuration.tabs; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import org.apache.hop.core.Const; import org.apache.hop.core.config.plugin.ConfigPluginType; import org.apache.hop.core.gui.plugin.GuiPlugin; @@ -43,7 +45,6 @@ import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Shell; @GuiPlugin @@ -51,7 +52,7 @@ public class ConfigPluginOptionsTab { public static final String GUI_WIDGETS_PARENT_ID = "EnterOptionsDialog-GuiWidgetsParent"; - private List wPluginsList; + private static Map pluginDataMap = new HashMap<>(); private Composite wPluginComposite; public ConfigPluginOptionsTab() { @@ -65,7 +66,6 @@ public ConfigPluginOptionsTab() { description = "Plugins options tab") public void addPluginOptionsTab(CTabFolder wTabFolder) { Shell shell = wTabFolder.getShell(); - int margin = PropsUi.getMargin(); CTabItem wPluginsTab = new CTabItem(wTabFolder, SWT.NONE); wPluginsTab.setFont(GuiResource.getInstance().getFontDefault()); @@ -80,31 +80,21 @@ public void addPluginOptionsTab(CTabFolder wTabFolder) { lookLayout.marginHeight = PropsUi.getFormMargin(); wPluginsTabComp.setLayout(lookLayout); - // Plugins list on the left + // A composite to show settings for a plugin - takes full width // - wPluginsList = new List(wPluginsTabComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - PropsUi.setLook(wPluginsList); - FormData fdPluginsList = new FormData(); - fdPluginsList.left = new FormAttachment(0, 0); - fdPluginsList.right = new FormAttachment(20, 0); - fdPluginsList.top = new FormAttachment(0, 0); - fdPluginsList.bottom = new FormAttachment(100, 0); - wPluginsList.setLayoutData(fdPluginsList); - - // A composite to show settings for a plugin on the right. - // - wPluginComposite = new Composite(wPluginsTabComp, SWT.NO_BACKGROUND | SWT.BORDER); + wPluginComposite = new Composite(wPluginsTabComp, SWT.NO_BACKGROUND); PropsUi.setLook(wPluginComposite); FormData fdPluginComposite = new FormData(); - fdPluginComposite.left = new FormAttachment(wPluginsList, margin); + fdPluginComposite.left = new FormAttachment(0, 0); fdPluginComposite.right = new FormAttachment(100, 0); fdPluginComposite.top = new FormAttachment(0, 0); fdPluginComposite.bottom = new FormAttachment(100, 0); wPluginComposite.setLayoutData(fdPluginComposite); wPluginComposite.setLayout(new FormLayout()); - // Add the list of configuration plugins + // Load all configuration plugins and store their data // + pluginDataMap.clear(); PluginRegistry pluginRegistry = PluginRegistry.getInstance(); java.util.List configPlugins = pluginRegistry.getPlugins(ConfigPluginType.class); for (IPlugin configPlugin : configPlugins) { @@ -118,15 +108,13 @@ public void addPluginOptionsTab(CTabFolder wTabFolder) { Object sourceData = method.invoke(null, (Object[]) null); // This config plugin is also a GUI plugin - // Add a tab + // Store the plugin data in the map // String name = Const.NVL( TranslateUtil.translate(annotation.description(), emptySourceData.getClass()), ""); - wPluginsList.add(name); - wPluginsList.setData(name, sourceData); - wPluginsList.addListener(SWT.Selection, e -> showConfigPluginSettings()); + pluginDataMap.put(name, sourceData); } } catch (Exception e) { new ErrorDialog( @@ -138,7 +126,8 @@ public void addPluginOptionsTab(CTabFolder wTabFolder) { } } - wPluginsList.select(new int[] {}); + // Show a helpful message when no plugin is selected + showPluginInstructions(wPluginComposite); FormData fdPluginsTabComp = new FormData(); fdPluginsTabComp.left = new FormAttachment(0, 0); @@ -150,23 +139,24 @@ public void addPluginOptionsTab(CTabFolder wTabFolder) { wPluginsTab.setControl(wPluginsTabComp); } - /** Someone selected the settings of a plugin to edit */ - private void showConfigPluginSettings() { - int index = wPluginsList.getSelectionIndex(); - if (index < 0) { + /** + * Show settings for a specific plugin. This is called from the tree selection in + * ConfigurationPerspective. + */ + public static void showConfigPluginSettings(String pluginName, Composite targetComposite) { + Object pluginSourceData = pluginDataMap.get(pluginName); + if (pluginSourceData == null) { return; } - String name = wPluginsList.getSelection()[0]; - Object pluginSourceData = wPluginsList.getData(name); - // Delete everything - for (Control child : wPluginComposite.getChildren()) { + // Delete everything in the target composite + for (Control child : targetComposite.getChildren()) { child.dispose(); } // Rebuild ScrolledComposite sPluginsComp = - new ScrolledComposite(wPluginComposite, SWT.V_SCROLL | SWT.H_SCROLL); + new ScrolledComposite(targetComposite, SWT.V_SCROLL | SWT.H_SCROLL); sPluginsComp.setLayout(new FormLayout()); FormData fdsPluginsComp = new FormData(); fdsPluginsComp.left = new FormAttachment(0, 0); @@ -179,7 +169,7 @@ private void showConfigPluginSettings() { PropsUi.setLook(wPluginsComp); wPluginsComp.setLayout(new FormLayout()); - wPluginComposite.layout(); + targetComposite.layout(); GuiCompositeWidgets compositeWidgets = new GuiCompositeWidgets(HopGui.getInstance().getVariables()); @@ -203,6 +193,52 @@ private void showConfigPluginSettings() { sPluginsComp.setMinWidth(bounds.width); sPluginsComp.setMinHeight(bounds.height); - wPluginComposite.layout(true, true); + targetComposite.layout(true, true); + } + + /** Get all available plugin names for tree population */ + public static java.util.Set getPluginNames() { + return pluginDataMap.keySet(); + } + + /** Show instruction message when no plugin is selected */ + public static void showPluginInstructions(Composite targetComposite) { + // Clear the composite + for (Control child : targetComposite.getChildren()) { + child.dispose(); + } + + // Create a centered label with instructions + Composite instructionsComp = new Composite(targetComposite, SWT.NONE); + PropsUi.setLook(instructionsComp); + instructionsComp.setLayout(new FormLayout()); + + FormData fdInstructions = new FormData(); + fdInstructions.left = new FormAttachment(0, 0); + fdInstructions.right = new FormAttachment(100, 0); + fdInstructions.top = new FormAttachment(0, 0); + fdInstructions.bottom = new FormAttachment(100, 0); + instructionsComp.setLayoutData(fdInstructions); + + org.eclipse.swt.widgets.Label instructionLabel = + new org.eclipse.swt.widgets.Label(instructionsComp, SWT.CENTER | SWT.WRAP); + instructionLabel.setText( + "Select a plugin from the tree on the left to configure its settings."); + PropsUi.setLook(instructionLabel); + instructionLabel.setForeground( + instructionsComp.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); + + FormData fdLabel = new FormData(); + fdLabel.left = new FormAttachment(20, 0); + fdLabel.right = new FormAttachment(80, 0); + fdLabel.top = new FormAttachment(40, 0); + instructionLabel.setLayoutData(fdLabel); + + targetComposite.layout(true, true); + } + + /** Get the plugin composite for a tab item */ + public Composite getPluginComposite() { + return wPluginComposite; } } diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_de_DE.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_de_DE.properties index 824a7cc47c7..f8eb7da7d61 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_de_DE.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_de_DE.properties @@ -79,7 +79,7 @@ EnterMappingDialog.ResultMappings.Label=Zuordnungen: EnterMappingDialog.SourceFields.Label=Quellfelder: EnterMappingDialog.TargetFields.Label=Zielfelder: EnterMappingDialog.Title=Zuordnung eingeben -EnterOptionsDialog.AskOnExit.Label=Bei Programmende nachfragen? +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=Ge\u00E4nderte Dateien automatisch speichern? EnterOptionsDialog.AutoSplitHops.Label=Hops automatisch trennen EnterOptionsDialog.AutoSplitHops.Tooltip=Wird ein Transform auf einen Hop gezogen, kann dieser automatisch getrennt und die neue Transform zwischen die bestehenden eingef\u00FCgt werden\nBei Aktivierung dieser Option passiert das automatisch, andernfalls wird ein Best\u00E4tigungsdialog ge\u00F6ffnet. @@ -96,7 +96,7 @@ EnterOptionsDialog.ConfigFilename.Label=Name der Hop Konfigurationsdatei EnterOptionsDialog.CopyOrDistributeDialog.Label="Kopieren oder Verteilen"-Dialog anzeigen? EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=Wenn diese Option aktiviert ist und ein Schritt gerade mehr als einen anderen Schritt mit Daten beliefern soll,\nwird in einem Dialog abgefragt, ob die Daten auf die Schritte verteilt werden sollen, oder jeder\nSchritt alle Daten bekommt (Kopieren). Ist diese Option nicht aktiviert werden die Daten als Voreinstellung kopiert. EnterOptionsDialog.DarkMode.Label=Dunkler Modus ? -EnterOptionsDialog.DefaultFont.Label=Standardschriftart +EnterOptionsDialog.DefaultFont.Label=Standardschriftart: EnterOptionsDialog.DefaultLocale.Label=Bevorzugte Sprache EnterOptionsDialog.DefaultPreviewSize.Label=Standard-Zeilenanzahl im Vorschau-Dialog EnterOptionsDialog.DialogMiddlePercentage.Label=mittlere Prozentzahl f\u00FCr Dialoge @@ -104,9 +104,9 @@ EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label=Rahmen um Namen auf EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=Palette automatisch einklappen EnterOptionsDialog.FixedWidthFont.Label=Schriftart mit fixierter Breite: EnterOptionsDialog.General.Label=Allgemein -EnterOptionsDialog.GlobalZoom.Label=Verg\u00F6\u00DFerungsstufe der UI +EnterOptionsDialog.GlobalZoom.Label=Verg\u00F6\u00DFerungsstufe der UI: EnterOptionsDialog.GraphFont.Label=Schriftart des Arbeitsbereiches: -EnterOptionsDialog.GridSize.Label=Rasterabstand der Arbeitsoerfl\u00E4che +EnterOptionsDialog.GridSize.Label=Rasterabstand der Arbeitsoerfl\u00E4che: EnterOptionsDialog.GridSize.ToolTip=Icons werden am Raster eingeh\u00E4ngt, damit Transforms besser ausgerichtet werden k\u00F6nnen. EnterOptionsDialog.HelpToolTipsEnabled.Label=Zeige Tooltips EnterOptionsDialog.HideMenuBar.Label=Men\u00FCzeile verstecken @@ -128,11 +128,11 @@ EnterOptionsDialog.ShowTableViewToolbar.Label=Toolbar \u00FCber Tabellen anzeige EnterOptionsDialog.ShowTableViewToolbar.ToolTip=Diese Option aktiviert eine Toolbar \u00FCber allen Tabellen in der Hop GUI. EnterOptionsDialog.ShowViewport.Label=verstecke Viewport EnterOptionsDialog.ShowViewport.ToolTip=wenn aktiviert wird der Viewport in der rechten unteren Ecke nicht angezeigt -EnterOptionsDialog.SortFieldByName.Label=sortiere Felder nach Namen -EnterOptionsDialog.SortFieldByName.ToolTip=gibt Reihenfolge an, in der Felder gew\u00E4hlt werden ( Reihenfolge oder Name) +EnterOptionsDialog.SortFieldByName.Label= +EnterOptionsDialog.SortFieldByName.ToolTip= EnterOptionsDialog.TableOutput.SortMappings.Label=Tabellenausgabe: sortiere Mappings EnterOptionsDialog.Title=Hop Optionen -EnterOptionsDialog.ToolTipsEnabled.Label=Tooltips anzeigen +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.Transform.Label=Schritte EnterOptionsDialog.UseDatabaseCache.Label=Datenbank-Cache benutzen EnterOptionsDialog.UseDoubleClickOnCanvas.Label=Doppelklick auf Arbeitsoberfl\u00E4che verwenden? diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties index 8882e90c4b8..3ad0ae7c7c4 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_en_US.properties @@ -77,7 +77,20 @@ EnterMappingDialog.ResultMappings.Label=Mappings EnterMappingDialog.SourceFields.Label=Source fields EnterMappingDialog.TargetFields.Label=Target fields EnterMappingDialog.Title=Enter Mapping -EnterOptionsDialog.AskOnExit.Label=Ask user when exiting +EnterOptionsDialog.AskOnExit.Label=Show confirmation when exiting Hop +EnterOptionsDialog.AskOnExit.ConfirmMessage=Do you want to exit Hop GUI? +EnterOptionsDialog.AskOnExit.DoNotAskAgain=Do not ask this again +EnterOptionsDialog.Section.ConfirmationDialogs=Confirmation dialogs +EnterOptionsDialog.Section.Tooltips=Tooltips +EnterOptionsDialog.Section.Fonts=Fonts +EnterOptionsDialog.Section.Appearance=Appearance +EnterOptionsDialog.Section.GeneralAppearance=General appearance +EnterOptionsDialog.Section.PipelineWorkflowCanvas=Pipeline & Workflow canvas +EnterOptionsDialog.Section.TablesGrids=Tables & grids +EnterOptionsDialog.SplitHopsConfirm.Label=Show confirmation when splitting hops +EnterOptionsDialog.SplitHopsConfirm.Tooltip=If a transform is drawn on a hop, the hop can be split so that the new transform lays between the two original ones.\nIf this option is enabled, a confirmation dialog will be shown before splitting. +EnterOptionsDialog.SaveConfirm.Label=Show confirmation to save file when starting pipeline or workflow +EnterOptionsDialog.SaveConfirm.Tooltip=If this option is enabled, a confirmation dialog will be shown to save unsaved files before starting a pipeline or workflow. EnterOptionsDialog.AutoSave.Label=Autosave changed files EnterOptionsDialog.AutoSplitHops.Label=Automatically split hops EnterOptionsDialog.AutoSplitHops.Tooltip=If a transform is drawn on a hop the hop can be split\nso that the new transform lays between the two original ones.\nIf you activate this option this will happen automatically, otherwise a confirmation dialog is shown. @@ -85,58 +98,72 @@ EnterOptionsDialog.Button.Edit=Change EnterOptionsDialog.Button.Edit.Tooltip=Edits the option EnterOptionsDialog.Button.Reset=Reset EnterOptionsDialog.Button.Reset.Tooltip=Resets this option to the default value -EnterOptionsDialog.ClearCustomParameters.Confirmation=The custom flags and parameters have been deleted. -EnterOptionsDialog.ClearCustomParameters.Label=Clear custom parameters (transforms/plugins) -EnterOptionsDialog.ClearCustomParameters.Question=Do you really want to delete all custom flags and parameters in the dialogs of the transforms and plugins? -EnterOptionsDialog.ClearCustomParameters.Title=Question -EnterOptionsDialog.ClearCustomParameters.Tooltip=Deletes all custom flags and parameters in the dialogs of the transforms and plugins -EnterOptionsDialog.ConfigFilename.Label=Hop configuration filename -EnterOptionsDialog.CopyOrDistributeDialog.Label=Show Copy or Distribute dialog +EnterOptionsDialog.ResetConfirmations.Label=Reset all to enabled (Includes Action/Transfrom warnings) +EnterOptionsDialog.ResetConfirmations.Tooltip=Enable all confirmation dialogs and re-enable all transform warnings +EnterOptionsDialog.ResetConfirmations.Success=All confirmation dialogs have been enabled. +EnterOptionsDialog.ResetTooltips.Label=Reset all to enabled +EnterOptionsDialog.ResetTooltips.Tooltip=Enable all tooltip options +EnterOptionsDialog.ConfigFilename.Label=Hop configuration filename: +EnterOptionsDialog.CopyOrDistributeDialog.Label=Show copy or distribute dialog when drawing a hop EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=If this option is activated and a transform is about to deliver data to more than one transform, a dialog asks the user whether the data\nget distributed to each receiving transform or if each receiving transform gets the same data (copying).\nIf this option is not activated the default is to copy the data. EnterOptionsDialog.DarkMode.Label=Dark mode -EnterOptionsDialog.DefaultFont.Label=Default font -EnterOptionsDialog.DefaultLocale.Label=Preferred Language -EnterOptionsDialog.DefaultPreviewSize.Label=Preview data batch size -EnterOptionsDialog.DialogMiddlePercentage.Label=Dialog middle percentage -EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label=Draw border around names on canvas +EnterOptionsDialog.DefaultFont.Label=Default font: +EnterOptionsDialog.DefaultLocale.Label=Preferred language: +EnterOptionsDialog.DefaultPreviewSize.Label=Preview data batch size: +EnterOptionsDialog.EnterNumber.Hint=Enter a number +EnterOptionsDialog.DialogMiddlePercentage.Label=Dialog middle percentage: +EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label=Draw border around Action/Transform names on canvas EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=Auto collapse palette tree -EnterOptionsDialog.FixedWidthFont.Label=Fixed width font +EnterOptionsDialog.FixedWidthFont.Label=Fixed width font: EnterOptionsDialog.General.Label=General -EnterOptionsDialog.GlobalZoom.Label=UI zoom level -EnterOptionsDialog.GraphFont.Label=Font on workspace -EnterOptionsDialog.GridSize.Label=Canvas Grid Size +EnterOptionsDialog.GlobalZoom.Label=UI zoom level: +EnterOptionsDialog.GraphFont.Label=Font on workspace: +EnterOptionsDialog.GridSize.Label=Canvas grid size: EnterOptionsDialog.GridSize.ToolTip=Make the icons snap to a grid allowing you to align the transforms more easily. EnterOptionsDialog.HelpToolTipsEnabled.Label=Show help tooltips EnterOptionsDialog.HideMenuBar.Label=Hide the menu bar EnterOptionsDialog.HideMenuBar.ToolTip=Enabling this option hides the menu bar from the user interface. -EnterOptionsDialog.IconSize.Label=Icon size in workspace -EnterOptionsDialog.LineWidth.Label=Line width on workspace -EnterOptionsDialog.LookAndFeel.Label=Look && Feel -EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.Label=Maximum execution logging text size +EnterOptionsDialog.IconSize.Label=Icon size in workspace: +EnterOptionsDialog.LineWidth.Label=Line width on workspace: +EnterOptionsDialog.LookAndFeel.Label=Look & Feel +EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.Label=Maximum execution logging text size: EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.ToolTip=The maximum size of the log text shown in the execution information perspective to prevent out of memory errors. -EnterOptionsDialog.NoteFont.Label=Font for notes -EnterOptionsDialog.OpenLastFileStartup.Label=Open last file at startup +EnterOptionsDialog.NoteFont.Label=Font for notes: +EnterOptionsDialog.OpenLastFileStartup.Label=Reopen tabs on startup +EnterOptionsDialog.ReloadFileOnChange.Label=Reload file if changed on filesystem +EnterOptionsDialog.ReloadFileOnChange.ToolTip=Automatically reload files when they are changed outside of Apache Hop EnterOptionsDialog.RestartWarning.DialogMessage=Please restart the Hop GUI to apply look and feel changes EnterOptionsDialog.RestartWarning.DialogTitle=Restart EnterOptionsDialog.RestartWarning.Option1=Close EnterOptionsDialog.RestartWarning.Option2=Please don't show this message again -EnterOptionsDialog.ShowCanvasGrid.Label=Show Canvas Grid +EnterOptionsDialog.ShowCanvasGrid.Label=Show canvas grid EnterOptionsDialog.ShowCanvasGrid.ToolTip=If enabled, the canvas grid will be visible -EnterOptionsDialog.ShowTableViewToolbar.Label=Show a toolbar above tables +EnterOptionsDialog.ShowTableViewToolbar.Label=Show toolbar EnterOptionsDialog.ShowTableViewToolbar.ToolTip=Enabling this option makes a toolbar appear above all tables in the Hop GUI. -EnterOptionsDialog.ShowViewport.Label=Hide Viewport -EnterOptionsDialog.ShowViewport.ToolTip=If enabled, the viewport in the bottom right will be hidden -EnterOptionsDialog.SortFieldByName.Label=Sort field by name -EnterOptionsDialog.SortFieldByName.ToolTip=Determines the order when you select a field (incoming or sorted by name) +EnterOptionsDialog.ShowViewport.Label=Show viewport +EnterOptionsDialog.ShowViewport.ToolTip=If enabled, the viewport in the bottom right will be shown +EnterOptionsDialog.SortFieldByName.Label=Sort field names alphabetically in dropdowns +EnterOptionsDialog.SortFieldByName.ToolTip=When enabled, field names in dropdown lists are sorted alphabetically. When disabled, fields appear in their incoming order. EnterOptionsDialog.TableOutput.SortMappings.Label=Table Output: sort mappings EnterOptionsDialog.Title=Hop Options -EnterOptionsDialog.ToolTipsEnabled.Label=Display tooltips -EnterOptionsDialog.ResolveVarsInTips.Label=Resolve variables in tooltips +EnterOptionsDialog.ToolTipsEnabled.Label=Show tooltip when hovering over a hop or Action/Transform +EnterOptionsDialog.ResolveVarsInTips.Label=Show resolved variables in tooltips EnterOptionsDialog.Transform.Label=Transforms EnterOptionsDialog.UseDatabaseCache.Label=Use database cache EnterOptionsDialog.UseDoubleClickOnCanvas.Label=Use double click on canvas EnterOptionsDialog.UseGlobalFileBookmarks.Label=Use global bookmarks in the file dialog EnterOptionsDialog.WhatIsHopConfigSize.Label=This location can be changed with environment variable HOP_CONFIG_FOLDER +ConfigKeyboardShortcutsTab.SymbolReference.Title=Keyboard Symbol Reference: +ConfigKeyboardShortcutsTab.Key.Control=Control +ConfigKeyboardShortcutsTab.Key.OptionAlt=Option/Alt +ConfigKeyboardShortcutsTab.Key.Command=Command +ConfigKeyboardShortcutsTab.Key.Shift=Shift +ConfigKeyboardShortcutsTab.Key.Escape=Escape +ConfigKeyboardShortcutsTab.Key.Delete=Delete +ConfigKeyboardShortcutsTab.Key.UpArrow=Up Arrow +ConfigKeyboardShortcutsTab.Key.DownArrow=Down Arrow +ConfigKeyboardShortcutsTab.Key.LeftArrow=Left Arrow +ConfigKeyboardShortcutsTab.Key.RightArrow=Right Arrow EnterOrderedListDialog.AddAll.Tooltip=Add all items on the left EnterOrderedListDialog.AddOne.Tooltip=Add the selected items on the left EnterOrderedListDialog.AvailableItems.Label=Available items: diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_AR.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_AR.properties index 41058360695..f2607904668 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_AR.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_AR.properties @@ -54,7 +54,7 @@ EnterMappingDialog.ResultMappings.Label=Mapeos\: EnterMappingDialog.SourceFields.Label=Campos de origen\: EnterMappingDialog.TargetFields.Label=Campos destino\: EnterMappingDialog.Title=Ingrese Mapeo -EnterOptionsDialog.AskOnExit.Label=\u00BFPreguntar al usuario al salir? +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=\u00BFAuto guardar archivos cambiados? EnterOptionsDialog.AutoSplitHops.Label=\u00BFDividir saltos autom\u00E1ticamente? EnterOptionsDialog.AutoSplitHops.Tooltip=Si un paso es arrastrado hacia un salto, el salto puede ser dividido\n de manera que el nuevo paso quede ubicado entre los dos originales.\nSi se activa esta opci\u00F3n lo anterior suceder\u00E1 autom\u00E1ticamente, de otra manera se muestra un di\u00E1logo de confirmaci\u00F3n. @@ -81,7 +81,7 @@ EnterOptionsDialog.LookAndFeel.Label=Apariencia EnterOptionsDialog.NoteFont.Label=Fuente para notas\: EnterOptionsDialog.OpenLastFileStartup.Label=\u00BFAbrir el \u00FAltimo archivo al iniciar? EnterOptionsDialog.Title=Opciones de Hop -EnterOptionsDialog.ToolTipsEnabled.Label=\u00BFMostrar cuadros de ayuda? +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.UseDatabaseCache.Label=\u00BFUtilizar cache de base de datos? EnterPrintDialog.BottomMargin.Label=Margen inferior (pulgadas)\: EnterPrintDialog.Cols.Label=Columnas\: diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_ES.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_ES.properties index 51fcf168505..d6872ed0352 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_ES.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_es_ES.properties @@ -79,7 +79,7 @@ EnterMappingDialog.ResultMappings.Label=Mapeos EnterMappingDialog.SourceFields.Label=Campos de origen EnterMappingDialog.TargetFields.Label=Campos de destino EnterMappingDialog.Title=Introducir mapeo -EnterOptionsDialog.AskOnExit.Label=Preguntar al usuario al salir +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=Guardado autom\u00E1tico de archivos modificados EnterOptionsDialog.AutoSplitHops.Label=Dividir autom\u00E1ticamente los saltos EnterOptionsDialog.AutoSplitHops.Tooltip=Si se dibuja una transformaci\u00F3n en un salto, el salto puede dividirse\npara que la nueva transformaci\u00F3n se sit\u00FAe entre las dos originales.\nSi activa esta opci\u00F3n, esto ocurrir\u00E1 autom\u00E1ticamente; de lo contrario, se mostrar\u00E1 un cuadro de di\u00E1logo de confirmaci\u00F3n. @@ -96,16 +96,16 @@ EnterOptionsDialog.ConfigFilename.Label=Nombre del archivo de configuraci\u00F3n EnterOptionsDialog.CopyOrDistributeDialog.Label=Mostrar Copia o Distribuir di\u00E1logo EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=Si esta opci\u00F3n est\u00E1 activada y una transformaci\u00F3n est\u00E1 a punto de entregar datos a m\u00E1s de una transformaci\u00F3n, un cuadro de di\u00E1logo pregunta al usuario si los datos\nse distribuyen a cada transformaci\u00F3n receptora o si cada transformaci\u00F3n receptora recibe los mismos datos (copia).\nSi esta opci\u00F3n no est\u00E1 activada, por defecto se copian los datos. EnterOptionsDialog.DarkMode.Label=\u00BFModo oscuro? -EnterOptionsDialog.DefaultFont.Label=Fuente por defecto -EnterOptionsDialog.DefaultLocale.Label=Idioma preferido +EnterOptionsDialog.DefaultFont.Label=Fuente por defecto: +EnterOptionsDialog.DefaultLocale.Label=Idioma preferido: EnterOptionsDialog.DefaultPreviewSize.Label=Vista previa del tama\u00F1o del lote de datos EnterOptionsDialog.DialogMiddlePercentage.Label=Porcentaje medio del di\u00E1logo EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=Colapso autom\u00E1tico del \u00E1rbol de paletas EnterOptionsDialog.FixedWidthFont.Label=Fuente de anchura fija EnterOptionsDialog.General.Label=General -EnterOptionsDialog.GlobalZoom.Label=Nivel de zoom de la IU +EnterOptionsDialog.GlobalZoom.Label=Nivel de zoom de la IU: EnterOptionsDialog.GraphFont.Label=Fuente en el espacio de trabajo -EnterOptionsDialog.GridSize.Label=Tama\u00F1o de la cuadr\u00EDcula del lienzo +EnterOptionsDialog.GridSize.Label=Tama\u00F1o de la cuadr\u00EDcula del lienzo: EnterOptionsDialog.GridSize.ToolTip=Haga que los iconos se ajusten a una cuadr\u00EDcula que le permita alinear las transformaciones m\u00E1s f\u00E1cilmente. EnterOptionsDialog.HelpToolTipsEnabled.Label=Mostrar informaci\u00F3n de ayuda EnterOptionsDialog.HideMenuBar.Label=Ocultar la barra de men\u00FA @@ -119,7 +119,7 @@ EnterOptionsDialog.ShowCanvasGrid.Label=Mostrar cuadr\u00EDcula de lienzo EnterOptionsDialog.ShowCanvasGrid.ToolTip=Si se activa, la cuadr\u00EDcula del lienzo ser\u00E1 visible EnterOptionsDialog.ShowTableViewToolbar.Label=\u00BFMostrar una barra de herramientas encima de las tablas? EnterOptionsDialog.ShowTableViewToolbar.ToolTip=Activar esta opci\u00F3n hace que aparezca una barra de herramientas encima de todas las tablas en la interfaz gr\u00E1fica de Hop. -EnterOptionsDialog.ToolTipsEnabled.Label=Mostrar informaci\u00F3n sobre las herramientas +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.UseDatabaseCache.Label=Utilizar la cach\u00E9 de la base de datos EnterOptionsDialog.UseDoubleClickOnCanvas.Label=\u00BFHacer doble clic en el lienzo? EnterOptionsDialog.UseGlobalFileBookmarks.Label=\u00BFUtilizar marcadores globales en el di\u00E1logo de archivo? diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_fr_FR.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_fr_FR.properties index f395fdd7d14..66415f0c974 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_fr_FR.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_fr_FR.properties @@ -64,7 +64,7 @@ EnterMappingDialog.ResultMappings.Label=Sch\u00E9ma de correspondance EnterMappingDialog.SourceFields.Label=Champs sources EnterMappingDialog.TargetFields.Label=Champs cibles EnterMappingDialog.Title=Sch\u00E9ma de correspondance -EnterOptionsDialog.AskOnExit.Label=Demander confirmation sur sortie +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=Enregistrer automatiquement les traitements modifi\u00E9s EnterOptionsDialog.AutoSplitHops.Label=Ins\u00E9rer auto. transformation EnterOptionsDialog.AutoSplitHops.Tooltip=Si cette option est s\u00E9lectionn\u00E9e, l''insertion d''une transformation entre deux transformations se fera automatiquement. Sinon la confimation de l''utilisateur sera exig\u00E9e. @@ -81,14 +81,14 @@ EnterOptionsDialog.ConfigFilename.Label=Nom du fichier de configuration de Hop EnterOptionsDialog.CopyOrDistributeDialog.Label=Afficher choix "copier ou distribuer" EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=Si cette option est s\u00E9lectionn\u00E9e et une transformation est sur le point de transmettre des donn\u00E9es \u00E0 plus d''une \u00E9tape, l''utilisateur sera invit\u00E9 \u00E0 choisir entre la distribution et la copie. EnterOptionsDialog.DarkMode.Label=Mode sombre -EnterOptionsDialog.DefaultFont.Label=Police par d\u00E9faut +EnterOptionsDialog.DefaultFont.Label=Police par d\u00E9faut: EnterOptionsDialog.DefaultLocale.Label=Langue EnterOptionsDialog.DefaultPreviewSize.Label=Nbr lignes max \u00E0 pr\u00E9visualiser par d\u00E9faut EnterOptionsDialog.DialogMiddlePercentage.Label=% Bo\u00EEte de dialogue EnterOptionsDialog.FixedWidthFont.Label=Police \u00E0 largeur fixe EnterOptionsDialog.General.Label=G\u00E9n\u00E9ral -EnterOptionsDialog.GlobalZoom.Label=Niveau de zoom -EnterOptionsDialog.GraphFont.Label=Police espace travail +EnterOptionsDialog.GlobalZoom.Label=Niveau de zoom: +EnterOptionsDialog.GraphFont.Label=Police espace travail: EnterOptionsDialog.GridSize.Label=Taille de la grille EnterOptionsDialog.HideMenuBar.Label=Masquer la barre de menu EnterOptionsDialog.HideMenuBar.ToolTip=L''activation de cette option permet de masquer la barre de menu de l''interface utilisateur. @@ -101,7 +101,7 @@ EnterOptionsDialog.RestartWarning.Option1=Fermer EnterOptionsDialog.ShowCanvasGrid.Label=Afficher la grille EnterOptionsDialog.ShowTableViewToolbar.Label=Afficher la barre d''outils au-dessus des tableaux EnterOptionsDialog.Title=Options -EnterOptionsDialog.ToolTipsEnabled.Label=Afficher les info-bulles +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.UseDatabaseCache.Label=Utiliser un cache pour les bases de donn\u00E9es EnterPrintDialog.BottomMargin.Label=marge en Bas (inch)\: EnterPrintDialog.Cols.Label=Colonnes @@ -196,5 +196,5 @@ TransformFieldsDialog.TableCol.TrimType=Enlever espaces TransformFieldsDialog.TableCol.Type=Type TransformFieldsDialog.Title=Champs transformation EnterOptionsDialog.ShowViewport.Label=Masquer le guide visuel montrant la zone rendue -EnterOptionsDialog.SortFieldByName.Label=Trier les champs par nom -EnterOptionsDialog.ResolveVarsInTips.Label=R\u00E9soudre les variables dans les info-bulles +EnterOptionsDialog.SortFieldByName.Label= +EnterOptionsDialog.ResolveVarsInTips.Label= diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_it_IT.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_it_IT.properties index 0284e69db5c..3c347e4ef00 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_it_IT.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_it_IT.properties @@ -79,7 +79,7 @@ EnterMappingDialog.ResultMappings.Label=Mappature\: EnterMappingDialog.SourceFields.Label=Campi di origine\: EnterMappingDialog.TargetFields.Label=Campi di destinazione\: EnterMappingDialog.Title=Inserire la mappatura -EnterOptionsDialog.AskOnExit.Label=Chiedere conferma per uscire? +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=Salvare automaticamente i file cambiati? EnterOptionsDialog.AutoSplitHops.Label=Separare automaticamente gli hop? EnterOptionsDialog.AutoSplitHops.Tooltip=Se un transform \u00E8 disegnato su un hop, l''hop pu\u00F2 essere separato\r\nin modo che il nuovo transform stia fra i due originali.\r\nSe viene attivata quest''opzione ci\u00F2 avverr\u00E0 automaticamente, altrimenti verr\u00E0 mostrata una finestra di conferma. @@ -96,16 +96,16 @@ EnterOptionsDialog.ConfigFilename.Label=Nome del file di configurazione di Hop EnterOptionsDialog.CopyOrDistributeDialog.Label=Devo mostrare la finestra Copia o Distribuisci? EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=Se quest''opzione \u00E8 attivata ed un transform sta per inviare dati a pi\u00F9 di un transform, una finestra di dialogo chieder\u00E0 \r\nall''utente se i dati debbano essere distribuiti a ciascun transform ricevente o se ciascun transform ricevente debba ricevere gli stessi dati (copia).\r\nSe quest''opzione non \u00E8 attivata il comportamento di default \u00E8 copiare i dati. EnterOptionsDialog.DarkMode.Label=Abilito la modalit\u00E0 notturna (Dark mode)? -EnterOptionsDialog.DefaultFont.Label=Font di default +EnterOptionsDialog.DefaultFont.Label=Font di default: EnterOptionsDialog.DefaultLocale.Label=Lingua di default: EnterOptionsDialog.DefaultPreviewSize.Label=Numero di linee di default nella finestra d''anteprima\: EnterOptionsDialog.DialogMiddlePercentage.Label=Finestra di percentuale intermedia\: EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=Collassare automaticamente l''albero della palette? EnterOptionsDialog.FixedWidthFont.Label=Font a larghezza fissa\: EnterOptionsDialog.General.Label=Generale -EnterOptionsDialog.GlobalZoom.Label=Livello di zoom nella GUI +EnterOptionsDialog.GlobalZoom.Label=Livello di zoom nella GUI: EnterOptionsDialog.GraphFont.Label=Font nel workspace\: -EnterOptionsDialog.GridSize.Label=Dimensione griglia del canvas +EnterOptionsDialog.GridSize.Label=Dimensione griglia del canvas: EnterOptionsDialog.GridSize.ToolTip=Fa in modo che le icone siano agganciate alla griglia permettendo un pi\u00F9 agevole allineamento dei transforms. EnterOptionsDialog.HelpToolTipsEnabled.Label=Mostrare i tooltip di aiuto? EnterOptionsDialog.HideMenuBar.Label=Nascondi il menu @@ -116,7 +116,7 @@ EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.Label=Massima lunghezza del l EnterOptionsDialog.MaxExecutionLoggingTextSizeSize.ToolTip=Imposta la lunghezza massima del testo mostrato nella execution information perspective in maniera da evitare errori di memoria esaurita (out of memory). EnterOptionsDialog.NoteFont.Label=Font per le note\: EnterOptionsDialog.OpenLastFileStartup.Label=Aprire l''ultimo file all''avvio di Hop? -EnterOptionsDialog.RestartWarning.DialogMessage=Per favore riavvia la Hop GUI per applicare le modifiche al look && feel. +EnterOptionsDialog.RestartWarning.DialogMessage=Per favore riavvia la Hop GUI per applicare le modifiche al look & feel. EnterOptionsDialog.RestartWarning.DialogTitle=Riavvia EnterOptionsDialog.RestartWarning.Option1=Chiudi EnterOptionsDialog.RestartWarning.Option2=Per favore non mostrare ancora il messaggio @@ -126,11 +126,11 @@ EnterOptionsDialog.ShowTableViewToolbar.Label=Mostra la toolbar sopra le tabelle EnterOptionsDialog.ShowTableViewToolbar.ToolTip=Abilitando questa opzione Hop visualizza una toolbar sopra tutte le tabelle nella Hop GUI. EnterOptionsDialog.ShowViewport.Label=Nascondi il viewport EnterOptionsDialog.ShowViewport.ToolTip=Se abilitato, nasconde il viewport posizionato nell''angolo in basso a destra -EnterOptionsDialog.SortFieldByName.Label=Ordina i campi per nome -EnterOptionsDialog.SortFieldByName.ToolTip=Negli elenchi di campi, determina l''ordine quando quando devi selezionare un campo (sequenza di inserimento oppure alfabetico) +EnterOptionsDialog.SortFieldByName.Label= +EnterOptionsDialog.SortFieldByName.ToolTip= EnterOptionsDialog.TableOutput.SortMappings.Label=Table Output: ordina mappature EnterOptionsDialog.Title=Opzioni di Hop -EnterOptionsDialog.ToolTipsEnabled.Label=Mostrare suggerimenti? +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.Transform.Label=Transforms EnterOptionsDialog.UseDatabaseCache.Label=Utilizzare la cache del database? EnterOptionsDialog.UseDoubleClickOnCanvas.Label=Utilizzo il double click sulla canvas? diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ja_JP.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ja_JP.properties index 54aa7ec7629..c030e338cbf 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ja_JP.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ja_JP.properties @@ -58,7 +58,7 @@ EnterMappingDialog.ResultMappings.Label=\u30de\u30c3\u30d4\u30f3\u30b0\: EnterMappingDialog.SourceFields.Label=\u30bd\u30fc\u30b9\u30d5\u30a3\u30fc\u30eb\u30c9\: EnterMappingDialog.TargetFields.Label=\u30bf\u30fc\u30b2\u30c3\u30c8\u30d5\u30a3\u30fc\u30eb\u30c9\: EnterMappingDialog.Title=\u30de\u30c3\u30d4\u30f3\u30b0\u5165\u529b -EnterOptionsDialog.AskOnExit.Label=\u7D42\u4E86\u6642\u306B\u78BA\u8A8D\u3059\u308B\: +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=\u5909\u66F4\u3057\u305F\u30D5\u30A1\u30A4\u30EB\u3092\u81EA\u52D5\u4FDD\u5B58\u3059\u308B\: EnterOptionsDialog.AutoSplitHops.Label=\u7D50\u5408\u3092\u81EA\u52D5\u7684\u306B\u5206\u5C90\u3059\u308B\: EnterOptionsDialog.AutoSplitHops.Tooltip=\u30b9\u30c6\u30c3\u30d7\u3092\u30db\u30c3\u30d7\u4e0a\u306b\u914d\u7f6e\u3059\u308b\u3068\u30db\u30c3\u30d7\u306f\u5206\u5272\u3055\u308c\u3001\n\u65b0\u305f\u306a\u30b9\u30c6\u30c3\u30d7\u306f\u5143\u306e\uff12\u3064\u306e\u30b9\u30c6\u30c3\u30d7\u306e\u9593\u306b\u914d\u7f6e\u3055\u308c\u307e\u3059\u3002\n\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u6709\u52b9\u306b\u3059\u308b\u3068\u3053\u306e\u51e6\u7406\u306f\u81ea\u52d5\u7684\u306b\u884c\u308f\u308c\u307e\u3059\u3002\u7121\u52b9\u306e\u5834\u5408\u306f\u78ba\u8a8d\u30dc\u30c3\u30af\u30b9\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 @@ -89,7 +89,7 @@ EnterOptionsDialog.LookAndFeel.Label=\u5916\u89B3 EnterOptionsDialog.NoteFont.Label=\u30E1\u30E2\u306E\u30D5\u30A9\u30F3\u30C8\: EnterOptionsDialog.OpenLastFileStartup.Label=\u8D77\u52D5\u6642\u306B\u6700\u5F8C\u306B\u958B\u3044\u305F\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F\: EnterOptionsDialog.Title=Hop \u30AA\u30D7\u30B7\u30E7\u30F3 -EnterOptionsDialog.ToolTipsEnabled.Label=\u30C4\u30FC\u30EB\u30C1\u30C3\u30D7\u3092\u8868\u793A\u3059\u308B\: +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.UseDatabaseCache.Label=\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30AD\u30E3\u30C3\u30B7\u30E5\u3092\u4F7F\u7528\u3059\u308B\: EnterPrintDialog.BottomMargin.Label=\u4e0b\u4f59\u767d (\u30a4\u30f3\u30c1)\: EnterPrintDialog.Cols.Label=\u30ab\u30e9\u30e0\: diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ko_KR.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ko_KR.properties index e3152bece0b..74e942b51b3 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ko_KR.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_ko_KR.properties @@ -54,7 +54,7 @@ EnterMappingDialog.ResultMappings.Label=\uB9E4\uD551: EnterMappingDialog.SourceFields.Label=\uC18C\uC2A4 \uD544\uB4DC: EnterMappingDialog.TargetFields.Label=\uB300\uC0C1 \uD544\uB4DC: EnterMappingDialog.Title=\uB9E4\uD551 \uC785\uB825 -EnterOptionsDialog.AskOnExit.Label=\uB098\uAC08 \uB54C \uBB3C\uC5B4\uBCF4\uAE30? +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=\uBCC0\uACBD\uB41C \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uC800\uC7A5? EnterOptionsDialog.AutoSplitHops.Label=\uC790\uB3D9\uC73C\uB85C Hop \uBD84\uB9AC? EnterOptionsDialog.Button.Edit=\uBCC0\uACBD @@ -73,8 +73,8 @@ EnterOptionsDialog.DialogMiddlePercentage.Label=\uCC3D \uC911\uC559 \uBE44\uC728 EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=\uC790\uB3D9\uC73C\uB85C \uC811\uD788\uB294 \uD314\uB81B\uD2B8 \uD2B8\uB9AC? EnterOptionsDialog.FixedWidthFont.Label=\uACE0\uC815\uD3ED \uD3F0\uD2B8: EnterOptionsDialog.General.Label=\uC77C\uBC18 -EnterOptionsDialog.GraphFont.Label=\uC791\uC5C5\uACF5\uAC04 \uAE00\uAF34 -EnterOptionsDialog.GridSize.Label=\uADF8\uB9AC\uB4DC \uD06C\uAE30 +EnterOptionsDialog.GraphFont.Label=\uC791\uC5C5\uACF5\uAC04 \uAE00\uAF34: +EnterOptionsDialog.GridSize.Label=\uADF8\uB9AC\uB4DC \uD06C\uAE30: EnterOptionsDialog.HelpToolTipsEnabled.Label=\uB3C4\uC6C0\uB9D0 \uD234\uD301 \uBCF4\uAE30? EnterOptionsDialog.IconSize.Label=\uC791\uC5C5\uACF5\uAC04 \uC544\uC774\uCF58 \uD06C\uAE30: EnterOptionsDialog.LineWidth.Label=\uC791\uC5C5\uACF5\uAC04 \uC904 \uB113\uC774: @@ -82,7 +82,7 @@ EnterOptionsDialog.LookAndFeel.Label=\uB8E9\uC564\uD544 EnterOptionsDialog.NoteFont.Label=\uB178\uD2B8\uC758 \uD3F0\uD2B8: EnterOptionsDialog.OpenLastFileStartup.Label=\uC2DC\uC791\uD560 \uB54C\uC5D0 \uB9C8\uC9C0\uB9C9 \uD30C\uC77C \uC5F4\uAE30? EnterOptionsDialog.Title=Hop \uC635\uC158 -EnterOptionsDialog.ToolTipsEnabled.Label=\uD234\uD301 \uBCF4\uC774\uAE30? +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.UseDatabaseCache.Label=\uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uCE90\uC2DC \uC0AC\uC6A9? EnterPrintDialog.BottomMargin.Label=\uD558\uB2E8 \uC5EC\uBC31 (\uC778\uCE58)\: EnterPrintDialog.Cols.Label=\uCEEC\uB7FC\: diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_pt_BR.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_pt_BR.properties index f56c9f4efe0..568d47ea423 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_pt_BR.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_pt_BR.properties @@ -56,7 +56,7 @@ EnterMappingDialog.ResultMappings.Label=Mapeamentos EnterMappingDialog.SourceFields.Label=Campos de origem EnterMappingDialog.TargetFields.Label=Campos de destino EnterMappingDialog.Title=Digita o mapeamento -EnterOptionsDialog.AskOnExit.Label=Pedir confirma\u00E7\u00E3o ao sair +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=Salvar arquivos automaticamente EnterOptionsDialog.AutoSplitHops.Label=Partir saltos automaticamente EnterOptionsDialog.Button.Edit=Mudar @@ -65,12 +65,12 @@ EnterOptionsDialog.Button.Reset=Recompor EnterOptionsDialog.ClearCustomParameters.Title=Quest\u00E3o EnterOptionsDialog.CopyOrDistributeDialog.Label=Exibir di\u00E1logo de c\u00F3pia ou distribui\u00E7\u00E3o EnterOptionsDialog.DarkMode.Label=Modo escuro ? -EnterOptionsDialog.DefaultFont.Label=Fonte padr\u00E3o +EnterOptionsDialog.DefaultFont.Label=Fonte padr\u00E3o: EnterOptionsDialog.DefaultLocale.Label=Idioma preferido EnterOptionsDialog.FixedWidthFont.Label=Fonte de largura fixa EnterOptionsDialog.General.Label=Geral -EnterOptionsDialog.GraphFont.Label=Fonte do espa\u00E7o de trabalho -EnterOptionsDialog.GridSize.Label=Tamanho da grade na tela +EnterOptionsDialog.GraphFont.Label=Fonte do espa\u00E7o de trabalho: +EnterOptionsDialog.GridSize.Label=Tamanho da grade na tela: EnterOptionsDialog.HelpToolTipsEnabled.Label=Exibir dicas de ferramentas EnterOptionsDialog.HideMenuBar.Label=Ocultar barra de menu EnterOptionsDialog.LookAndFeel.Label=Apar\u00EAncia && Sentimento @@ -82,9 +82,9 @@ EnterOptionsDialog.RestartWarning.Option2=N\u00E3o exibir esta mensagem novament EnterOptionsDialog.ShowCanvasGrid.Label=Exibir grade na tela EnterOptionsDialog.ShowTableViewToolbar.Label=Exibir barra de ferramentas sobre tabelas ? EnterOptionsDialog.ShowViewport.Label=Ocultar janela de visualiza\u00E7\u00E3o -EnterOptionsDialog.SortFieldByName.Label=Ordenar campo por nome +EnterOptionsDialog.SortFieldByName.Label= EnterOptionsDialog.Title=Op\u00E7\u00F5es do HOP -EnterOptionsDialog.ToolTipsEnabled.Label=Exibir dicas de ferramentas +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.Transform.Label=Transforma\u00E7\u00F5es EnterOptionsDialog.UseDatabaseCache.Label=Usar cache de bases de dados EnterOptionsDialog.UseDoubleClickOnCanvas.Label=Usar clique duplo na tela ? diff --git a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_zh_CN.properties b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_zh_CN.properties index f10334fa84a..d2d9b5b0ea0 100644 --- a/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_zh_CN.properties +++ b/ui/src/main/resources/org/apache/hop/ui/core/dialog/messages/messages_zh_CN.properties @@ -79,7 +79,7 @@ EnterMappingDialog.ResultMappings.Label=\u6620\u5C04 EnterMappingDialog.SourceFields.Label=\u6E90\u5217 EnterMappingDialog.TargetFields.Label=\u76EE\u7684\u5217 EnterMappingDialog.Title=\u5217\u6620\u5C04 -EnterOptionsDialog.AskOnExit.Label=\u9000\u51FA\u65F6\u8BE2\u95EE +EnterOptionsDialog.AskOnExit.Label= EnterOptionsDialog.AutoSave.Label=\u81EA\u52A8\u4FDD\u5B58\u53D8\u66F4 EnterOptionsDialog.AutoSplitHops.Label=\u81EA\u52A8\u5206\u5272\u8282\u70B9\u8FDE\u63A5 EnterOptionsDialog.AutoSplitHops.Tooltip=\u5982\u679C\u5728\u4E00\u6761\u8282\u70B9\u8FDE\u63A5\u4E0A\u653E\u7F6E\u4E86\u4E00\u4E2A\u8282\u70B9,\n\u662F\u5426\u8981\u81EA\u52A8\u5206\u5272\u8FD9\u4E2A\u8282\u70B9\u8FDE\u63A5\u6210\u4E3A\u4E24\u6761\u8FDE\u63A5,\n\u5206\u522B\u8FDE\u63A5\u539F\u8282\u70B9\u548C\u65B0\u8282\u70B9. @@ -96,7 +96,7 @@ EnterOptionsDialog.ConfigFilename.Label=Hop \u914D\u7F6E\u6587\u4EF6\u540D EnterOptionsDialog.CopyOrDistributeDialog.Label=\u663E\u793A "\u590D\u5236\u6216\u5206\u53D1" \u5BF9\u8BDD\u6846 EnterOptionsDialog.CopyOrDistributeDialog.Tooltip=\u5F53\u4E00\u4E2A Transform \u5411\u591A\u4E2A Transform \u53D1\u9001\u6570\u636E\u65F6,\u662F\u5426\u8981\u5F39\u51FA\u4E00\u4E2A\u5BF9\u8BDD\u6846\u6765\u8BE2\u95EE\u7528\u6237\u662F\u4F7F\u7528\u5206\u53D1\u65B9\u5F0F\u8FD8\u662F\u590D\u5236\u65B9\u5F0F EnterOptionsDialog.DarkMode.Label=\u6DF1\u8272\u6A21\u5F0F -EnterOptionsDialog.DefaultFont.Label=\u9ED8\u8BA4\u5B57\u4F53 +EnterOptionsDialog.DefaultFont.Label=\u9ED8\u8BA4\u5B57\u4F53: EnterOptionsDialog.DefaultLocale.Label=\u9ED8\u8BA4\u8BED\u8A00\: EnterOptionsDialog.DefaultPreviewSize.Label=\u9884\u89C8\u5BF9\u8BDD\u6846\u7684\u7F3A\u7701\u884C\u6570\: EnterOptionsDialog.DialogMiddlePercentage.Label=\u5BF9\u8BDD\u6846\u7684\u4E2D\u90E8\u4F4D\u7F6E\u767E\u5206\u6BD4\: @@ -104,7 +104,7 @@ EnterOptionsDialog.DrawBorderAroundCanvasNamesOnCanvas.Label=\u5728\u753B\u5E03\ EnterOptionsDialog.EnableAutoCollapseCoreObjectTree.Label=\u81EA\u52A8\u6298\u53E0\u5206\u7C7B\: EnterOptionsDialog.FixedWidthFont.Label=\u56FA\u5B9A\u5BBD\u5EA6\u5B57\u4F53\: EnterOptionsDialog.General.Label=\u4E00\u822C\u9879 -EnterOptionsDialog.GlobalZoom.Label=UI \u7F29\u653E\u7EA7\u522B +EnterOptionsDialog.GlobalZoom.Label=UI \u7F29\u653E\u7EA7\u522B: EnterOptionsDialog.GraphFont.Label=\u5DE5\u4F5C\u533A\u5185\u5B57\u4F53\: EnterOptionsDialog.GridSize.Label=\u683C\u7F51\u5927\u5C0F\: EnterOptionsDialog.GridSize.ToolTip=\u5C06\u56FE\u6807\u8D34\u8FD1\u7F51\u683C,\u53EF\u4EE5\u66F4\u5BB9\u6613\u5BF9\u9F50\u56FE\u6807 @@ -128,11 +128,11 @@ EnterOptionsDialog.ShowTableViewToolbar.Label=\u8868\u683C\u4E0A\u65B9\u663E\u79 EnterOptionsDialog.ShowTableViewToolbar.ToolTip=\u542F\u7528\u6B64\u9009\u9879\u5C06\u5728 Hop GUI \u4E2D\u7684\u6240\u6709\u8868\u683C\u4E0A\u65B9\u663E\u793A\u5DE5\u5177\u680F\u3002 EnterOptionsDialog.ShowViewport.Label=\u9690\u85CF\u89C6\u56FE EnterOptionsDialog.ShowViewport.ToolTip=\u5982\u679C\u542F\u7528\uFF0C\u5C06\u9690\u85CF\u53F3\u4E0B\u89D2\u89C6\u7A97 -EnterOptionsDialog.SortFieldByName.Label=\u6309\u540D\u79F0\u6392\u5E8F -EnterOptionsDialog.SortFieldByName.ToolTip=\u786E\u5B9A\u9009\u62E9\u5B57\u6BB5\u65F6\u7684\u6392\u5E8F(\u6309\u8F93\u5165\u987A\u5E8F\u6216\u6309\u540D\u79F0\u6392\u5E8F) +EnterOptionsDialog.SortFieldByName.Label= +EnterOptionsDialog.SortFieldByName.ToolTip= EnterOptionsDialog.TableOutput.SortMappings.Label=\u8868\u8F93\u51FA\: \u6392\u5E8F\u6620\u5C04 EnterOptionsDialog.Title=Hop \u9009\u9879 -EnterOptionsDialog.ToolTipsEnabled.Label=\u663E\u793A\u63D0\u793A\: +EnterOptionsDialog.ToolTipsEnabled.Label= EnterOptionsDialog.Transform.Label=Transforms EnterOptionsDialog.UseDatabaseCache.Label=\u4F7F\u7528\u6570\u636E\u5E93\u7F13\u5B58\: EnterOptionsDialog.UseDoubleClickOnCanvas.Label=\u5728canvas\u4F7F\u7528\u53CC\u51FB diff --git a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/configuration/messages/messages_en_US.properties b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/configuration/messages/messages_en_US.properties index ec5d64cedd2..21291a8fe17 100644 --- a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/configuration/messages/messages_en_US.properties +++ b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/configuration/messages/messages_en_US.properties @@ -19,3 +19,4 @@ ConfigurationPerspective.Description = A perspective to configure all aspects of Apache Hop ConfigurationPerspective.Name = Configuration Perspective HopConfigurationPerspective.GuiPlugin.Description = A perspective to configure all aspects of Apache Hop +HopConfigurationperspective.Search.Text=Search settings... \ No newline at end of file