diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 145b86628..1df581652 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -33,8 +33,12 @@ RocketActions.CopyAct.Copy = Copy RocketActions.CopyAct.ttip.Copy = Copy this component (and subcomponents) to the clipboard. RocketActions.PasteAct.Paste = Paste RocketActions.PasteAct.ttip.Paste = Paste the component or simulation on the clipboard to the design. +RocketActions.DuplicateAct.Duplicate = Duplicate +RocketActions.DuplicateAct.ttip.Duplicate = Duplicate this component (and subcomponents). RocketActions.EditAct.Edit = Edit RocketActions.EditAct.ttip.Edit = Edit the selected component. +RocketActions.ScaleAct.Scale = Scale +RocketActions.ScaleAct.ttip.Scale = Scale parts of the rocket design RocketActions.NewStageAct.Newstage = New stage RocketActions.NewStageAct.ttip.Newstage = Add a new stage to the rocket design. RocketActions.MoveUpAct.Moveup = Move up @@ -187,7 +191,7 @@ edtmotorconfdlg.col.configuration = Configuration edtmotorconfdlg.but.Removeconfiguration = Remove Configuration edtmotorconfdlg.but.Renameconfiguration = Rename Configuration edtmotorconfdlg.but.Newconfiguration = New Configuration -edtmotorconfdlg.but.Copyconfiguration = Copy Configuration +edtmotorconfdlg.but.Duplicateconfiguration = Duplicate Configuration edtmotorconfdlg.title.Editmotorconf = Edit Flight configurations edtmotorconfdlg.title.Renameconf = Rename Flight Configuration edtmotorconfdlg.title.Selectdeploymentconf = Select Deployment Configuration @@ -203,11 +207,11 @@ edtmotorconfdlg.tbl.None = None edtmotorconfdlg.tbl.Motorheader = Motor edtmotorconfdlg.tbl.Mountheader = Motor Mount edtmotorconfdlg.tbl.Ignitionheader = Ignition -edtmotorconfdlg.but.Resetdeployment = Reset to default +edtmotorconfdlg.but.Resetdeployment = Reset deployment edtmotorconfdlg.but.Selectdeployment = Select deployment edtmotorconfdlg.tbl.Recoveryheader = Recovery Device edtmotorconfdlg.tbl.Deploymentheader = Deployment -edtmotorconfdlg.but.Resetseparation = Reset to default +edtmotorconfdlg.but.Resetseparation = Reset separation edtmotorconfdlg.but.Selectseparation = Select separation edtmotorconfdlg.tbl.Stageheader = Stage edtmotorconfdlg.tbl.Separationheader = Separation @@ -491,6 +495,11 @@ simpanel.but.ttip.newsimulation = Add a new simulation simpanel.but.ttip.editsim = Edit the selected simulation simpanel.but.ttip.runsimu = Re-run the selected simulations simpanel.but.ttip.deletesim = Delete the selected simulations +simpanel.pop.edit = Edit +simpanel.pop.plot = Plot / Export +simpanel.pop.run = Run +simpanel.pop.delete = Delete +simpanel.pop.duplicate = Duplicate simpanel.checkbox.donotask = Do not ask me again simpanel.lbl.defpref = You can change the default operation in the preferences. simpanel.dlg.lbl.DeleteSim1 = Delete the selected simulations? @@ -1342,8 +1351,6 @@ main.menu.edit.cut = Cut main.menu.edit.copy = Copy main.menu.edit.paste = Paste main.menu.edit.delete = Delete -main.menu.edit.resize = Scale\u2026 -main.menu.edit.resize.desc = Scale parts of the rocket design main.menu.edit.editpreset= Edit Component Preset File main.menu.edit.preferences = Preferences main.menu.edit.preferences.desc = Setup the application preferences diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index 10f983b4b..c6354d4a8 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -35,6 +35,8 @@ RocketActions.PasteAct.Paste = Paste RocketActions.PasteAct.ttip.Paste = Paste the component or simulation on the clipboard to the design. RocketActions.EditAct.Edit = Edit RocketActions.EditAct.ttip.Edit = Edituj vybranou komponentu. +RocketActions.ScaleAct.Scale = Merítko +RocketActions.ScaleAct.ttip.Scale = Merítko cástí návrhu rakety RocketActions.NewStageAct.Newstage = Nový stupen RocketActions.NewStageAct.ttip.Newstage = Pridej nový stupen rakety. RocketActions.ActBoosterstage = Urychlovací stupen @@ -1000,8 +1002,6 @@ main.menu.edit.cut = Vyjmout main.menu.edit.copy = Kopírovat main.menu.edit.paste = Vlo\u017Eit main.menu.edit.delete = Smazat -main.menu.edit.resize = Merítko... -main.menu.edit.resize.desc = Merítko cástí návrhu rakety main.menu.edit.preferences = Nastavení main.menu.edit.preferences.desc = Nastavení aplikace diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 15b4943f0..442081510 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -35,6 +35,8 @@ RocketActions.PasteAct.Paste = Einf RocketActions.PasteAct.ttip.Paste = Fügt die Komponente oder Simulation aus der Zwischenablage in das Design ein. RocketActions.EditAct.Edit = Bearbeiten RocketActions.EditAct.ttip.Edit = Die ausgewählte Komponente bearbeiten. +RocketActions.ScaleAct.Scale = Skalieren +RocketActions.ScaleAct.ttip.Scale = Teile des Raketendesigns skalieren RocketActions.NewStageAct.Newstage = Neue Stufe RocketActions.NewStageAct.ttip.Newstage = Fügt eine neue Stufe in das Raketendesign ein. RocketActions.ActBoosterstage = Booster-Stufe @@ -1057,8 +1059,6 @@ main.menu.edit.cut = Ausschneiden main.menu.edit.copy = Kopieren main.menu.edit.paste = Einfügen main.menu.edit.delete = Löschen -main.menu.edit.resize = Skalieren... -main.menu.edit.resize.desc = Teile des Raketendesigns skalieren main.menu.edit.preferences = Einstellungen main.menu.edit.preferences.desc = Einstellungen der Anwenung ändern diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 960f883ae..9d0e5b2c3 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -802,6 +802,8 @@ RocketActions.DelSimuAct.Delete = Borrar RocketActions.DelSimuAct.ttip.Delete = Borrar la simulaci\u00f3n seleccionada RocketActions.EditAct.Edit = Editar componente RocketActions.EditAct.ttip.Edit = Editar valores del componente seleccionado +RocketActions.ScaleAct.Scale = Dimensionar +RocketActions.ScaleAct.ttip.Scale = Dimensionar las partes del dise\u00f1o del cohete RocketActions.MoveDownAct.Movedown = Mover hacia abajo RocketActions.MoveDownAct.ttip.Movedown = Mover este componente hacia abajo RocketActions.MoveUpAct.Moveup = Mover hacia arriba @@ -1380,13 +1382,13 @@ dlg.but.close = Cerrar ! Common labels used in buttons of dialog windows dlg.but.ok = OK -edtmotorconfdlg.but.Copyconfiguration = Copiar +edtmotorconfdlg.but.Duplicateconfiguration = Duplicar edtmotorconfdlg.but.Newconfiguration = Nueva configuraci\u00f3n ! Edit Motor configuration dialog edtmotorconfdlg.but.Removeconfiguration = Quitar configuraci\u00f3n edtmotorconfdlg.but.Renameconfiguration = Renombrar configuraci\u00f3n -edtmotorconfdlg.but.Resetdeployment = Reiniciar valor por defecto -edtmotorconfdlg.but.Resetseparation = Reiniciar valor por defecto +edtmotorconfdlg.but.Resetdeployment = Reiniciar despliegue +edtmotorconfdlg.but.Resetseparation = Reiniciar separaci\u00f3n edtmotorconfdlg.but.Selectdeployment = Seleccionar despliegue edtmotorconfdlg.but.Selectseparation = Seleccionar separaci\u00f3n edtmotorconfdlg.col.configuration = Configuraci\u00f3n @@ -1449,8 +1451,6 @@ main.menu.edit.preferences = Preferencias main.menu.edit.preferences.desc = Configurar las preferencias de la aplicaci\u00f3n main.menu.edit.redo = Rehacer main.menu.edit.redo.desc = Rehacer la operaci\u00f3n anterior -main.menu.edit.resize = Dimensionar... -main.menu.edit.resize.desc = Dimensionar las partes del dise\u00f1o del cohete main.menu.edit.undo = Deshacer main.menu.edit.undo.desc = Deshacer la operaci\u00f3n anterior ! "main" prefix is used for the main application dialog diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index fef49ea70..ab7fb5544 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -793,6 +793,8 @@ RocketActions.DelSimuAct.Delete = Effacer RocketActions.DelSimuAct.ttip.Delete = Effacer la simulation s\u00E9lectionn\u00E9e. RocketActions.EditAct.Edit = Modifier RocketActions.EditAct.ttip.Edit = Modifier la pi\u00E8ce s\u00E9lectionn\u00E9e. +RocketActions.ScaleAct.Scale = Redimensionner +RocketActions.ScaleAct.ttip.Scale = Redimensionner certaines parties de la fus\u00E9e RocketActions.MoveDownAct.Movedown = Descendre RocketActions.MoveDownAct.ttip.Movedown = Descendre la pi\u00E8ce. RocketActions.MoveUpAct.Moveup = Monter @@ -1375,12 +1377,12 @@ dlg.but.close = Fermer ! Common labels used in buttons of dialog windows dlg.but.ok = Accepter -edtmotorconfdlg.but.Copyconfiguration = Copier +edtmotorconfdlg.but.Duplicateconfiguration = Dupliquer edtmotorconfdlg.but.Newconfiguration = Nouvelle configuration edtmotorconfdlg.but.Removeconfiguration = Supprimer la configuration edtmotorconfdlg.but.Renameconfiguration = Renommer -edtmotorconfdlg.but.Resetdeployment = R\u00E9initialiser \u00E0 la valeur par d\u00E9faut -edtmotorconfdlg.but.Resetseparation = R\u00E9initialiser \u00E0 la valeur par d\u00E9faut +edtmotorconfdlg.but.Resetdeployment = R\u00E9initialiserle d\u00E9ploiement +edtmotorconfdlg.but.Resetseparation = R\u00E9initialiser la s\u00E9paration edtmotorconfdlg.but.Selectdeployment = Choisir le d\u00E9ploiement edtmotorconfdlg.but.Selectseparation = Choisir la s\u00E9paration edtmotorconfdlg.col.configuration = Configuration @@ -1443,8 +1445,6 @@ main.menu.edit.preferences = Pr\u00E9f\u00E9rences main.menu.edit.preferences.desc = Configure les pr\u00E9f\u00E9rences de l'application main.menu.edit.redo = Refaire modification main.menu.edit.redo.desc = Refaire l'op\u00E9ration precedente qui avait \u00E9t\u00E9 d\u00E9faite -main.menu.edit.resize = Redimensionner... -main.menu.edit.resize.desc = Redimensionner certaines parties de la fus\u00E9e main.menu.edit.undo = Annuler modification main.menu.edit.undo.desc = Annuler l'op\u00E9ration pr\u00E9c\u00E9dente ! "main" prefix is used for the main application dialog diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index 0230e85a6..ef7a5d226 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -35,6 +35,8 @@ RocketActions.PasteAct.Paste = Incolla RocketActions.PasteAct.ttip.Paste = Incolla il componente o la simulazione dagli appunti al disegno. RocketActions.EditAct.Edit = Modifica RocketActions.EditAct.ttip.Edit = Modifica il componente selezionato. +RocketActions.ScaleAct.Scale = Scala +RocketActions.ScaleAct.ttip.Scale = Scala parti del razzo RocketActions.NewStageAct.Newstage = Nuovo stadio RocketActions.NewStageAct.ttip.Newstage = Aggiungi un nuovo stadio al disegno del razzo. RocketActions.ActBoosterstage = Stadio Booster @@ -1061,8 +1063,6 @@ main.menu.edit.cut = Taglia main.menu.edit.copy = Copia main.menu.edit.paste = Incolla main.menu.edit.delete = Cancella -main.menu.edit.resize = Scala... -main.menu.edit.resize.desc = Scala parti del razzo main.menu.edit.preferences = Preferenze main.menu.edit.preferences.desc = Imposta le preferenze dell'applicazione diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index bd73caaf9..8cf8b6b03 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -31,6 +31,8 @@ RocketActions.PasteAct.Paste = \u8CBC\u308A\u4ED8\u3051 RocketActions.PasteAct.ttip.Paste = \u90E8\u54C1\u3084\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u8CBC\u308A\u4ED8\u3051 RocketActions.EditAct.Edit = \u7DE8\u96C6 RocketActions.EditAct.ttip.Edit = \u9078\u629E\u3057\u305F\u90E8\u54C1\u306E\u7DE8\u96C6 +RocketActions.ScaleAct.Scale = \u62E1\u5927\u7E2E\u5C0F +RocketActions.ScaleAct.ttip.Scale = \u90E8\u54C1\u306E\u5927\u304D\u3055\u3092\u8CB7\u3048\u308B RocketActions.NewStageAct.Newstage = \u65B0\u3057\u3044\u30B9\u30C6\u30FC\u30B8 RocketActions.NewStageAct.ttip.Newstage = \u30ED\u30B1\u30C3\u30C8\u306B\u65B0\u3057\u3044\u30B9\u30C6\u30FC\u30B8\u3092\u8FFD\u52A0\u3059\u308B RocketActions.ActBoosterstage = \u30D6\u30FC\u30B9\u30BF\u30FC\u30B9\u30C6\u30FC\u30B8 @@ -1092,8 +1094,6 @@ main.menu.edit.cut = \u5207\u308A\u53D6\u308A main.menu.edit.copy = \u30B3\u30D4\u30FC main.menu.edit.paste = \u8CBC\u308A\u4ED8\u3051 main.menu.edit.delete = \u524A\u9664 -main.menu.edit.resize = \u62E1\u5927\u7E2E\u5C0F... -main.menu.edit.resize.desc = \u90E8\u54C1\u306E\u5927\u304D\u3055\u3092\u8CB7\u3048\u308B main.menu.edit.editpreset = Component Preset File\u306E\u7DE8\u96C6 main.menu.edit.preferences = \u8A2D\u5B9A main.menu.edit.preferences.desc = \u30A2\u30D7\u30EA\u306E\u8A2D\u5B9A\u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7 diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 61e4147aa..557859b8a 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -37,6 +37,8 @@ RocketActions.PasteAct.Paste = Plak RocketActions.PasteAct.ttip.Paste = Plak dit onderdeel of simulatie van het klembord naar het ontwerp. RocketActions.EditAct.Edit = Bewerk RocketActions.EditAct.ttip.Edit = Pas geselecteerd onderdeel aan. +RocketActions.ScaleAct.Scale = Herschaal +RocketActions.ScaleAct.ttip.Scale = Herschaal delen van het raketontwerp RocketActions.NewStageAct.Newstage = Nieuwe trap RocketActions.NewStageAct.ttip.Newstage = Voeg nieuwe trap toe aan het raketontwerp. RocketActions.MoveUpAct.Moveup = Beweeg omhoog @@ -179,7 +181,7 @@ edtmotorconfdlg.col.configuration = Configuratie edtmotorconfdlg.but.Removeconfiguration = Verwijder Configuratie edtmotorconfdlg.but.Renameconfiguration = Hernoem Configuratie edtmotorconfdlg.but.Newconfiguration = Nieuwe Configuratie -edtmotorconfdlg.but.Copyconfiguration = Kopieer Configuratie +edtmotorconfdlg.but.Duplicateconfiguration = Dupliceer Configuratie edtmotorconfdlg.title.Editmotorconf = Pas Vluchtconfiguratie Aan edtmotorconfdlg.title.Renameconf = Hernoem Vluchtconfiguratie edtmotorconfdlg.title.Selectdeploymentconf = Selecteer Ontplooiingconfiguratie @@ -195,11 +197,11 @@ edtmotorconfdlg.tbl.None = Geen edtmotorconfdlg.tbl.Motorheader = Motor edtmotorconfdlg.tbl.Mountheader = Motorbevestiging edtmotorconfdlg.tbl.Ignitionheader = Ontsteking -edtmotorconfdlg.but.Resetdeployment = Herstel naar standaard +edtmotorconfdlg.but.Resetdeployment = Herstel ontplooiing edtmotorconfdlg.but.Selectdeployment = Selecteer ontplooiing edtmotorconfdlg.tbl.Recoveryheader = Herstelapparaat edtmotorconfdlg.tbl.Deploymentheader = Ontplooiing -edtmotorconfdlg.but.Resetseparation = Herstel naar standaard +edtmotorconfdlg.but.Resetseparation = Herstel afscheiding edtmotorconfdlg.but.Selectseparation = Selecteer afscheiding edtmotorconfdlg.tbl.Stageheader = Trap edtmotorconfdlg.tbl.Separationheader = Afscheiding @@ -1269,8 +1271,6 @@ main.menu.edit.cut = Knop main.menu.edit.copy = Kopieer main.menu.edit.paste = Plak main.menu.edit.delete = Verwijder -main.menu.edit.resize = Schaal... -main.menu.edit.resize.desc = Schaal delen van het raketontwerp main.menu.edit.editpreset= Bewerk Component Preset bestand main.menu.edit.preferences = Voorkeuren main.menu.edit.preferences.desc = Stel de programma voorkeuren in diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index 36bb37169..05127de1c 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -35,6 +35,8 @@ RocketActions.PasteAct.ttip.Paste = Wklej wybran\u0105 cz\u0119\u015B\u0107 lub symulacj\u0119 ze schowka do projektu. RocketActions.EditAct.Edit = Edycja RocketActions.EditAct.ttip.Edit = Edytuj zaznaczon\u0105 cz\u0119\u015B\u0107. + RocketActions.ScaleAct.Scale = Skaluj + RocketActions.ScaleAct.ttip.Scale = Skaluj cz\u0119\u015Bci modelu rakiety RocketActions.NewStageAct.Newstage = Nowy stopie\u0144 RocketActions.NewStageAct.ttip.Newstage = Dodaj stopie\u0144 do projektu rakiety. RocketActions.ActBoosterstage = Stopie\u0144 startowy rakiety @@ -1002,8 +1004,6 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. main.menu.edit.copy = Kopiuj main.menu.edit.paste = Wklej main.menu.edit.delete = Usu\u0144 - main.menu.edit.resize = Skaluj... - main.menu.edit.resize.desc = Skaluj cz\u0119\u015Bci modelu rakiety... main.menu.edit.preferences = Ustawienia main.menu.edit.preferences.desc = Edytuj ustawienia programu diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties index 7d8b195b8..c40699812 100644 --- a/core/resources/l10n/messages_pt.properties +++ b/core/resources/l10n/messages_pt.properties @@ -778,6 +778,8 @@ RocketActions.DelSimuAct.Delete = Excluir RocketActions.DelSimuAct.ttip.Delete = Excluir a simula\u00e7\u00e3o selecionada. RocketActions.EditAct.Edit = Editar RocketActions.EditAct.ttip.Edit = Edite o componente selecionado. +RocketActions.ScaleAct.Scale = Escala... +RocketActions.ScaleAct.ttip.Scale = Escalar partes do projeto do foguete RocketActions.MoveDownAct.Movedown = Mover para baixo RocketActions.MoveDownAct.ttip.Movedown = Mover este componente para baixo. RocketActions.MoveUpAct.Moveup = Mover para cima @@ -1338,12 +1340,12 @@ dlg.but.close = Fechar # Common labels used in buttons of dialog windows dlg.but.ok = Ok -edtmotorconfdlg.but.Copyconfiguration = Copiar +edtmotorconfdlg.but.Duplicateconfiguration = Duplikat edtmotorconfdlg.but.Newconfiguration = Nova configura\u00e7\u00e3o edtmotorconfdlg.but.Removeconfiguration = Remover configura\u00e7\u00e3o edtmotorconfdlg.but.Renameconfiguration = Renomear -edtmotorconfdlg.but.Resetdeployment = Retornar aos padr\u00f5es -edtmotorconfdlg.but.Resetseparation = Retornar aos padr\u00f5es +edtmotorconfdlg.but.Resetdeployment = Retornar o lan\u00e7amento +edtmotorconfdlg.but.Resetseparation = Retornar a separa\u00e7\u00e3o edtmotorconfdlg.but.Selectdeployment = Selecionar o lan\u00e7amento edtmotorconfdlg.but.Selectseparation = Selecionar a separa\u00e7\u00e3o edtmotorconfdlg.lbl.Configname = Nome de configura\u00e7\u00e3o: @@ -1405,8 +1407,6 @@ main.menu.edit.preferences = Prefer\u00eancias main.menu.edit.preferences.desc = Configurar as prefer\u00eancias do aplicativo main.menu.edit.redo = Refazer main.menu.edit.redo.desc = Refazer a opera\u00e7\u00e3o previamente desfeita -main.menu.edit.resize = Escala... -main.menu.edit.resize.desc = Escalar partes do projeto do foguete main.menu.edit.undo = Desfazer main.menu.edit.undo.desc = Desfazer a opera\u00e7\u00e3o anterior # "main" prefix is used for the main application dialog diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index de8cd8a45..3279250b7 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -35,6 +35,8 @@ RocketActions.PasteAct.Paste = \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c RocketActions.PasteAct.ttip.Paste = \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438\u043b\u0438 \u0440\u0430\u0441\u0447\u0435\u0442 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043e\u0431\u043c\u0435\u043d\u0430. RocketActions.EditAct.Edit = \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c RocketActions.EditAct.ttip.Edit = \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442. +RocketActions.ScaleAct.Scale = \u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c +RocketActions.ScaleAct.ttip.Scale = \u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438 \u0441\u0445\u0435\u043c\u044b RocketActions.NewStageAct.Newstage = \u041d\u043e\u0432\u0430\u044f \u0441\u0442\u0443\u043f\u0435\u043d\u044c RocketActions.NewStageAct.ttip.Newstage = \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0441\u0442\u0443\u043f\u0435\u043d\u044c \u0432 \u0441\u0445\u0435\u043c\u0443. RocketActions.ActBoosterstage = \u0420\u0430\u0437\u0433\u043e\u043d\u043d\u0430\u044f \u0441\u0442\u0443\u043f\u0435\u043d\u044c @@ -1164,8 +1166,6 @@ main.menu.edit.cut = \u0412\u044b\u0440\u0435\u0437\u0430\u0442\u044c main.menu.edit.copy = \u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c main.menu.edit.paste = \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c main.menu.edit.delete = \u0423\u0434\u0430\u043b\u0438\u0442\u044c -main.menu.edit.resize = \u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c... -main.menu.edit.resize.desc = \u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438 \u0441\u0445\u0435\u043c\u044b main.menu.edit.editprese = \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 main.menu.edit.preferences = \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 main.menu.edit.preferences.desc = \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index b6de29107..8a06a77ef 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -37,6 +37,8 @@ RocketActions.PasteAct.Paste = Paste RocketActions.PasteAct.ttip.Paste = Paste the component or simulation on the clipboard to the design. RocketActions.EditAct.Edit = Edit RocketActions.EditAct.ttip.Edit = Edit the selected component. +RocketActions.ScaleAct.Scale = Scale +RocketActions.ScaleAct.ttip.Scale = Scale parts of the rocket design RocketActions.NewStageAct.Newstage = New stage RocketActions.NewStageAct.ttip.Newstage = Add a new stage to the rocket design. RocketActions.ActBoosterstage = Booster stage @@ -181,7 +183,7 @@ edtmotorconfdlg.col.configuration = Configuration edtmotorconfdlg.but.Removeconfiguration = Remove Configuration edtmotorconfdlg.but.Renameconfiguration = Rename Configuration edtmotorconfdlg.but.Newconfiguration = New Configuration -edtmotorconfdlg.but.Copyconfiguration = Copy Configuration +edtmotorconfdlg.but.Duplicateconfiguration = Duplicate Configuration edtmotorconfdlg.title.Editmotorconf = Edit Flight configurations edtmotorconfdlg.title.Renameconf = Rename Flight Configuration edtmotorconfdlg.title.Selectdeploymentconf = Select Deployment Configuration @@ -197,11 +199,11 @@ edtmotorconfdlg.tbl.None = None edtmotorconfdlg.tbl.Motorheader = Motor edtmotorconfdlg.tbl.Mountheader = Motor Mount edtmotorconfdlg.tbl.Ignitionheader = Ignition -edtmotorconfdlg.but.Resetdeployment = Reset to default +edtmotorconfdlg.but.Resetdeployment = Reset deployment edtmotorconfdlg.but.Selectdeployment = Select deployment edtmotorconfdlg.tbl.Recoveryheader = Recovery Device edtmotorconfdlg.tbl.Deploymentheader = Deployment -edtmotorconfdlg.but.Resetseparation = Reset to default +edtmotorconfdlg.but.Resetseparation = Reset separation edtmotorconfdlg.but.Selectseparation = Select separation edtmotorconfdlg.tbl.Stageheader = Stage edtmotorconfdlg.tbl.Separationheader = Separation @@ -1170,8 +1172,6 @@ main.menu.edit.cut = Cut main.menu.edit.copy = Copy main.menu.edit.paste = Paste main.menu.edit.delete = Delete -main.menu.edit.resize = Scale... -main.menu.edit.resize.desc = Scale parts of the rocket design main.menu.edit.editpreset= Edit Component Preset File main.menu.edit.preferences = Preferences main.menu.edit.preferences.desc = Setup the application preferences diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties index 1bb91ba81..9e9cfed05 100644 --- a/core/resources/l10n/messages_zh_CN.properties +++ b/core/resources/l10n/messages_zh_CN.properties @@ -867,6 +867,8 @@ RocketActions.DelSimuAct.Delete = \u5220\u9664 RocketActions.DelSimuAct.ttip.Delete = \u5220\u9664\u9009\u5B9A\u4EFF\u771F RocketActions.EditAct.Edit = \u7F16\u8F91 RocketActions.EditAct.ttip.Edit = \u7F16\u8F91\u9009\u4E2D\u7EC4\u4EF6 +RocketActions.ScaleAct.Scale = \u7F29\u653E +RocketActions.ScaleAct.ttip.Scale = \u7F29\u653E\u706B\u7BAD\u8BBE\u8BA1\u7A3F RocketActions.MoveDownAct.Movedown = \u5411\u4E0B\u79FB\u52A8 RocketActions.MoveDownAct.ttip.Movedown = \u5411\u4E0B\u79FB\u52A8\u8BE5\u90E8\u4EF6 RocketActions.MoveUpAct.Moveup = \u5411\u4E0A\u79FB\u52A8 @@ -1528,8 +1530,6 @@ main.menu.edit.preferences = \u9996\u9009\u9879 main.menu.edit.preferences.desc = \u4FEE\u6539\u5E94\u7528\u7A0B\u5E8F\u9996\u9009\u9879 main.menu.edit.redo = \u91CD\u505A main.menu.edit.redo.desc = \u91CD\u505A\u64CD\u4F5C -main.menu.edit.resize = \u7F29\u653E... -main.menu.edit.resize.desc = \u7F29\u653E\u706B\u7BAD\u8BBE\u8BA1\u7A3F main.menu.edit.undo = \u64A4\u9500 main.menu.edit.undo.desc = \u64A4\u9500\u64CD\u4F5C ! "main" prefix is used for the main application dialog diff --git a/core/resources/pix/icons/edit-copy.png b/core/resources/pix/icons/edit-copy.png index b7c938a99..28ac512bd 100644 Binary files a/core/resources/pix/icons/edit-copy.png and b/core/resources/pix/icons/edit-copy.png differ diff --git a/core/resources/pix/icons/edit-duplicate.png b/core/resources/pix/icons/edit-duplicate.png new file mode 100644 index 000000000..c02116357 Binary files /dev/null and b/core/resources/pix/icons/edit-duplicate.png differ diff --git a/core/resources/pix/icons/edit-edit.png b/core/resources/pix/icons/edit-edit.png new file mode 100644 index 000000000..44310b471 Binary files /dev/null and b/core/resources/pix/icons/edit-edit.png differ diff --git a/core/resources/pix/icons/sim-plot.png b/core/resources/pix/icons/sim-plot.png new file mode 100644 index 000000000..32f27ca7b Binary files /dev/null and b/core/resources/pix/icons/sim-plot.png differ diff --git a/core/resources/pix/icons/sim-run.png b/core/resources/pix/icons/sim-run.png new file mode 100644 index 000000000..495d6d432 Binary files /dev/null and b/core/resources/pix/icons/sim-run.png differ diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java index baea99547..ad53576e6 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java @@ -345,17 +345,31 @@ public class ScaleDialog extends JDialog { selectionOption.setToolTipText(tip); panel.add(selectionOption, "growx, wrap para*2"); - // Change the offset checkbox to false when 'Scale selection' is selection and only one component is selected, - // since this is a common action. - selectionOption.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (SCALE_SELECTION.equals(selectionOption.getSelectedItem()) && (selection != null) && - (selection.size() == 1) && (scaleOffsets != null)) { - scaleOffsets.setSelected(false); + // Select the 'scale component / scale selection and all subcomponents' if a component is selected + if (selection != null && selection.size() > 0) { + boolean entireRocket = false; // Flag to scale entire rocket + for (RocketComponent component : selection) { + if (component instanceof Rocket || (component instanceof AxialStage && !(component instanceof ParallelStage))) { + entireRocket = true; + break; } } - }); + if (!entireRocket) { + selectionOption.setSelectedIndex(1); + } + } + + // Change the offset checkbox to false when 'Scale selection' is selection and only one component is selected, + // since this is a common action. + ItemListener listener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (scaleOffsets == null) return; + + scaleOffsets.setSelected(!SCALE_SELECTION.equals(selectionOption.getSelectedItem())); + } + }; + selectionOption.addItemListener(listener); // Scale multiplier @@ -424,7 +438,7 @@ public class ScaleDialog extends JDialog { // Scale offsets scaleOffsets = new JCheckBox(trans.get("checkbox.scaleOffsets")); scaleOffsets.setToolTipText(trans.get("checkbox.scaleOffsets.ttip")); - scaleOffsets.setSelected(true); + listener.itemStateChanged(null); // Triggers the selection state of scaleOffsets panel.add(scaleOffsets, "span, wrap para*3"); @@ -457,7 +471,7 @@ public class ScaleDialog extends JDialog { }); panel.add(cancel, "right, gap para"); - + GUIUtil.setDisposableDialogOptions(this, scale); } @@ -571,7 +585,7 @@ public class ScaleDialog extends JDialog { */ private void scaleChildren(RocketComponent component, List scaledComponents, double mul, boolean scaleMass) { for (RocketComponent child : component.getChildren()) { - if (!scaledComponents.contains(component)) { + if (!scaledComponents.contains(child)) { scale(child, mul, scaleMass, scaleOffsets.isSelected()); scaledComponents.add(child); scaleChildren(child, scaledComponents, mul, scaleMass); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java index c2452a36d..2030dba2b 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java @@ -60,7 +60,7 @@ import com.google.inject.Module; @SuppressWarnings("serial") public class PhotoFrame extends JFrame { private static final Logger log = LoggerFactory.getLogger(PhotoFrame.class); - private final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + private final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); private final Translator trans = Application.getTranslator(); private final PhotoPanel photoPanel; diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index ac607b8bd..880713cfa 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -51,7 +51,6 @@ import net.sf.openrocket.gui.dialogs.DecalNotFoundDialog; import net.sf.openrocket.gui.dialogs.DetailDialog; import net.sf.openrocket.gui.dialogs.LicenseDialog; import net.sf.openrocket.gui.dialogs.PrintDialog; -import net.sf.openrocket.gui.dialogs.ScaleDialog; import net.sf.openrocket.gui.dialogs.SwingWorkerDialog; import net.sf.openrocket.gui.dialogs.WarningDialog; import net.sf.openrocket.gui.dialogs.optimization.GeneralOptimizationDialog; @@ -87,6 +86,8 @@ import net.sf.openrocket.utils.ComponentPresetEditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; + public class BasicFrame extends JFrame { private static final long serialVersionUID = 948877655223365313L; @@ -98,7 +99,10 @@ public class BasicFrame extends JFrame { private static final Translator trans = Application.getTranslator(); private static final Preferences prefs = Application.getPreferences(); - private static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + private static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); + + private static final int SHIFT_SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | + SHIFT_DOWN_MASK; public static final int COMPONENT_TAB = 0; public static final int CONFIGURATION_TAB = 1; @@ -124,6 +128,7 @@ public class BasicFrame extends JFrame { private JTabbedPane tabbedPane; private RocketPanel rocketpanel; private ComponentTree tree = null; + private final JPopupMenu popupMenu; private final DocumentSelectionModel selectionModel; private final TreeSelectionModel componentSelectionModel; @@ -161,7 +166,18 @@ public class BasicFrame extends JFrame { selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); - actions = new RocketActions(document, selectionModel, this); + actions = new RocketActions(document, selectionModel, this, simulationPanel); + + // Populate the popup menu + popupMenu = new JPopupMenu(); + popupMenu.add(actions.getEditAction()); + popupMenu.add(actions.getCutAction()); + popupMenu.add(actions.getCopyAction()); + popupMenu.add(actions.getPasteAction()); + popupMenu.add(actions.getDuplicateAction()); + popupMenu.add(actions.getDeleteAction()); + popupMenu.addSeparator(); + popupMenu.add(actions.getScaleAction()); log.debug("Constructing the BasicFrame UI"); @@ -278,16 +294,25 @@ public class BasicFrame extends JFrame { // Double-click opens config dialog MouseListener ml = new MouseAdapter() { @Override - public void mousePressed(MouseEvent e) { + public void mouseClicked(MouseEvent e) { int selRow = tree.getRowForLocation(e.getX(), e.getY()); TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); if (selRow != -1) { - if ((e.getClickCount() == 2) && !ComponentConfigDialog.isDialogVisible()) { + if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2) && !ComponentConfigDialog.isDialogVisible()) { // Double-click RocketComponent c = (RocketComponent) selPath.getLastPathComponent(); ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, c); + } else if ((e.getButton() == MouseEvent.BUTTON3) && (e.getClickCount() == 1)) { + if (!tree.isPathSelected(selPath)) { + // Select new path + tree.setSelectionPath(selPath); + } + + doComponentTreePopup(e); } + } else { + tree.clearSelection(); } } }; @@ -334,6 +359,13 @@ public class BasicFrame extends JFrame { panel.add(button, "sizegroup buttons, aligny 0%"); button = new SelectColorButton(actions.getEditAction()); + button.setIcon(null); + button.setMnemonic(0); + panel.add(button, "sizegroup buttons, gaptop 20%"); + + button = new SelectColorButton(actions.getDuplicateAction()); + button.setIcon(null); + button.setMnemonic(0); panel.add(button, "sizegroup buttons"); button = new SelectColorButton(actions.getDeleteAction()); @@ -689,6 +721,9 @@ public class BasicFrame extends JFrame { menu.addSeparator(); + item = new JMenuItem(actions.getEditAction()); + menu.add(item); + item = new JMenuItem(actions.getCutAction()); menu.add(item); @@ -698,24 +733,15 @@ public class BasicFrame extends JFrame { item = new JMenuItem(actions.getPasteAction()); menu.add(item); + item = new JMenuItem(actions.getDuplicateAction()); + menu.add(item); + item = new JMenuItem(actions.getDeleteAction()); menu.add(item); menu.addSeparator(); - - item = new JMenuItem(trans.get("main.menu.edit.resize")); - item.setIcon(Icons.EDIT_SCALE); - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc")); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - log.info(Markers.USER_MARKER, "Scale... selected"); - ScaleDialog dialog = new ScaleDialog(document, getSelectedComponents(), BasicFrame.this); - dialog.setVisible(true); - dialog.dispose(); - } - }); + item = new JMenuItem(actions.getScaleAction()); menu.add(item); @@ -856,7 +882,7 @@ public class BasicFrame extends JFrame { //// Debug log item = new JMenuItem(trans.get("main.menu.help.debugLog"), KeyEvent.VK_D); item.setIcon(Icons.HELP_DEBUG_LOG); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, SHORTCUT_KEY)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, SHIFT_SHORTCUT_KEY)); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.debugLog.desc")); item.addActionListener(new ActionListener() { @Override @@ -898,6 +924,10 @@ public class BasicFrame extends JFrame { this.setJMenuBar(menubar); } + public void doComponentTreePopup(MouseEvent e) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + private JMenu makeDebugMenu() { JMenu menu; JMenuItem item; diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index d945d653d..ca3e49a5e 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -5,6 +5,7 @@ import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -21,8 +22,10 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; +import net.sf.openrocket.gui.dialogs.ScaleDialog; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.ParallelStage; @@ -32,7 +35,8 @@ import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.Pair; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -44,16 +48,21 @@ import net.sf.openrocket.util.Pair; public class RocketActions { public static final KeyStroke CUT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_X, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); public static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); public static final KeyStroke PASTE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_V, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); + public static final KeyStroke DUPLICATE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_D, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); + public static final KeyStroke EDIT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_E, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); private final OpenRocketDocument document; private final Rocket rocket; private final BasicFrame parentFrame; private final DocumentSelectionModel selectionModel; + private final SimulationPanel simulationPanel; private final RocketAction deleteComponentAction; @@ -62,19 +71,23 @@ public class RocketActions { private final RocketAction cutAction; private final RocketAction copyAction; private final RocketAction pasteAction; + private final RocketAction duplicateAction; private final RocketAction editAction; + private final RocketAction scaleAction; private final RocketAction newStageAction; private final RocketAction moveUpAction; private final RocketAction moveDownAction; private static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(RocketActions.class); public RocketActions(OpenRocketDocument document, DocumentSelectionModel selectionModel, - BasicFrame parentFrame) { + BasicFrame parentFrame, SimulationPanel simulationPanel) { this.document = document; this.rocket = document.getRocket(); this.selectionModel = selectionModel; this.parentFrame = parentFrame; + this.simulationPanel = simulationPanel; // Add action also to updateActions() this.deleteAction = new DeleteAction(); @@ -83,7 +96,9 @@ public class RocketActions { this.cutAction = new CutAction(); this.copyAction = new CopyAction(); this.pasteAction = new PasteAction(); + this.duplicateAction = new DuplicateAction(); this.editAction = new EditAction(); + this.scaleAction = new ScaleAction(); this.newStageAction = new NewStageAction(); this.moveUpAction = new MoveUpAction(); this.moveDownAction = new MoveDownAction(); @@ -115,7 +130,9 @@ public class RocketActions { cutAction.clipboardChanged(); copyAction.clipboardChanged(); pasteAction.clipboardChanged(); + duplicateAction.clipboardChanged(); editAction.clipboardChanged(); + scaleAction.clipboardChanged(); newStageAction.clipboardChanged(); moveUpAction.clipboardChanged(); moveDownAction.clipboardChanged(); @@ -147,10 +164,18 @@ public class RocketActions { public Action getPasteAction() { return pasteAction; } + + public Action getDuplicateAction() { + return duplicateAction; + } public Action getEditAction() { return editAction; } + + public Action getScaleAction() { + return scaleAction; + } public Action getNewStageAction() { return newStageAction; @@ -243,20 +268,10 @@ public class RocketActions { for (int i = 0; i < components.size(); i++) { if (components.contains(components.get(i).getParent())) { - RocketComponent oldChild = components.get(i); - RocketComponent oldParent = oldChild.getParent(); + RocketComponent originalParent = components.get(i).getParent(); + int originalParentIdx = components.indexOf(originalParent); - int index = components.indexOf(oldParent); - int childPos = oldParent.getChildPosition(oldChild); - - RocketComponent newChild = result.get(i); - RocketComponent newParent = result.get(index); - - // Add the newly copied child to the parent - newParent.addChild(newChild, childPos); - - // Remove the old child from the parent - newParent.removeChild(oldChild); + result.get(originalParentIdx).addChild(result.get(i)); } } @@ -646,7 +661,7 @@ public class RocketActions { this.putValue(MNEMONIC_KEY, KeyEvent.VK_C); this.putValue(ACCELERATOR_KEY, COPY_KEY_STROKE); //// Copy this component (and subcomponents) to the clipboard. - this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.CopyAct.Copy")); + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.CopyAct.ttip.Copy")); this.putValue(SMALL_ICON, Icons.EDIT_COPY); clipboardChanged(); } @@ -667,12 +682,12 @@ public class RocketActions { OpenRocketClipboard.setClipboard(copiedComponents); parentFrame.selectTab(BasicFrame.COMPONENT_TAB); - } else if (sims.length > 0) { - + } else if (sims != null && sims.length > 0) { Simulation[] simsCopy = new Simulation[sims.length]; for (int i=0; i < sims.length; i++) { simsCopy[i] = sims[i].copy(); } + OpenRocketClipboard.setClipboard(simsCopy); parentFrame.selectTab(BasicFrame.SIMULATION_TAB); } @@ -765,9 +780,98 @@ public class RocketActions { (OpenRocketClipboard.getClipboardSimulations() != null)); } } - - - + + + /** + * Action that duplicates the selected component. + */ + private class DuplicateAction extends RocketAction { + private static final long serialVersionUID = 1L; + + public DuplicateAction() { + //// Copy + this.putValue(NAME, trans.get("RocketActions.DuplicateAct.Duplicate")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); + this.putValue(ACCELERATOR_KEY, DUPLICATE_KEY_STROKE); + //// Copy this component (and subcomponents) to the clipboard. + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DuplicateAct.ttip.Duplicate")); + this.putValue(SMALL_ICON, Icons.EDIT_DUPLICATE); + clipboardChanged(); + } + + @Override + public void actionPerformed(ActionEvent e) { + List components = selectionModel.getSelectedComponents(); + if (components != null) { + components.sort(Comparator.comparing(c -> c.getParent() != null ? c.getParent().getChildPosition(c) : 0)); + components = new ArrayList<>(components); + fillInMissingSelections(components); + } + Simulation[] sims = selectionModel.getSelectedSimulations(); + + if (isCopyable(components)) { + ComponentConfigDialog.disposeDialog(); + + List copiedComponents = copyComponentsMaintainParent(components); + OpenRocketClipboard.setClipboard(copiedComponents); + copiedComponents = new LinkedList<>(OpenRocketClipboard.getClipboardComponents()); + + List duplicateComponents = new LinkedList<>(); + for (RocketComponent component : copiedComponents) { + duplicateComponents.add(component.copy()); + } + + List> positions = new LinkedList<>(); + for (RocketComponent component : duplicateComponents) { + positions.add(getPastePosition(component)); + } + + if (duplicateComponents.size() == 1) { + document.addUndoPosition("Duplicate " + duplicateComponents.get(0).getComponentName()); + } else { + document.addUndoPosition("Duplicate components"); + } + + for (int i = 0; i < duplicateComponents.size(); i++) { + positions.get(i).getU().addChild(duplicateComponents.get(i), positions.get(i).getV()); + } + + selectionModel.setSelectedComponents(duplicateComponents); + + parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + } else if (sims != null && sims.length > 0) { + ArrayList copySims = new ArrayList(); + + // TODO: the undoing doesn't do anything... + if (sims.length == 1) { + document.addUndoPosition("Duplicate " + sims[0].getName()); + } else { + document.addUndoPosition("Duplicate simulations"); + } + + for (Simulation s: sims) { + Simulation copy = s.duplicateSimulation(rocket); + String name = copy.getName(); + if (name.matches(OpenRocketDocument.SIMULATION_NAME_PREFIX + "[0-9]+ *")) { + copy.setName(document.getNextSimulationName()); + } + document.addSimulation(copy); + copySims.add(copy); + } + + selectionModel.setSelectedSimulations(copySims.toArray(new Simulation[0])); + + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } + } + + @Override + public void clipboardChanged() { + this.setEnabled(isCopyable(selectionModel.getSelectedComponent()) || + isSimulationSelected()); + } + + } @@ -780,33 +884,39 @@ public class RocketActions { public EditAction() { //// Edit this.putValue(NAME, trans.get("RocketActions.EditAct.Edit")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_E); + this.putValue(ACCELERATOR_KEY, EDIT_KEY_STROKE); //// Edit the selected component. this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.EditAct.ttip.Edit")); + this.putValue(SMALL_ICON, Icons.EDIT_EDIT); clipboardChanged(); } @Override public void actionPerformed(ActionEvent e) { List components = selectionModel.getSelectedComponents(); - if (!checkAllClassesEqual(components)) - return; + Simulation[] sims = selectionModel.getSelectedSimulations(); - // Do nothing if the config dialog is already visible - if (ComponentConfigDialog.isDialogVisible()) - return; + if ((components != null) && (components.size() > 0) && checkAllClassesEqual(components)) { + // Do nothing if the config dialog is already visible + if (ComponentConfigDialog.isDialogVisible()) + return; - List listeners = null; - if (components.size() > 1) { - listeners = components.subList(1, components.size()); + List listeners = null; + if (components.size() > 1) { + listeners = components.subList(1, components.size()); + } + ComponentConfigDialog.showDialog(parentFrame, document, components.get(0), listeners); + } else if (sims != null && sims.length > 0 && (simulationPanel != null)) { + simulationPanel.editSimulation(); } - ComponentConfigDialog.showDialog(parentFrame, document, components.get(0), listeners); } @Override public void clipboardChanged() { List components = selectionModel.getSelectedComponents(); - this.setEnabled(checkAllClassesEqual(components)); + this.setEnabled(checkAllClassesEqual(components) || isSimulationSelected()); } /** @@ -828,7 +938,33 @@ public class RocketActions { } } + /** + * Action to scale the currently selected component. + */ + private class ScaleAction extends RocketAction { + private static final long serialVersionUID = 1L; + public ScaleAction() { + //// Scale + this.putValue(NAME, trans.get("RocketActions.ScaleAct.Scale")); + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.ScaleAct.ttip.Scale")); + this.putValue(SMALL_ICON, Icons.EDIT_SCALE); + clipboardChanged(); + } + + @Override + public void actionPerformed(ActionEvent e) { + log.info(Markers.USER_MARKER, "Scale... selected"); + ScaleDialog dialog = new ScaleDialog(document, selectionModel.getSelectedComponents(), null); + dialog.setVisible(true); + dialog.dispose(); + } + + @Override + public void clipboardChanged() { + this.setEnabled(true); + } + } diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 05c7b04ff..f8ac84ec5 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -6,8 +6,6 @@ import java.awt.Component; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.FlavorEvent; -import java.awt.datatransfer.FlavorListener; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; @@ -21,12 +19,10 @@ import java.util.Arrays; import java.util.Comparator; import javax.swing.AbstractAction; -import javax.swing.Action; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; -import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; @@ -38,7 +34,6 @@ import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.text.DefaultEditorKit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -142,17 +137,7 @@ public class SimulationPanel extends JPanel { editButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int[] selection = simulationTable.getSelectedRows(); - if (selection.length == 0) { - return; - } - - Simulation[] sims = new Simulation[selection.length]; - for (int i = 0; i < selection.length; i++) { - selection[i] = simulationTable.convertRowIndexToModel(selection[i]); - sims[i] = document.getSimulation(selection[i]); - } - openDialog(false, sims); + editSimulation(); } }); this.add(editButton, "gapright para"); @@ -164,21 +149,7 @@ public class SimulationPanel extends JPanel { runButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int[] selection = simulationTable.getSelectedRows(); - if (selection.length == 0) { - return; - } - Simulation[] sims = new Simulation[selection.length]; - for (int i = 0; i < selection.length; i++) { - selection[i] = simulationTable.convertRowIndexToModel(selection[i]); - sims[i] = document.getSimulation(selection[i]); - } - - long t = System.currentTimeMillis(); - new SimulationRunDialog(SwingUtilities.getWindowAncestor( - SimulationPanel.this), document, sims).setVisible(true); - log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); - fireMaintainSelection(); + runSimulation(); } }); this.add(runButton, "gapright para"); @@ -190,50 +161,7 @@ public class SimulationPanel extends JPanel { deleteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int[] selection = simulationTable.getSelectedRows(); - if (selection.length == 0) { - return; - } - // Verify deletion - boolean verify = Application.getPreferences().getBoolean(Preferences.CONFIRM_DELETE_SIMULATION, true); - if (verify) { - - JPanel panel = new JPanel(new MigLayout()); - //// Do not ask me again - JCheckBox dontAsk = new JCheckBox(trans.get("simpanel.checkbox.donotask")); - panel.add(dontAsk, "wrap"); - //// You can change the default operation in the preferences. - panel.add(new StyledLabel(trans.get("simpanel.lbl.defpref"), -2)); - - int ret = JOptionPane.showConfirmDialog(SimulationPanel.this, - new Object[] { - //// Delete the selected simulations? - trans.get("simpanel.dlg.lbl.DeleteSim1"), - //// This operation cannot be undone. - trans.get("simpanel.dlg.lbl.DeleteSim2"), - "", - panel }, - //// Delete simulations - trans.get("simpanel.dlg.lbl.DeleteSim3"), - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE); - if (ret != JOptionPane.OK_OPTION) - return; - - if (dontAsk.isSelected()) { - Application.getPreferences().putBoolean(Preferences.CONFIRM_DELETE_SIMULATION, false); - } - } - - // Delete simulations - for (int i = 0; i < selection.length; i++) { - selection[i] = simulationTable.convertRowIndexToModel(selection[i]); - } - Arrays.sort(selection); - for (int i = selection.length - 1; i >= 0; i--) { - document.removeSimulation(selection[i]); - } - simulationTableModel.fireTableDataChanged(); + deleteSimulation(); } }); this.add(deleteButton, "gapright para"); @@ -244,26 +172,7 @@ public class SimulationPanel extends JPanel { plotButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int selected = simulationTable.getSelectedRow(); - if (selected < 0) { - return; - } - selected = simulationTable.convertRowIndexToModel(selected); - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(selected, selected); - - - Simulation sim = document.getSimulations().get(selected); - - if (!sim.hasSimulationData()) { - new SimulationRunDialog(SwingUtilities.getWindowAncestor( - SimulationPanel.this), document, sim).setVisible(true); - } - - fireMaintainSelection(); - - openDialog(true, sim); - + plotSimulation(); } }); this.add(plotButton, "wrap para"); @@ -542,30 +451,45 @@ public class SimulationPanel extends JPanel { simulationTableModel.setColumnWidths(simulationTable.getColumnModel()); pm = new JPopupMenu(); - pm.add(new CopyAction(simulationTable)); + pm.add(new EditSimulationAction()); + pm.add(new DuplicateSimulationAction()); + pm.add(new DeleteSimulationAction()); + pm.addSeparator(); + pm.add(new RunSimulationAction()); + pm.add(new PlotSimulationAction()); + // Mouse listener to act on double-clicks simulationTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { + int selectedRow = simulationTable.getSelectedRow(); + if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { - int selectedRow = simulationTable.getSelectedRow(); - if (selectedRow < 0) { - return; - } int selected = simulationTable.convertRowIndexToModel(selectedRow); int column = simulationTable.columnAtPoint(e.getPoint()); if (column == 0) { SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected)); } else { - simulationTable.clearSelection(); simulationTable.addRowSelectionInterval(selectedRow, selectedRow); openDialog(document.getSimulations().get(selected)); } - } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1){ + } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { + // Get the row that the right-click action happened on + int r = simulationTable.rowAtPoint(e.getPoint()); + + // Select new row + if (!simulationTable.isRowSelected(r)) { + if (r >= 0 && r < simulationTable.getRowCount()) { + simulationTable.setRowSelectionInterval(r, r); + } else { + return; + } + } + doPopup(e); } } @@ -608,6 +532,159 @@ public class SimulationPanel extends JPanel { updateButtonStates(); } + private void plotSimulation() { + int selected = simulationTable.getSelectedRow(); + if (selected < 0) { + return; + } + selected = simulationTable.convertRowIndexToModel(selected); + simulationTable.clearSelection(); + simulationTable.addRowSelectionInterval(selected, selected); + + + Simulation sim = document.getSimulations().get(selected); + + if (!sim.hasSimulationData()) { + new SimulationRunDialog(SwingUtilities.getWindowAncestor( + SimulationPanel.this), document, sim).setVisible(true); + } + + fireMaintainSelection(); + + openDialog(true, sim); + } + + private void deleteSimulation() { + int[] selection = simulationTable.getSelectedRows(); + if (selection.length == 0) { + return; + } + // Verify deletion + boolean verify = Application.getPreferences().getBoolean(Preferences.CONFIRM_DELETE_SIMULATION, true); + if (verify) { + + JPanel panel = new JPanel(new MigLayout()); + //// Do not ask me again + JCheckBox dontAsk = new JCheckBox(trans.get("simpanel.checkbox.donotask")); + panel.add(dontAsk, "wrap"); + //// You can change the default operation in the preferences. + panel.add(new StyledLabel(trans.get("simpanel.lbl.defpref"), -2)); + + int ret = JOptionPane.showConfirmDialog(SimulationPanel.this, + new Object[] { + //// Delete the selected simulations? + trans.get("simpanel.dlg.lbl.DeleteSim1"), + //// This operation cannot be undone. + trans.get("simpanel.dlg.lbl.DeleteSim2"), + "", + panel }, + //// Delete simulations + trans.get("simpanel.dlg.lbl.DeleteSim3"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE); + if (ret != JOptionPane.OK_OPTION) + return; + + if (dontAsk.isSelected()) { + Application.getPreferences().putBoolean(Preferences.CONFIRM_DELETE_SIMULATION, false); + } + } + + // Delete simulations + for (int i = 0; i < selection.length; i++) { + selection[i] = simulationTable.convertRowIndexToModel(selection[i]); + } + Arrays.sort(selection); + for (int i = selection.length - 1; i >= 0; i--) { + document.removeSimulation(selection[i]); + } + simulationTableModel.fireTableDataChanged(); + } + + private void runSimulation() { + Simulation[] sims = getSelectedSimulations(); + if (sims == null) return; + + long t = System.currentTimeMillis(); + new SimulationRunDialog(SwingUtilities.getWindowAncestor( + SimulationPanel.this), document, sims).setVisible(true); + log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); + fireMaintainSelection(); + } + + public void editSimulation() { + Simulation[] sims = getSelectedSimulations(); + if (sims == null) return; + + openDialog(false, sims); + } + + private Simulation[] getSelectedSimulations() { + int[] selection = simulationTable.getSelectedRows(); + if (selection.length == 0) { + return null; + } + + Simulation[] sims = new Simulation[selection.length]; + for (int i = 0; i < selection.length; i++) { + selection[i] = simulationTable.convertRowIndexToModel(selection[i]); + sims[i] = document.getSimulation(selection[i]); + } + return sims; + } + + + private void copySimulationAction() { + int numCols=simulationTable.getColumnCount(); + int numRows=simulationTable.getSelectedRowCount(); + int[] rowsSelected=simulationTable.getSelectedRows(); + + if (numRows!=rowsSelected[rowsSelected.length-1]-rowsSelected[0]+1 || numRows!=rowsSelected.length) { + + JOptionPane.showMessageDialog(null, "Invalid Copy Selection", "Invalid Copy Selection", JOptionPane.ERROR_MESSAGE); + return; + } + + StringBuilder excelStr =new StringBuilder(); + for (int k = 1; k < numCols; k++) { + excelStr.append(simulationTable.getColumnName(k)); + if (k < numCols-1) { + excelStr.append("\t"); + } + } + excelStr.append("\n"); + for (int i = 0; i < numRows; i++) { + for (int j = 1; j < numCols; j++) { + excelStr.append(simulationTable.getValueAt(rowsSelected[i], j)); + if (j < numCols-1) { + excelStr.append("\t"); + } + } + excelStr.append("\n"); + } + + StringSelection sel = new StringSelection(excelStr.toString()); + + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + cb.setContents(sel, sel); + } + + private void duplicateSimulation() { + Simulation[] sims = getSelectedSimulations(); + if (sims == null) return; + + for (Simulation s: sims) { + Simulation copy = s.duplicateSimulation(document.getRocket()); + String name = copy.getName(); + if (name.matches(OpenRocketDocument.SIMULATION_NAME_PREFIX + "[0-9]+ *")) { + copy.setName(document.getNextSimulationName()); + } + document.addSimulation(copy); + } + simulationTable.getSelectionModel().setSelectionInterval(simulationTable.getRowCount()-sims.length,simulationTable.getRowCount()-1); + + } + protected void doPopup(MouseEvent e) { pm.show(e.getComponent(), e.getX(), e.getY()); } @@ -700,53 +777,71 @@ public class SimulationPanel extends JPanel { simulationTable.addRowSelectionInterval(row, row); } } - - class CopyAction extends AbstractAction { - private JTable table; + class EditSimulationAction extends AbstractAction { + public EditSimulationAction() { + putValue(NAME, trans.get("simpanel.pop.edit")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_E); + this.putValue(ACCELERATOR_KEY, RocketActions.EDIT_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_EDIT); + } - public CopyAction(JTable table) { - this.table = table; - putValue(NAME, "Copy"); + @Override + public void actionPerformed(ActionEvent e) { + editSimulation(); + } + } + + class RunSimulationAction extends AbstractAction { + public RunSimulationAction() { + putValue(NAME, trans.get("simpanel.pop.run")); + putValue(SMALL_ICON, Icons.SIM_RUN); + } + + @Override + public void actionPerformed(ActionEvent e) { + runSimulation(); + } + } + + class DeleteSimulationAction extends AbstractAction { + public DeleteSimulationAction() { + putValue(NAME, trans.get("simpanel.pop.delete")); + putValue(MNEMONIC_KEY, KeyEvent.VK_D); + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); + putValue(SMALL_ICON, Icons.EDIT_DELETE); + } + + @Override + public void actionPerformed(ActionEvent e) { + deleteSimulation(); + } + } + + class PlotSimulationAction extends AbstractAction { + public PlotSimulationAction() { + putValue(NAME, trans.get("simpanel.pop.plot")); + putValue(SMALL_ICON, Icons.SIM_PLOT); + } + + @Override + public void actionPerformed(ActionEvent e) { + plotSimulation(); + } + } + + class DuplicateSimulationAction extends AbstractAction { + public DuplicateSimulationAction() { + putValue(NAME, trans.get("simpanel.pop.duplicate")); + putValue(MNEMONIC_KEY, KeyEvent.VK_D); + putValue(ACCELERATOR_KEY, RocketActions.DUPLICATE_KEY_STROKE); + putValue(SMALL_ICON, Icons.EDIT_DUPLICATE); } @Override public void actionPerformed(ActionEvent e) { - - int numCols=table.getColumnCount(); - int numRows=table.getSelectedRowCount(); - int[] rowsSelected=table.getSelectedRows(); - - if (numRows!=rowsSelected[rowsSelected.length-1]-rowsSelected[0]+1 || numRows!=rowsSelected.length ) { - - JOptionPane.showMessageDialog(null, "Invalid Copy Selection", "Invalid Copy Selection", JOptionPane.ERROR_MESSAGE); - return; - } - - StringBuffer excelStr=new StringBuffer(); - for (int k=1; k newConfigs = new LinkedHashMap<>(); - // create or copy configuration - if (copy) { + // create or duplicate configuration + if (duplicate) { List oldIds = getSelectedConfigurationIds(); if (oldIds == null || oldIds.size() == 0) return; @@ -217,7 +208,7 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe rocket.setSelectedConfiguration((FlightConfigurationId) newConfigs.keySet().toArray()[0]); } - private void renameConfiguration() { + private void renameConfigurationAction() { List fcIds = getSelectedConfigurationIds(); if (fcIds == null) return; FlightConfigurationId initFcId = fcIds.get(0); @@ -226,9 +217,10 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe for (int i = 1; i < fcIds.size(); i++) { rocket.getFlightConfiguration(fcIds.get(i)).setName(newName); } + configurationChanged(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - private void removeConfiguration() { + private void removeConfigurationAction() { List fcIds = getSelectedConfigurationIds(); if (fcIds == null || fcIds.size() == 0) return; @@ -239,7 +231,27 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe configurationChanged(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - + + public void doPopupConfig(MouseEvent e) { + popupMenuConfig.show(e.getComponent(), e.getX(), e.getY()); + } + + public AbstractAction getNewConfigAction() { + return newConfigAction; + } + + public AbstractAction getRenameConfigAction() { + return renameConfigAction; + } + + public AbstractAction getRemoveConfigAction() { + return removeConfigAction; + } + + public AbstractAction getDuplicateConfigAction() { + return duplicateConfigAction; + } + private void configurationChanged(int cce) { motorConfigurationPanel.fireTableDataChanged(cce); recoveryConfigurationPanel.fireTableDataChanged(cce); @@ -248,10 +260,10 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe private void updateButtonState() { FlightConfigurationId currentId = rocket.getSelectedConfiguration().getFlightConfigurationID(); - // Enable the remove/rename/copy buttons only when a configuration is selected. + // Enable the remove/rename/duplicate buttons only when a configuration is selected. removeConfButton.setEnabled(currentId.isValid()); renameConfButton.setEnabled(currentId.isValid()); - copyConfButton.setEnabled(currentId.isValid()); + duplicateConfButton.setEnabled(currentId.isValid()); // Count the number of motor mounts int motorMountCount = rocket.accept(new ListMotorMounts()).size(); @@ -310,4 +322,48 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe recoveryConfigurationPanel.synchronizeConfigurationSelection(); separationConfigurationPanel.synchronizeConfigurationSelection(); } + + private class NewConfigAction extends AbstractAction { + public NewConfigAction() { + putValue(NAME, trans.get("edtmotorconfdlg.but.Newconfiguration")); + } + + @Override + public void actionPerformed(ActionEvent e) { + newOrDuplicateConfigAction(false); + } + } + + private class RenameConfigAction extends AbstractAction { + public RenameConfigAction() { + putValue(NAME, trans.get("edtmotorconfdlg.but.Renameconfiguration")); + } + + @Override + public void actionPerformed(ActionEvent e) { + renameConfigurationAction(); + } + } + + private class RemoveConfigAction extends AbstractAction { + public RemoveConfigAction() { + putValue(NAME, trans.get("edtmotorconfdlg.but.Removeconfiguration")); + } + + @Override + public void actionPerformed(ActionEvent e) { + removeConfigurationAction(); + } + } + + private class DuplicateConfigAction extends AbstractAction { + public DuplicateConfigAction() { + putValue(NAME, trans.get("edtmotorconfdlg.but.Duplicateconfiguration")); + } + + @Override + public void actionPerformed(ActionEvent e) { + newOrDuplicateConfigAction(true); + } + } } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java index 0cac6bca9..f2e6a3f27 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java @@ -3,7 +3,6 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.CardLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; @@ -19,6 +18,7 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.KeyStroke; @@ -62,6 +62,9 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel private final MotorChooserDialog motorChooserDialog; protected FlightConfigurableTableModel configurationTableModel; + private final JPopupMenu popupMenuFull; // popup menu containing all the options + + MotorConfigurationPanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { super(flightConfigurationPanel, rocket); @@ -82,6 +85,27 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel cards = new JPanel(new CardLayout()); this.add(cards, "pushy"); + // Get all the actions + AbstractAction selectMotorAction = new SelectMotorAction(); + AbstractAction removeMotorAction = new RemoveMotorAction(); + AbstractAction selectIgnitionAction = new SelectIgnitionAction(); + AbstractAction resetIgnitionAction = new ResetIgnitionAction(); + AbstractAction renameConfigAction = flightConfigurationPanel.getRenameConfigAction(); + AbstractAction removeConfigAction = flightConfigurationPanel.getRemoveConfigAction(); + AbstractAction duplicateConfigAction = flightConfigurationPanel.getDuplicateConfigAction(); + + // Populate the popup menu + popupMenuFull = new JPopupMenu(); + popupMenuFull.add(selectMotorAction); + popupMenuFull.add(removeMotorAction); + popupMenuFull.addSeparator(); + popupMenuFull.add(selectIgnitionAction); + popupMenuFull.add(resetIgnitionAction); + popupMenuFull.addSeparator(); + popupMenuFull.add(renameConfigAction); + popupMenuFull.add(removeConfigAction); + popupMenuFull.add(duplicateConfigAction); + JLabel helpText = new JLabel(trans.get("MotorConfigurationPanel.lbl.nomotors")); cards.add(helpText, HELP_LABEL ); @@ -93,43 +117,19 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel configurationPanel.add(scroll, "spanx, grow, pushy, wrap"); //// Select motor - selectMotorButton = new SelectColorButton(trans.get("MotorConfigurationPanel.btn.selectMotor")); - selectMotorButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - selectMotor(); - } - }); + selectMotorButton = new SelectColorButton(selectMotorAction); configurationPanel.add(selectMotorButton, "split, align right, sizegroup button"); //// Remove motor button - removeMotorButton = new SelectColorButton(trans.get("MotorConfigurationPanel.btn.removeMotor")); - removeMotorButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - removeMotor(); - } - }); + removeMotorButton = new SelectColorButton(removeMotorAction); configurationPanel.add(removeMotorButton, "sizegroup button"); //// Select Ignition button - selectIgnitionButton = new SelectColorButton(trans.get("MotorConfigurationPanel.btn.selectIgnition")); - selectIgnitionButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - selectIgnition(); - } - }); - configurationPanel.add(selectIgnitionButton, "sizegroup button"); + selectIgnitionButton = new SelectColorButton(selectIgnitionAction); + configurationPanel.add(selectIgnitionButton, "sizegroup button, gapleft para"); //// Reset Ignition button - resetIgnitionButton = new SelectColorButton(trans.get("MotorConfigurationPanel.btn.resetIgnition")); - resetIgnitionButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - resetIgnition(); - } - }); + resetIgnitionButton = new SelectColorButton(resetIgnitionAction); configurationPanel.add(resetIgnitionButton, "sizegroup button, wrap"); cards.add(configurationPanel, TABLE_LABEL ); @@ -194,15 +194,55 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel configurationTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); configurationTable.setDefaultRenderer(Object.class, new MotorTableCellRenderer()); + ListSelectionListener listener = new ListSelectionListener() { + private int previousRow = -1; + private int previousColumn = -1; + + @Override + public void valueChanged(ListSelectionEvent event) { + if (table != null && (configurationTable.getSelectedRow() != previousRow || + configurationTable.getSelectedColumn() != previousColumn)) { + updateButtonState(); + previousRow = configurationTable.getSelectedRow(); + previousColumn = configurationTable.getSelectedColumn(); + } + } + }; + + configurationTable.getSelectionModel().addListSelectionListener(listener); + configurationTable.getColumnModel().getSelectionModel().addListSelectionListener(listener); + configurationTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - updateButtonState(); int selectedColumn = table.getSelectedColumn(); - if (e.getClickCount() == 2) { + + if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { if (selectedColumn > 0) { selectMotor(); } + } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { + // Get the row and column of the selected cell + int r = configurationTable.rowAtPoint(e.getPoint()); + int c = configurationTable.columnAtPoint(e.getPoint()); + + // Select new cell + if (!configurationTable.isCellSelected(r, c)) { + if (r >= 0 && r < configurationTable.getRowCount() && + c >= 0 && c < configurationTable.getColumnCount()) { + configurationTable.setRowSelectionInterval(r, r); + configurationTable.setColumnSelectionInterval(c, c); + } else { + configurationTable.clearSelection(); + return; + } + } + + if (c > 0) { + doPopupFull(e); + } else { + flightConfigurationPanel.doPopupConfig(e); + } } } }); @@ -234,6 +274,10 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel }); } + private void doPopupFull(MouseEvent e) { + popupMenuFull.show(e.getComponent(), e.getX(), e.getY()); + } + public void updateComponentSelection(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; @@ -408,6 +452,50 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel } + private class SelectMotorAction extends AbstractAction { + public SelectMotorAction() { + putValue(NAME, trans.get("MotorConfigurationPanel.btn.selectMotor")); + } + + @Override + public void actionPerformed(ActionEvent e) { + selectMotor(); + } + } + + private class RemoveMotorAction extends AbstractAction { + public RemoveMotorAction() { + putValue(NAME, trans.get("MotorConfigurationPanel.btn.removeMotor")); + } + + @Override + public void actionPerformed(ActionEvent e) { + removeMotor(); + } + } + + private class SelectIgnitionAction extends AbstractAction { + public SelectIgnitionAction() { + putValue(NAME, trans.get("MotorConfigurationPanel.btn.selectIgnition")); + } + + @Override + public void actionPerformed(ActionEvent e) { + selectIgnition(); + } + } + + private class ResetIgnitionAction extends AbstractAction { + public ResetIgnitionAction() { + putValue(NAME, trans.get("MotorConfigurationPanel.btn.resetIgnition")); + } + + @Override + public void actionPerformed(ActionEvent e) { + resetIgnition(); + } + } + private class MotorTableCellRenderer extends FlightConfigurablePanel.FlightConfigurableCellRenderer { @Override diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index dceb85424..fb174692a 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -1,7 +1,6 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -12,11 +11,14 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.dialogs.flightconfiguration.DeploymentSelectionDialog; @@ -35,6 +37,7 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel recoveryTableModel; private final JButton selectDeploymentButton; private final JButton resetDeploymentButton; + private final JPopupMenu popupMenuFull; // popup menu containing all the options RecoveryConfigurationPanel(FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { @@ -43,26 +46,30 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel 0) { + selectDeployment(); + } + } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { + // Get the row and column of the selected cell + int r = recoveryTable.rowAtPoint(e.getPoint()); + int c = recoveryTable.columnAtPoint(e.getPoint()); + + // Select new cell + if (!recoveryTable.isCellSelected(r, c)) { + if (r >= 0 && r < recoveryTable.getRowCount() && + c >= 0 && c < recoveryTable.getColumnCount()) { + recoveryTable.setRowSelectionInterval(r, r); + recoveryTable.setColumnSelectionInterval(c, c); + } else { + recoveryTable.clearSelection(); + return; + } + } + + if (c > 0) { + doPopupFull(e); + } else { + flightConfigurationPanel.doPopupConfig(e); + } } } }); + rocket.addComponentChangeListener(cce -> { final RocketComponent source = cce.getSource(); if(source instanceof FlightConfigurableComponent) { @@ -185,6 +235,10 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel separationTableModel; private final JButton selectSeparationButton; private final JButton resetDeploymentButton; - - + private final JPopupMenu popupMenuFull; // popup menu containing all the options + + SeparationConfigurationPanel(FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { super(flightConfigurationPanel,rocket); JScrollPane scroll = new JScrollPane(table); this.add(scroll, "span, grow, pushy, wrap"); + + // Get all the actions + AbstractAction selectSeparationAction = new SelectSeparationAction(); + AbstractAction resetSeparationAction = new ResetSeparationAction(); + AbstractAction renameConfigAction = flightConfigurationPanel.getRenameConfigAction(); + AbstractAction removeConfigAction = flightConfigurationPanel.getRemoveConfigAction(); + AbstractAction duplicateConfigAction = flightConfigurationPanel.getDuplicateConfigAction(); + + // Populate the popup menu + popupMenuFull = new JPopupMenu(); + popupMenuFull.add(selectSeparationAction); + popupMenuFull.add(resetSeparationAction); + popupMenuFull.addSeparator(); + popupMenuFull.add(renameConfigAction); + popupMenuFull.add(removeConfigAction); + popupMenuFull.add(duplicateConfigAction); - //// Select deployment - selectSeparationButton = new SelectColorButton(trans.get("edtmotorconfdlg.but.Selectseparation")); + //// Select separation + selectSeparationButton = new SelectColorButton(selectSeparationAction); selectSeparationButton.setEnabled(false); - selectSeparationButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - selectSeparation(); - } - }); this.add(selectSeparationButton, "split, align right, sizegroup button"); - //// Reset deployment - resetDeploymentButton = new SelectColorButton(trans.get("edtmotorconfdlg.but.Resetseparation")); + //// Reset separation + resetDeploymentButton = new SelectColorButton(resetSeparationAction); resetDeploymentButton.setEnabled(false); - resetDeploymentButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - resetSeparation(); - } - }); this.add(resetDeploymentButton, "sizegroup button, wrap"); // Set 'Enter' key action to open the separation selection dialog @@ -104,16 +111,60 @@ public class SeparationConfigurationPanel extends FlightConfigurablePanel 0) { + selectSeparation(); + } + } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { + // Get the row and column of the selected cell + int r = separationTable.rowAtPoint(e.getPoint()); + int c = separationTable.columnAtPoint(e.getPoint()); + + // Select new cell + if (!separationTable.isCellSelected(r, c)) { + if (r >= 0 && r < separationTable.getRowCount() && + c >= 0 && c < separationTable.getColumnCount()) { + separationTable.setRowSelectionInterval(r, r); + separationTable.setColumnSelectionInterval(c, c); + } else { + separationTable.clearSelection(); + return; + } + } + + if (c > 0) { + doPopupFull(e); + } else { + flightConfigurationPanel.doPopupConfig(e); + } } } }); + separationTable.setDefaultRenderer(Object.class, new SeparationTableCellRenderer()); return separationTable; @@ -192,6 +243,11 @@ public class SeparationConfigurationPanel extends FlightConfigurablePanel selectedComponents = Arrays.stream(selectionModel.getSelectionPaths()) + .map(c -> (RocketComponent) c.getLastPathComponent()).collect(Collectors.toList()); + + boolean newClick = true; + for (RocketComponent component : clicked) { + if (selectedComponents.contains(component)) { + newClick = false; + break; + } + } + + if (newClick) { + for (RocketComponent rocketComponent : clicked) { + if (!selectedComponents.contains(rocketComponent)) { + TreePath path = ComponentTreeModel.makeTreePath(rocketComponent); + selectionModel.setSelectionPath(path); + } + } + } + + basicFrame.doComponentTreePopup(event); + } + } + + private void handleComponentClick(RocketComponent[] clicked, MouseEvent event) { List selectedComponents = Arrays.stream(selectionModel.getSelectionPaths()) .map(c -> (RocketComponent) c.getLastPathComponent()).collect(Collectors.toList()); diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java index be095f1c1..5478bff00 100644 --- a/swing/src/net/sf/openrocket/gui/util/Icons.java +++ b/swing/src/net/sf/openrocket/gui/util/Icons.java @@ -58,11 +58,16 @@ public class Icons { public static final Icon FILE_QUIT = loadImageIcon("pix/icons/application-exit.png", "Quit OpenRocket"); public static final Icon EDIT_UNDO = loadImageIcon("pix/icons/edit-undo.png", trans.get("Icons.Undo")); public static final Icon EDIT_REDO = loadImageIcon("pix/icons/edit-redo.png", trans.get("Icons.Redo")); + public static final Icon EDIT_EDIT = loadImageIcon("pix/icons/edit-edit.png", "Edit"); public static final Icon EDIT_CUT = loadImageIcon("pix/icons/edit-cut.png", "Cut"); public static final Icon EDIT_COPY = loadImageIcon("pix/icons/edit-copy.png", "Copy"); public static final Icon EDIT_PASTE = loadImageIcon("pix/icons/edit-paste.png", "Paste"); + public static final Icon EDIT_DUPLICATE = loadImageIcon("pix/icons/edit-duplicate.png", "Duplicate"); public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete"); public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale"); + + public static final Icon SIM_RUN = loadImageIcon("pix/icons/sim-run.png", "Run"); + public static final Icon SIM_PLOT = loadImageIcon("pix/icons/sim-plot.png", "Plot"); public static final Icon HELP_ABOUT = loadImageIcon("pix/icons/help-about.png", "About"); public static final Icon HELP_LICENSE = loadImageIcon("pix/icons/help-license.png", "License");