diff --git a/README.md b/README.md index cbfb71713b..726d5c3504 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ OpenRocket needs help to become even better. Implementing features, writing docu - Daniel Williams, pod support, maintainer - Joe Pfeiffer (maintainer) - Billy Olsen (maintainer) -- Sibo Van Gool (3D OBJ export, maintainer) +- Sibo Van Gool (RASAero file format, 3D OBJ export, dark theme, maintainer) - Neil Weinstock (tester, icons, forum support) - H. Craig Miller (tester) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index c490ad629a..711e7e1197 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -16,23 +16,18 @@ debug.currentFile = messages.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Do not ask me again -RocketActions.lbl.Youcanchangedefop = You can change the default operation in the preferences. -RocketActions.showConfirmDialog.lbl1 = Delete the selected simulations? -RocketActions.showConfirmDialog.lbl2 = This operation cannot be undone. -RocketActions.showConfirmDialog.title = Delete simulations RocketActions.DelCompAct.Delete = Delete -RocketActions.DelCompAct.ttip.Delete = Delete the selected component. +RocketActions.DelCompAct.ttip.Delete = Delete the selected components. RocketActions.DelSimuAct.Delete = Delete -RocketActions.DelSimuAct.ttip.Delete = Delete the selected simulation. +RocketActions.DelSimuAct.ttip.Delete = Delete the selected simulations. RocketActions.DelAct.Delete = Delete -RocketActions.DelAct.ttip.Delete = Delete the selected component or simulation. +RocketActions.DelAct.ttip.Delete = Delete the selected components or simulations. RocketActions.CutAction.Cut = Cut -RocketActions.CutAction.ttip.Cut = Cut this component or simulation to the clipboard and delete from this design +RocketActions.CutAction.ttip.Cut = Cut these components or simulations to the clipboard and delete from this design RocketActions.CopyAct.Copy = Copy -RocketActions.CopyAct.ttip.Copy = Copy this component (and subcomponents) to the clipboard. +RocketActions.CopyAct.ttip.Copy = Copy these components (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.PasteAct.ttip.Paste = Paste the components or simulations on the clipboard to the design. RocketActions.PasteAct.invalidPosition.msg = Invalid paste position for object '%s', ignoring pasting. RocketActions.PasteAct.invalidPosition.title = Could not paste RocketActions.DuplicateAct.Duplicate = Duplicate @@ -385,15 +380,15 @@ pref.dlg.PrefBooleanSelector2 = Confirm pref.dlg.Add = Add pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket. -PreferencesDialog.lbl.language = Interface language: -PreferencesDialog.languages.default = System default -PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket. PreferencesDialog.CancelOperation.title = Discard Preference Changes PreferencesDialog.CancelOperation.msg.discardChanges = Are you sure you want to discard the preference changes? generalprefs.lbl.language = Interface language generalprefs.languages.default = System default generalprefs.lbl.languageEffect = The language will change the next time you start OpenRocket. +generalprefs.lbl.UITheme = UI Theme +generalprefs.lbl.FontSize = UI Font Size +generalprefs.lbl.themeRestartOR = You must restart OpenRocket for the UI changes to take effect. generalprefs.ImportWarning.title = Reload OpenRocket generalprefs.ImportWarning.msg = You may need to restart OpenRocket for some of the changes to take effect. @@ -407,6 +402,10 @@ PreferencesOptionPanel.checkbox.userDirectories.ttip = If unchecked, user direct PreferencesOptionPanel.checkbox.windowInfo = Export window information (position, size\u2026) PreferencesOptionPanel.checkbox.windowInfo.ttip = If unchecked, window information (position, size\u2026) will not be exported. +! UI Themes +UITheme.Light = Light (Default) +UITheme.Dark = Dark + ! Welcome dialog welcome.dlg.title = Welcome to OpenRocket welcome.dlg.lbl.thankYou = Thank you for downloading OpenRocket @@ -581,12 +580,23 @@ 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.edit.ttip= Edit the selected simulation(s) +simpanel.pop.cut= Cut +simpanel.pop.cut.ttip= Copy the selected simulation(s) to the clipboard and delete from this design +simpanel.pop.copy= Copy +simpanel.pop.copy.ttip= Copy the selected simulation(s) to the clipboard +simpanel.pop.paste= Paste +simpanel.pop.paste.ttip= Paste the simulation(s) on the clipboard to the design. simpanel.pop.plot = Plot / Export +simpanel.pop.plot.ttip= Plot or Export the selected simulation(s) simpanel.pop.run = Run +simpanel.pop.run.ttip= Run the selected simulation(s) simpanel.pop.delete = Delete +simpanel.pop.delete.ttip= Delete the selected simulation(s) simpanel.pop.duplicate = Duplicate +simpanel.pop.duplicate.ttip= Duplicate the selected simulation(s) simpanel.pop.exportSimTableToCSV = Export simulation table as CSV file -simpanel.pop.exportSelectedSimsToCSV = Export simulations as CSV file +simpanel.pop.exportSelectedSimsToCSV = Export simulation(s) as CSV file simpanel.pop.exportToCSV.save.dialog.title = Save as CSV file simpanel.dlg.no.simulation.table.rows = Simulation table has no entries\u2026 Please run a simulation first. simpanel.checkbox.donotask = Do not ask me again @@ -594,6 +604,7 @@ simpanel.lbl.defpref = You can change the default operation in the preferences. simpanel.dlg.lbl.DeleteSim1 = Delete the selected simulations? simpanel.dlg.lbl.DeleteSim2 = This operation cannot be undone. simpanel.dlg.lbl.DeleteSim3 = Delete simulations +simpanel.col.Status = Status simpanel.col.Name = Name simpanel.col.Motors = Motors simpanel.col.Configuration = Configuration @@ -615,6 +626,7 @@ simpanel.ttip.notSimulated = Not simulated yet
Click Run simulat simpanel.ttip.noData = No simulation data available. simpanel.ttip.noWarnings = No warnings. simpanel.ttip.warnings = Warnings: +simpanel.msg.invalidCopySelection = Invalid copy selection ! SimulationRunDialog SimuRunDlg.title.RunSim = Running simulations\u2026 @@ -1192,6 +1204,7 @@ InnerTubeCfg.tab.ttip.Radialpos = Radial position InnerTubeCfg.lbl.Selectclustercfg = Select cluster configuration: InnerTubeCfg.lbl.TubeSep = Tube separation: InnerTubeCfg.lbl.ttip.TubeSep = The separation of the tubes, 1.0 = touching each other +InnerTubeCfg.lbl.ttip.TubeSepAbs = The separation of the tubes, 0 = touching each other InnerTubeCfg.lbl.Rotation = Rotation: InnerTubeCfg.lbl.ttip.Rotation = Rotation angle of the cluster configuration InnerTubeCfg.lbl.Rotangle = Rotation angle of the cluster configuration @@ -1200,6 +1213,10 @@ InnerTubeCfg.lbl.longA1 = Split the cluster into separate components.
InnerTubeCfg.lbl.longA2 = This also duplicates all components attached to this inner tube. InnerTubeCfg.but.Resetsettings = Reset settings InnerTubeCfg.but.ttip.Resetsettings = Reset the separation and rotation to the default values +InnerTubeCfg.radioBut.Relative = Relative +InnerTubeCfg.radioBut.Relative.ttip = The separation is measured relative to the outer diameter of the inner tube +InnerTubeCfg.radioBut.Absolute = Absolute +InnerTubeCfg.radioBut.Absolute.ttip = The separation is measured in length units ! LaunchLugConfig LaunchLugCfg.lbl.Length = Length: @@ -2025,6 +2042,7 @@ Warning.TUBE_SEPARATION = Space between tube fins may not simulate accurately. Warning.TUBE_OVERLAP = Overlapping tube fins may not simulate accurately. Warning.EMPTY_BRANCH = Simulation branch contains no data Warning.SEPARATION_ORDER = Stages separated in an unreasonable order +Warning.EARLY_SEPARATION = Stages separated before clearing launch rod/rail ! Scale dialog ScaleDialog.lbl.scaleRocket = Entire rocket @@ -2319,7 +2337,7 @@ ComponentPresetChooserDialog.menu.units = Units ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = Show Legacy Database ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog
Directly apply a preset by double-clicking it or by selecting it and closing this window. ComponentPresetChooserDialog.checkbox.alwaysOpenPreset = Always open this dialog when creating a new %s -table.column.Favorite = Favorite +table.column.Favorite = \u2026 Favorite table.column.Legacy = Legacy table.column.Manufacturer = Manufacturer table.column.PartNo = Part Number diff --git a/core/resources/l10n/messages_ar.properties b/core/resources/l10n/messages_ar.properties index 96b45e6ab8..5a957d4ccf 100644 --- a/core/resources/l10n/messages_ar.properties +++ b/core/resources/l10n/messages_ar.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_ar.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = لا تسألني مجددا -RocketActions.lbl.Youcanchangedefop = يمكنك تغيير العملية الافتراضية في التفضيلات. -RocketActions.showConfirmDialog.lbl1 = إحذف المحاكاة المختارة؟ -RocketActions.showConfirmDialog.lbl2 = لا يمكنك التراجع عن هذه العملية. -RocketActions.showConfirmDialog.title = إحذف كل المحاكاة RocketActions.DelCompAct.Delete = إحذف RocketActions.DelCompAct.ttip.Delete = إحذف القطعة المختارة RocketActions.DelSimuAct.Delete = إحذف @@ -348,9 +343,6 @@ pref.dlg.PrefBooleanSelector2 = تأكيد pref.dlg.Add = أضف pref.dlg.DescriptionArea.Adddirectories =.ملفات محرك روكسيم أو أرشيفات زيب مفصولة بفاصلة منقوطة لتحميل منحنيات الدفع الخارجية. ستدخل التغييرات حيز التنفيذ في المرة التالية التي تفتح فيها أوبنروكت (* .rse)أو RASP ملفات محرك (* .eng), أضف الدلائل -PreferencesDialog.lbl.language = :لغة الواجهة -PreferencesDialog.languages.default = النظام الافتراضي -PreferencesDialog.lbl.languageEffect = .ستتغير اللغة في المرة التالية التي تعيد تشغيل أوبنروكت generalprefs.lbl.language = :لغة الواجهة generalprefs.languages.default = النظام الافتراضي generalprefs.lbl.languageEffect = .ستتغير اللغة في المرة التالية التي تعيد تشغيل أوبنروكت diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index 08a09dffd0..a1b326463c 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_cs.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Pr\u0161te se me neptejte -RocketActions.lbl.Youcanchangedefop = Mu\u017Eete zmenit vchoz operaci v nastaven. -RocketActions.showConfirmDialog.lbl1 = Chcete smazat oznacenou simulaci? -RocketActions.showConfirmDialog.lbl2 = Tuto operaci nelze vzt zpet. -RocketActions.showConfirmDialog.title = Sma\u017E simulace RocketActions.DelCompAct.Delete = Sma\u017E RocketActions.DelCompAct.ttip.Delete = Sma\u017E oznacenou komponentu. RocketActions.DelSimuAct.Delete = Sma\u017E @@ -269,9 +264,9 @@ pref.dlg.PrefBooleanSelector2 = Potvrd pref.dlg.Add = Pridej pref.dlg.DescriptionArea.Adddirectories = Pridej adresre, soubory RASP motor (*.eng), RockSim engine soubory (*.rse) nebo ZIP archiv oddelen oddelovacem (;) k nahrn externch vkonovch prubehu. Zmeny se projev po restaru programu OpenRocket. -PreferencesDialog.lbl.language = Jazyk rohrann: -PreferencesDialog.languages.default = Vchoz -PreferencesDialog.lbl.languageEffect = Jazyk se zmen pri dal\u0161m spu\u0161ten programu OpenRocket. +generalprefs.lbl.language = Jazyk rohrann: +generalprefs.languages.default = Vchoz +generalprefs.lbl.languageEffect = Jazyk se zmen pri dal\u0161m spu\u0161ten programu OpenRocket. ! Software update checker update.dlg.error.title = Nemohu zskat informace o aktualizacch diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 0c68ebee2e..007a46da37 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_de.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Nicht wieder fragen -RocketActions.lbl.Youcanchangedefop = Die Standardaktion kann in den Einstellungen gendert werden. -RocketActions.showConfirmDialog.lbl1 = Simulationen lschen? -RocketActions.showConfirmDialog.lbl2 = Diese Aktion kann nicht rckgngig gemacht werden. -RocketActions.showConfirmDialog.title = Simulationen lschen RocketActions.DelCompAct.Delete = Lschen RocketActions.DelCompAct.ttip.Delete = Die ausgewhlte Komponente lschen. RocketActions.DelSimuAct.Delete = Lschen @@ -271,9 +266,9 @@ pref.dlg.PrefBooleanSelector2 = Best pref.dlg.Add = Hinzufgen pref.dlg.DescriptionArea.Adddirectories = Um eigene Schubkurven zu laden, Verzeichnisse, RASP-Motordateien (*.eng), RockSim-Motordateien (*.rse) oder ZIP-Archive mit Semikolon getrennt eingeben. nderungen werden beim nchsten Neustart von OpenRocket bernommen. -PreferencesDialog.lbl.language = Sprache: -PreferencesDialog.languages.default = Systemeinstellung -PreferencesDialog.lbl.languageEffect = Die Sprache wird beim nchsten Neustart von OpenRocket gendert. +generalprefs.lbl.language = Sprache: +generalprefs.languages.default = Systemeinstellung +generalprefs.lbl.languageEffect = Die Sprache wird beim nchsten Neustart von OpenRocket gendert. ! Software update checker update.dlg.error.title = Es konnten keine Informationen ber Programmaktualisierungen empfangen werden. diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 3d989be9bb..db5dc37d17 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -733,9 +733,9 @@ PlotDialog.title.Flightdataplot = Representaci\u00f3n de los datos de vuelo ComponentTreeRenderer.total = total -PreferencesDialog.languages.default = Idioma por defecto -PreferencesDialog.lbl.language = Idioma de la interfaz: -PreferencesDialog.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket. +generalprefs.languages.default = Idioma por defecto +generalprefs.lbl.language = Idioma de la interfaz: +generalprefs.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket. PresetModel.lbl.custompreset = Personalizado PresetModel.lbl.partsLib = Biblioteca de piezas @@ -811,12 +811,6 @@ RocketActions.NewStageAct.Newstage = Nueva etapa RocketActions.NewStageAct.ttip.Newstage = A\u00f1adir una nueva etapa al dise\u00f1o del cohete RocketActions.PasteAct.Paste = Pegar RocketActions.PasteAct.ttip.Paste = Pegar al portapapeles -! RocketActions -RocketActions.checkbox.Donotaskmeagain = No volver a preguntarme -RocketActions.lbl.Youcanchangedefop = Puede modificar la operaci\u00f3n por defecto con sus preferencias -RocketActions.showConfirmDialog.lbl1 = \u00bfBorrar las simulaciones seleccionadas? -RocketActions.showConfirmDialog.lbl2 = Esta operaci\u00f3n no puede deshacerse. -RocketActions.showConfirmDialog.title = Borrar simulaciones RocketCfg.lbl.Comments = Comentarios: RocketCfg.lbl.Designer = Dise\u00f1ador: diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index 0eff3be76b..fbbdd38e5d 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -725,9 +725,9 @@ PlotDialog.title.Flightdataplot = Trac\u00E9 du vol ComponentTreeRenderer.total = total -PreferencesDialog.languages.default = Valeur syst\u00E8me par d\u00E9faut -PreferencesDialog.lbl.language = Langue du programme: -PreferencesDialog.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket. +generalprefs.languages.default = Valeur syst\u00E8me par d\u00E9faut +generalprefs.lbl.language = Langue du programme: +generalprefs.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket. PresetModel.lbl.custompreset = Personnalis PresetModel.lbl.partsLib = Biblioth\u00E8que de pi\u00E8ces @@ -802,12 +802,6 @@ RocketActions.NewStageAct.Newstage = Nouvel \u00E9tage RocketActions.NewStageAct.ttip.Newstage = Ajouter un nouvel \u00E9tage au projet. RocketActions.PasteAct.Paste = Coller RocketActions.PasteAct.ttip.Paste = Coller la pi\u00E8ce ou simulation pr\u00E9sente dans le presse papier dans le projet. -! RocketActions -RocketActions.checkbox.Donotaskmeagain = Ne plus me demander -RocketActions.lbl.Youcanchangedefop = Vous pouvez changer le mode op\u00E9ratoire par d\u00E9faut dans les pr\u00E9ferences. -RocketActions.showConfirmDialog.lbl1 = Supprimer les simulations s\u00E9lectionn\u00E9es? -RocketActions.showConfirmDialog.lbl2 = Cette op\u00E9ration n'est pas r\u00E9versible. -RocketActions.showConfirmDialog.title = Effacer les simulations RocketCfg.lbl.Comments = Commentaires: RocketCfg.lbl.Designer = Concepteur: diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index 55c33cc870..58d24f634e 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_it.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Non chiedermelo piu' -RocketActions.lbl.Youcanchangedefop = Puoi cambiare le operazioni prestabilite in PREFERENZE. -RocketActions.showConfirmDialog.lbl1 = Cancello le simulazioni selezionate? -RocketActions.showConfirmDialog.lbl2 = Questa operazione non puo' essere annullata. -RocketActions.showConfirmDialog.title = Cancello Simulazioni RocketActions.DelCompAct.Delete = Cancella RocketActions.DelCompAct.ttip.Delete = Cancello il componente selezionato. RocketActions.DelSimuAct.Delete = Cancella @@ -273,9 +268,9 @@ pref.dlg.PrefBooleanSelector2 = Conferma pref.dlg.Add = Aggiungi pref.dlg.DescriptionArea.Adddirectories = Aggiungi cartelle, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separate da puntoevirgola (;) per caricare curve di spinta esterne. I cambiamenti avranno effetto la prossima volta che avvierai OpenRocket. -PreferencesDialog.lbl.language = Lingua dell'interfaccia: -PreferencesDialog.languages.default = Predefinita di sistema -PreferencesDialog.lbl.languageEffect = La lingua sara' cambiata la prossima volta che avvierai OpenRocket. +generalprefs.lbl.language = Lingua dell'interfaccia: +generalprefs.languages.default = Predefinita di sistema +generalprefs.lbl.languageEffect = La lingua sara' cambiata la prossima volta che avvierai OpenRocket. ! Software update checker update.dlg.error.title = Non sono in grado di recuperare informazioni sugli aggiornamenti diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index 947c2022a6..a593c4c626 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -12,11 +12,6 @@ debug.currentFile = messages_ja.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = \u6B21\u56DE\u304B\u3089\u8868\u793A\u3057\u306A\u3044 -RocketActions.lbl.Youcanchangedefop = \u8A2D\u5B9A\u306E\u4E2D\u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u5909\u66F4\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059 -RocketActions.showConfirmDialog.lbl1 = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u6D88\u53BB\u3057\u307E\u3059\u304B\uFF1F -RocketActions.showConfirmDialog.lbl2 = \u6D88\u53BB\u3057\u305F\u3089\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093 -RocketActions.showConfirmDialog.title = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 RocketActions.DelCompAct.Delete = \u524A\u9664 RocketActions.DelCompAct.ttip.Delete = \u9078\u629E\u3057\u305F\u90E8\u54C1\u306E\u524A\u9664 RocketActions.DelSimuAct.Delete = \u524A\u9664 @@ -270,9 +265,9 @@ pref.dlg.PrefBooleanSelector2 = \u78BA\u8A8D pref.dlg.Add = \u8FFD\u52A0 pref.dlg.DescriptionArea.Adddirectories = \u30D5\u30A9\u30EB\u30C0, RASP motor files (*.eng), RockSim engine files (*.rse) \u3082\u3057\u304F\u306F ZIP archives \u3092\u30BB\u30DF\u30B3\u30ED\u30F3(;)\u306B\u3088\u3063\u3066\u5206\u3051\u3089\u308C\u305F\u5F62\u3067\u8FFD\u52A0\u306E\u63A8\u529B\u5C65\u6B74\u3068\u3057\u3066\u8FFD\u52A0\u3067\u304D\u307E\u3059\u3002\u3053\u306E\u5909\u66F4\u306FOpenRocket\u306E\u518D\u8D77\u52D5\u6642\u306B\u6709\u52B9\u306B\u306A\u308A\u307E\u3059 -PreferencesDialog.lbl.language = \u8A00\u8A9E\uFF1A -PreferencesDialog.languages.default = \u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E -PreferencesDialog.lbl.languageEffect = \u8A00\u8A9E\u306F\u518D\u8D77\u52D5\u6642\u306B\u5909\u66F4\u3055\u308C\u307E\u3059 +generalprefs.lbl.language = \u8A00\u8A9E\uFF1A +generalprefs.languages.default = \u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E +generalprefs.lbl.languageEffect = \u8A00\u8A9E\u306F\u518D\u8D77\u52D5\u6642\u306B\u5909\u66F4\u3055\u308C\u307E\u3059 ! Software update checker update.dlg.error.title = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u60C5\u5831\u306E\u8AAD\u307F\u51FA\u3057\u304C\u3067\u304D\u307E\u305B\u3093 diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 8fb96b5397..9b9615c49c 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -18,11 +18,6 @@ debug.currentFile = messages_nl.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Niet opnieuw vragen -RocketActions.lbl.Youcanchangedefop = U kan de standaardbewerking veranderen in de voorkeuren. -RocketActions.showConfirmDialog.lbl1 = Geselecteerde simulatie verwijderen? -RocketActions.showConfirmDialog.lbl2 = Deze bewerking kan niet ongedaan worden. -RocketActions.showConfirmDialog.title = Verwijder simulaties RocketActions.DelCompAct.Delete = Verwijder RocketActions.DelCompAct.ttip.Delete = Verwijder het geselecteerde onderdeel. RocketActions.DelSimuAct.Delete = Verwijder @@ -350,10 +345,6 @@ pref.dlg.PrefBooleanSelector2 = Bevestig pref.dlg.Add = Voeg toe pref.dlg.DescriptionArea.Adddirectories = Voeg folders, RASP motorbestanden (*.eng), RockSim motorbestanden (*.rse) of ZIP-archieven gescheiden door een puntkomma (;) om externe stuwkrachtcurves te laden. Wijzigingen zullen van kracht gaan de volgende keer dat u OpenRocket start. -PreferencesDialog.lbl.language = Interface taal: -PreferencesDialog.languages.default = Systeemstandaard -PreferencesDialog.lbl.languageEffect = De taal zal veranderen de volgende keer dat u OpenRocket start. - generalprefs.lbl.language = Interface taal generalprefs.languages.default = Systeemstandaard generalprefs.lbl.languageEffect = De taal zal veranderen de volgende keer dat u OpenRocket start. diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index 55577b1054..69ba91cdbe 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_pl.properties ! RocketActions - RocketActions.checkbox.Donotaskmeagain = Nie pytaj ponownie - RocketActions.lbl.Youcanchangedefop = Domy\u015Bln\u0105 operacj\u0119 mo\u017Cna zmieni\u0107 w ustawieniach. - RocketActions.showConfirmDialog.lbl1 = Usun\u0105\u0107 zaznaczone symulacje? - RocketActions.showConfirmDialog.lbl2 = Tej operacji nie mo\u017Cna cofn\u0105\u0107. - RocketActions.showConfirmDialog.title = Usu\u0144 symulacje RocketActions.DelCompAct.Delete = Usu\u0144 RocketActions.DelCompAct.ttip.Delete = Usu\u0144 wybran\u0105 cz\u0119\u015B\u0107 RocketActions.DelSimuAct.Delete = Usu\u0144 @@ -270,10 +265,10 @@ pref.dlg.PrefBooleanSelector2 = Potwierd\u017A pref.dlg.Add = Dodaj pref.dlg.DescriptionArea.Adddirectories = Dodaj katalogi, pliki silnikowe RASP (*.eng), Pliki silnikowe RockSim (*.rse) albo archiwa ZIP rozdzielone \u015Brednikiem (;) by za\u0142adowa\u0107 zewn\u0119trzne krzywe si\u0142y ci\u0105gu. Zmiany zostan\u0105 wprowadzone przy kolejnym uruchomieniu OpenRocket. - - PreferencesDialog.lbl.language = J\u0119zyk programu: - PreferencesDialog.languages.default = Domy\u015Blny j\u0119zyk systemu - PreferencesDialog.lbl.languageEffect = Nowy j\u0119zyk zostanie ustawiony przy kolejnym uruchomieniu OpenRocket. + +generalprefs.lbl.language = J\u0119zyk programu: +generalprefs.languages.default = Domy\u015Blny j\u0119zyk systemu +generalprefs.lbl.languageEffect = Nowy j\u0119zyk zostanie ustawiony przy kolejnym uruchomieniu OpenRocket. ! Software update checker update.dlg.error.title = Nie mo\u017Cna uzyska\u0107 informacji o aktualizacji diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties index b9e7669c58..89feaa3e60 100644 --- a/core/resources/l10n/messages_pt.properties +++ b/core/resources/l10n/messages_pt.properties @@ -709,9 +709,9 @@ PlotDialog.lbl.Chart = Clique e arraste para baixo+direita para am # PlotDialog PlotDialog.title.Flightdataplot = Plotagem dos dados de voo -PreferencesDialog.languages.default = Padr\u00e3o do sistema -PreferencesDialog.lbl.language = Idioma da interface: -PreferencesDialog.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket. +generalprefs.languages.default = Padr\u00e3o do sistema +generalprefs.lbl.language = Idioma da interface: +generalprefs.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket. PresetModel.lbl.custompreset = Personalizado PresetModel.lbl.partsLib = Biblioteca de pe\u00e7as @@ -786,12 +786,6 @@ RocketActions.NewStageAct.Newstage = Novo est\u00e1gio RocketActions.NewStageAct.ttip.Newstage = Adicionar um novo est\u00e1gio ao projeto do foguete. RocketActions.PasteAct.Paste = Colar RocketActions.PasteAct.ttip.Paste = Cole o componente ou simula\u00e7\u00e3o na \u00e1rea da transfer\u00eancia para o projeto. -# RocketActions -RocketActions.checkbox.Donotaskmeagain = N\u00e3o me pergunte novamente -RocketActions.lbl.Youcanchangedefop = Voc\u00ea pode alterar a opera\u00e7\u00e3o padr\u00e3o em Prefer\u00eancias. -RocketActions.showConfirmDialog.lbl1 = Excluir as simula\u00e7\u00f5es selecionadas? -RocketActions.showConfirmDialog.lbl2 = Esta opera\u00e7\u00e3o n\u00e3o poder\u00e1 ser desfeita. -RocketActions.showConfirmDialog.title = Excluir simula\u00e7\u00f5es RocketCfg.lbl.Comments = Coment\u00e1rios: RocketCfg.lbl.Designer = Projetista: diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 57de961809..7da3fd403f 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -16,11 +16,6 @@ debug.currentFile = messages_ru.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = \u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C -RocketActions.lbl.Youcanchangedefop = \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445. -RocketActions.showConfirmDialog.lbl1 = \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B? -RocketActions.showConfirmDialog.lbl2 = \u042D\u0442\u0443 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E \u043D\u0435\u043B\u044C\u0437\u044F \u043E\u0442\u043C\u0435\u043D\u0438\u0442\u044C. -RocketActions.showConfirmDialog.title = \u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432 RocketActions.DelCompAct.Delete = \u0423\u0434\u0430\u043B\u0438\u0442\u044C RocketActions.DelCompAct.ttip.Delete = \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0439 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442. RocketActions.DelSimuAct.Delete = \u0423\u0434\u0430\u043B\u0438\u0442\u044C @@ -346,10 +341,6 @@ pref.dlg.PrefBooleanSelector2 = \u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434 pref.dlg.Add = \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C pref.dlg.DescriptionArea.Adddirectories = \u0414\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u0441\u0432\u043E\u0438\u0445 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u0444\u0430\u0439\u043B\u044B \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 RASP (*.eng), \u0444\u0430\u0439\u043B\u044B \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 RockSim (*.rse) \u0438\u043B\u0438 ZIP-\u0430\u0440\u0445\u0438\u0432\u044B, \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0435 \u0442\u043E\u0447\u043A\u043E\u0439 \u0441 \u0437\u0430\u043F\u044F\u0442\u043E\u0439 (;). \u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u0441\u0442\u0443\u043F\u044F\u0442 \u0432 \u0441\u0438\u043B\u0443 \u043F\u0440\u0438 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket. -PreferencesDialog.lbl.language = \u042F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: -PreferencesDialog.languages.default = \u0421\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439 -PreferencesDialog.lbl.languageEffect = \u042F\u0437\u044B\u043A \u0441\u043C\u0435\u043D\u0438\u0442\u0441\u044F \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket. - generalprefs.lbl.language = \u042F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 generalprefs.languages.default = \u0421\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439 generalprefs.lbl.languageEffect = \u042F\u0437\u044B\u043A \u0441\u043C\u0435\u043D\u0438\u0442\u0441\u044F \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket. diff --git a/core/resources/l10n/messages_tr.properties b/core/resources/l10n/messages_tr.properties index 3c9c22979f..f619a70c12 100644 --- a/core/resources/l10n/messages_tr.properties +++ b/core/resources/l10n/messages_tr.properties @@ -17,12 +17,6 @@ purposes) debug.currentFile = messages.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = Yeniden Tekrarlama -RocketActions.lbl.Youcanchangedefop = Hatal\u0131 \u00c7al\u0131\u015fmay\u0131 De\u011fi\u015ftirebilirsin -in the preferences. -RocketActions.showConfirmDialog.lbl1 = Se\u00e7ili sim\u00fclasyonlar\u0131 silmek ister misiniz? -RocketActions.showConfirmDialog.lbl2 = Bu \u00e7al\u0131\u015fma tamamlanmad\u0131. -RocketActions.showConfirmDialog.title = Sim\u00fclasyonlar\u0131 Sil RocketActions.DelCompAct.Delete = Sil RocketActions.DelCompAct.ttip.Delete = Se\u00e7ili par\u00e7ay\u0131 sil. RocketActions.DelSimuAct.Delete = Sil diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index 8a7e912d83..08550fd700 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -18,11 +18,6 @@ debug.currentFile = messages.properties ! RocketActions -RocketActions.checkbox.Donotaskmeagain = \u0411\u0456\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0438\u0442\u0430\u0442\u0438 -RocketActions.lbl.Youcanchangedefop = \u0412\u0438 \u043C\u043E\u0436\u0435\u0442\u0435 \u0437\u043C\u0456\u043D\u0438\u0442\u0438 \u0442\u0438\u043F\u043E\u0432\u0443 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E \u0432 \u043E\u043F\u0446\u0438\u044F\u0445. -RocketActions.showConfirmDialog.lbl1 = \u0412\u0434\u0430\u043B\u0438\u0442\u0438 \u0432\u0438\u0431\u0440\u0430\u043D\u0443 \u0441\u0438\u043C\u0443\u043B\u044F\u0446\u0456\u044E? -RocketActions.showConfirmDialog.lbl2 = \u0426\u044F \u043E\u043F\u0435\u0440\u0430\u0446\u0456\u044F \u043D\u0435 \u043C\u043E\u0436\u0435 \u0431\u0443\u0442\u0438 \u0432\u0456\u0434\u043C\u0456\u043D\u0435\u043D\u0430. -RocketActions.showConfirmDialog.title = \u0412\u0434\u0430\u043B\u0438\u0442\u0438 \u0441\u0438\u043C\u0443\u043B\u044F\u0446\u0456\u044E RocketActions.DelCompAct.Delete = \u0412\u0434\u0430\u043B\u0438\u0442\u0438 RocketActions.DelCompAct.ttip.Delete = \u0412\u0434\u0430\u043B\u0438\u0442\u0438 \u0432\u0438\u0431\u0440\u0430\u043D\u0443 \u0447\u0430\u0441\u0442\u0438\u043D\u0443. RocketActions.DelSimuAct.Delete = \u0412\u0434\u0430\u043B\u0438\u0442\u0438 @@ -309,9 +304,9 @@ pref.dlg.PrefBooleanSelector2 = Confirm pref.dlg.Add = Add pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket. -PreferencesDialog.lbl.language = Interface language: -PreferencesDialog.languages.default = System default -PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket. +generalprefs.lbl.language = Interface language: +generalprefs.languages.default = System default +generalprefs.lbl.languageEffect = The language will change the next time you start OpenRocket. ! Software update checker update.dlg.error.title = Unable to retrieve update information diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties index b4bff94ed6..0a6180ad89 100644 --- a/core/resources/l10n/messages_zh_CN.properties +++ b/core/resources/l10n/messages_zh_CN.properties @@ -797,9 +797,9 @@ PlotConfiguration.Verticalmotion = \u5782\u76F4\u8FD0\u52A8 vs. \u65F6\u95F PlotDialog.CheckBox.Showdatapoints = \u663E\u793A\u6570\u636E\u70B9 PlotDialog.lbl.Chart = \u5DE6\u952E\u62D6\u62FD\u79FB\u52A8\u6570\u636E\u533A. \u6EDA\u8F6E\u7F29\u653E. ctrl-\u6EDA\u8F6E\u4EC5\u7F29\u653EX\u8F74. ctrl-\u5DE6\u952E\u62D6\u62FD\u79FB\u52A8\u89C6\u56FE. \u53F3\u952E\u8FC7\u62FD\u8C03\u6574\u663E\u793A\u5927\u5C0F. -PreferencesDialog.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4 -PreferencesDialog.lbl.language = \u754C\u9762\u8BED\u8A00: -PreferencesDialog.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548 +generalprefs.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4 +generalprefs.lbl.language = \u754C\u9762\u8BED\u8A00: +generalprefs.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548 PresetModel.lbl.custompreset = \u5b9a\u5236 PresetModel.lbl.partsLib = \u96f6\u4ef6\u5e93 @@ -875,12 +875,6 @@ RocketActions.NewStageAct.Newstage = \u65B0\u5EFA\u4E00\u7EA7 RocketActions.NewStageAct.ttip.Newstage = \u5728\u8BBE\u8BA1\u4E2D\u65B0\u5EFA\u4E00\u7EA7 RocketActions.PasteAct.Paste = \u7C98\u8D34 RocketActions.PasteAct.ttip.Paste = \u5C06\u526A\u8D34\u677F\u91CC\u7684\u90E8\u4EF6\u6216\u4EFF\u771F\u7C98\u8D34\u5230\u8BE5\u8BBE\u8BA1\u4E2D -! RocketActions -RocketActions.checkbox.Donotaskmeagain = \u4E0D\u518D\u63D0\u793A -RocketActions.lbl.Youcanchangedefop = \u4F60\u53EF\u4EE5\u4FEE\u6539\u7F3A\u7701\u8BBE\u7F6E -RocketActions.showConfirmDialog.lbl1 = \u5220\u9664\u9009\u5B9A\u4EFF\u771F? -RocketActions.showConfirmDialog.lbl2 = \u8BE5\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500 -RocketActions.showConfirmDialog.title = \u5220\u9664\u4EFF\u771F RocketCfg.lbl.Comments = \u6CE8\u91CA: RocketCfg.lbl.Designer = \u8BBE\u8BA1\u4EBA: diff --git a/core/resources/pix/componenticons/pods-small.png b/core/resources/pix/componenticons/pods-small.png index 17371e846f..d000955a20 100644 Binary files a/core/resources/pix/componenticons/pods-small.png and b/core/resources/pix/componenticons/pods-small.png differ diff --git a/core/resources/pix/icons/cd-override-subcomponent_dark.png b/core/resources/pix/icons/cd-override-subcomponent_dark.png new file mode 100644 index 0000000000..6ffec0bc4a Binary files /dev/null and b/core/resources/pix/icons/cd-override-subcomponent_dark.png differ diff --git a/core/resources/pix/icons/cd-override-subcomponent.png b/core/resources/pix/icons/cd-override-subcomponent_light.png similarity index 100% rename from core/resources/pix/icons/cd-override-subcomponent.png rename to core/resources/pix/icons/cd-override-subcomponent_light.png diff --git a/core/resources/pix/icons/cd-override_dark.png b/core/resources/pix/icons/cd-override_dark.png new file mode 100644 index 0000000000..566d47bcfc Binary files /dev/null and b/core/resources/pix/icons/cd-override_dark.png differ diff --git a/core/resources/pix/icons/cd-override.png b/core/resources/pix/icons/cd-override_light.png similarity index 100% rename from core/resources/pix/icons/cd-override.png rename to core/resources/pix/icons/cd-override_light.png diff --git a/core/resources/pix/icons/cg-override-subcomponent_dark.png b/core/resources/pix/icons/cg-override-subcomponent_dark.png new file mode 100644 index 0000000000..4930915e0e Binary files /dev/null and b/core/resources/pix/icons/cg-override-subcomponent_dark.png differ diff --git a/core/resources/pix/icons/cg-override-subcomponent.png b/core/resources/pix/icons/cg-override-subcomponent_light.png similarity index 100% rename from core/resources/pix/icons/cg-override-subcomponent.png rename to core/resources/pix/icons/cg-override-subcomponent_light.png diff --git a/core/resources/pix/icons/cg-override_dark.png b/core/resources/pix/icons/cg-override_dark.png new file mode 100644 index 0000000000..1e1bb74f7c Binary files /dev/null and b/core/resources/pix/icons/cg-override_dark.png differ diff --git a/core/resources/pix/icons/cg-override.png b/core/resources/pix/icons/cg-override_light.png similarity index 100% rename from core/resources/pix/icons/cg-override.png rename to core/resources/pix/icons/cg-override_light.png diff --git a/core/resources/pix/icons/mass-override-subcomponent_dark.png b/core/resources/pix/icons/mass-override-subcomponent_dark.png new file mode 100755 index 0000000000..60d6fa5b86 Binary files /dev/null and b/core/resources/pix/icons/mass-override-subcomponent_dark.png differ diff --git a/core/resources/pix/icons/mass-override-subcomponent.png b/core/resources/pix/icons/mass-override-subcomponent_light.png similarity index 100% rename from core/resources/pix/icons/mass-override-subcomponent.png rename to core/resources/pix/icons/mass-override-subcomponent_light.png diff --git a/core/resources/pix/icons/mass-override_dark.png b/core/resources/pix/icons/mass-override_dark.png new file mode 100755 index 0000000000..3096bf60f1 Binary files /dev/null and b/core/resources/pix/icons/mass-override_dark.png differ diff --git a/core/resources/pix/icons/mass-override.png b/core/resources/pix/icons/mass-override_light.png similarity index 100% rename from core/resources/pix/icons/mass-override.png rename to core/resources/pix/icons/mass-override_light.png diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index c1ea85f24c..47160f751b 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -383,12 +383,7 @@ public int getSimulationIndex(Simulation simulation) { * @param simulation the simulation to be added */ public void addSimulation(Simulation simulation) { - simulations.add(simulation); - FlightConfigurationId simId = simulation.getId(); - if( !rocket.containsFlightConfigurationID( simId )){ - rocket.createFlightConfiguration(simId); - } - fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); + addSimulation(simulation, simulations.size()); } /** @@ -399,6 +394,10 @@ public void addSimulation(Simulation simulation) { */ public void addSimulation(Simulation simulation, int n) { simulations.add(n, simulation); + FlightConfigurationId simId = simulation.getId(); + if( !rocket.containsFlightConfigurationID( simId )){ + rocket.createFlightConfiguration(simId); + } fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); } diff --git a/core/src/net/sf/openrocket/logging/Warning.java b/core/src/net/sf/openrocket/logging/Warning.java index 9d1bc04211..23c4bcb90e 100644 --- a/core/src/net/sf/openrocket/logging/Warning.java +++ b/core/src/net/sf/openrocket/logging/Warning.java @@ -386,7 +386,11 @@ public boolean replaceBy(Message other) { public static final Warning TUBE_SEPARATION = new Other(trans.get("Warning.TUBE_SEPARATION")); public static final Warning TUBE_OVERLAP = new Other(trans.get("Warning.TUBE_OVERLAP")); + /** A Warning that stage separation occurred at other than the last stage */ public static final Warning SEPARATION_ORDER = new Other(trans.get("Warning.SEPARATION_ORDER")); + /** A Warning that stage separation occurred before the rocket cleared the launch rod or rail */ + public static final Warning EARLY_SEPARATION = new Other(trans.get("Warning.EARLY_SEPARATION")); + public static final Warning EMPTY_BRANCH = new Other(trans.get("Warning.EMPTY_BRANCH")); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index b4d3ac47e6..249f8dfeb5 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -170,14 +170,14 @@ public String toDebugSeparation() { * @return the previous stage in the rocket */ public AxialStage getUpperStage() { - if( null == this.parent ) { + if (this.parent == null) { return null; - }else if(Rocket.class.isAssignableFrom(this.parent.getClass()) ){ - final int thisIndex = getStageNumber(); - if( 0 < thisIndex ){ - return (AxialStage)parent.getChild(thisIndex-1); + } else if (Rocket.class.isAssignableFrom(this.parent.getClass())) { + final int thisIndex = parent.getChildPosition(this); + if (thisIndex > 0) { + return (AxialStage) parent.getChild(thisIndex-1); } - }else { + } else { return this.parent.getStage(); } return null; diff --git a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java index 1e17bb335a..c42aec1b00 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java @@ -170,6 +170,11 @@ public void setInstanceCount( final int newCount ){ " Please set setClusterConfiguration(ClusterConfiguration) instead.", new UnsupportedOperationException("InnerTube.setInstanceCount(..) on an"+this.getClass().getSimpleName())); } + + @Override + public boolean isAfter(){ + return false; + } /** * Get the cluster scaling. A value of 1.0 indicates that the tubes are packed @@ -177,14 +182,10 @@ public void setInstanceCount( final int newCount ){ * pack inside each other. */ public double getClusterScale() { + mutex.verify(); return clusterScale; } - @Override - public boolean isAfter(){ - return false; - } - /** * Set the cluster scaling. * @see #getClusterScale() @@ -203,6 +204,23 @@ public void setClusterScale(double scale) { clusterScale = scale; fireComponentChangeEvent(new ComponentChangeEvent(this, ComponentChangeEvent.MASS_CHANGE)); } + + /** + * Get the cluster scaling as an absolute distance measurement. A value of 0 indicates that the tubes are packed + * touching each other, larger values separate the tubes and smaller values pack inside each other. + */ + public double getClusterScaleAbsolute() { + return (getClusterScale() - 1) * getOuterRadius() * 2; + } + + /** + * Set the absolute cluster scaling (in terms of distance). + * @see #getClusterScaleAbsolute() + */ + public void setClusterScaleAbsolute(double scale) { + double scaleRel = scale / (getOuterRadius() * 2) + 1; + setClusterScale(scaleRel); + } @@ -242,27 +260,21 @@ public double getClusterSeparation() { } public List getClusterPoints() { - List list = new ArrayList(getInstanceCount()); + List list = new ArrayList<>(getInstanceCount()); List points = cluster.getPoints(clusterRotation - getRadialDirection()); double separation = getClusterSeparation(); + double yOffset = this.radialPosition * Math.cos(this.radialDirection); + double zOffset = this.radialPosition * Math.sin(this.radialDirection); for (int i = 0; i < points.size() / 2; i++) { - list.add(new Coordinate(0, points.get(2 * i) * separation, points.get(2 * i + 1) * separation)); + list.add(new Coordinate(0, points.get(2 * i) * separation + yOffset, points.get(2 * i + 1) * separation + zOffset)); } return list; } @Override public Coordinate[] getInstanceOffsets(){ - - if ( 1 == getInstanceCount()) { - double yOffset = this.radialPosition * Math.cos(this.radialDirection); - double zOffset = this.radialPosition * Math.sin(this.radialDirection); - return new Coordinate[] { Coordinate.ZERO.add(0.0, yOffset, zOffset) }; - } - List points = getClusterPoints(); - - return points.toArray( new Coordinate[ points.size() ]); + return points.toArray(new Coordinate[0]); } // @Override diff --git a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java index cb38838579..9cd011ead6 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java @@ -849,12 +849,20 @@ private boolean inline(final RocketComponent candidate) { // have a radial offset of 0 from their centerline, we are in line. if ((this.parent instanceof RingInstanceable) && - (!MathUtil.equals(this.parent.getRadiusMethod().getRadius(this.parent.parent, this, this.parent.getRadiusOffset()), 0))) + (!MathUtil.equals(this.parent.getRadiusMethod().getRadius( + this.parent.parent, this, this.parent.getRadiusOffset()), 0))) return false; - if ((candidate.parent instanceof RingInstanceable) && - (!MathUtil.equals(candidate.parent.getRadiusMethod().getRadius(candidate.parent.parent, candidate, candidate.parent.getRadiusOffset()), 0))) - return false; + if (candidate.parent instanceof RingInstanceable) { + // We need to check if the grandparent of the candidate is a body tube and if the outer radius is automatic. + // If so, then this would cause an infinite loop when checking the radius of the radiusMethod. + if (candidate.parent.parent == this && (this.isAftRadiusAutomatic() || this.isForeRadiusAutomatic())) { + return false; + } else { + return MathUtil.equals(candidate.parent.getRadiusMethod().getRadius( + candidate.parent.parent, candidate, candidate.parent.getRadiusOffset()), 0); + } + } return true; } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java index a518844bff..bba87bd3af 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -118,6 +118,8 @@ public void setForeRadius(double radius, boolean doClamping) { clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + + setForeShoulderRadius(getForeShoulderRadius()); } public void setForeRadius(double radius) { @@ -207,6 +209,8 @@ public void setAftRadius(double radius, boolean doClamping) { clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + + setAftShoulderRadius(getAftShoulderRadius()); } public void setAftRadius(double radius) { @@ -398,6 +402,7 @@ public void setForeShoulderRadius(double foreShoulderRadius) { ((Transition) listener).setForeShoulderRadius(foreShoulderRadius); } } + foreShoulderRadius = Math.min(foreShoulderRadius, getForeRadius()); if (MathUtil.equals(this.foreShoulderRadius, foreShoulderRadius)) return; @@ -471,6 +476,8 @@ public void setAftShoulderRadius(double aftShoulderRadius) { } } + aftShoulderRadius = Math.min(aftShoulderRadius, getAftRadius()); + if (MathUtil.equals(this.aftShoulderRadius, aftShoulderRadius)) return; this.aftShoulderRadius = aftShoulderRadius; diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index be9fd8bc67..32977c194e 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -485,6 +485,11 @@ else if (event.getType() == FlightEvent.Type.EJECTION_CHARGE) currentStatus.getWarnings().add(Warning.SEPARATION_ORDER); } + // If I haven't cleared the rail yet, flag a warning + if (!currentStatus.isLaunchRodCleared()) { + currentStatus.getWarnings().add(Warning.EARLY_SEPARATION); + } + // Create a new simulation branch for the booster SimulationStatus boosterStatus = new SimulationStatus(currentStatus); diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index 02d272f03d..4c4f60ef0d 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -118,6 +118,8 @@ public abstract class Preferences implements ChangeSource { public static final String LAUNCH_USE_ISA = "LaunchUseISA"; public static final String SIMULATION_TIME_STEP = "SimulationTimeStep"; public static final String GEODETIC_COMPUTATION = "GeodeticComputationStrategy"; + + public static final String UI_THEME = "UITheme"; private static final AtmosphericModel ISA_ATMOSPHERIC_MODEL = new ExtendedISAModel(); @@ -549,11 +551,6 @@ public void setTimeStep(double timeStep) { fireChangeEvent(); } - - public final float getRocketInfoFontSize() { - return (float) (11.0 + 3 * Application.getPreferences().getChoice(Preferences.ROCKET_INFO_FONT_SIZE, 2, 0)); - } - /** * Enable/Disable the auto-opening of the last edited design file on startup. */ @@ -820,25 +817,6 @@ public final void putEnum(String key, Enum value) { } } - public Color getDefaultColor(Class c) { - String color = get("componentColors", c, StaticFieldHolder.DEFAULT_COLORS); - if (color == null) - return Color.BLACK; - - Color clr = parseColor(color); - if (clr != null) { - return clr; - } else { - return Color.BLACK; - } - } - - public final void setDefaultColor(Class c, Color color) { - if (color == null) - return; - putString("componentColors", c.getSimpleName(), stringifyColor(color)); - } - /** * Retrieve a Line style for the given component. @@ -1014,6 +992,29 @@ protected String get(String directory, public abstract void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite); public abstract Set getComponentFavorites(ComponentPreset.Type type); + + + /* + NOTE: It is unusual for the UI Theme to be stored in the preferences instead of SwingPreferences. In fact, this code + is not pretty. Sometimes I just really hate Java and circular dependencies... + But the reason why this is implemented is because it would otherwise be an even bigger nightmare to fix unit tests + that use their own preferences... Also wasn't a fan of always casting the preferences to SwingPreferences. + */ + /** + * Get the current theme used for the UI. + * @return the current theme + */ + public Object getUITheme() { + return null; + } + + /** + * Set the theme used for the UI. + * @param theme the theme to set + */ + public void setUITheme(Object theme) {} + + /* * Within a holder class so they will load only when needed. @@ -1032,19 +1033,6 @@ private static class StaticFieldHolder { DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name()); DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name()); } - - private static final HashMap, String> DEFAULT_COLORS = new HashMap, String>(); - - static { - DEFAULT_COLORS.put(BodyComponent.class, "0,0,240"); - DEFAULT_COLORS.put(TubeFinSet.class, "0,0,200"); - DEFAULT_COLORS.put(FinSet.class, "0,0,200"); - DEFAULT_COLORS.put(LaunchLug.class, "0,0,180"); - DEFAULT_COLORS.put(RailButton.class, "0,0,180"); - DEFAULT_COLORS.put(InternalComponent.class, "170,0,100"); - DEFAULT_COLORS.put(MassObject.class, "0,0,0"); - DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0"); - } } private final List listeners = new ArrayList(); diff --git a/core/src/net/sf/openrocket/util/Color.java b/core/src/net/sf/openrocket/util/Color.java index 7c3186d347..4f912bb4e6 100644 --- a/core/src/net/sf/openrocket/util/Color.java +++ b/core/src/net/sf/openrocket/util/Color.java @@ -66,6 +66,10 @@ public java.awt.Color toAWTColor() { return new java.awt.Color(red, green, blue, alpha); } + public static Color fromAWTColor(java.awt.Color AWTColor) { + return new Color(AWTColor.getRed(), AWTColor.getGreen(), AWTColor.getBlue(), AWTColor.getAlpha()); + } + @Override public boolean equals(Object obj) { if (super.equals(obj)) { diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index a6e5fd7ee2..f0012a0d51 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1049,7 +1049,7 @@ public static Rocket makeFalcon9Heavy() { boosterCone.setThickness(0.002); //payloadFairingNoseCone.setLength(0.118); //payloadFairingNoseCone.setAftRadius(0.052); - boosterCone.setAftShoulderRadius( 0.051 ); + boosterCone.setAftShoulderRadius( 0.0375 ); boosterCone.setAftShoulderLength( 0.02 ); boosterCone.setAftShoulderThickness( 0.001 ); boosterCone.setAftShoulderCapped( false ); diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 245948e04a..a790cfc076 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -498,7 +498,7 @@ public void testFalcon9HComponentMasses() { // ====== ====== ====== ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { - expMass = 0.0222459863653; + expMass = 0.0210923935430; // think of the casts as an assert that ( child instanceof NoseCone) == true NoseCone nose = (NoseCone) boosters.getChild(0); compMass = nose.getComponentMass(); @@ -582,7 +582,7 @@ public void testFalcon9HComponentCM() { // ====== ====== ====== ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { - expCMx = 0.055710581052; + expCMx = 0.053835211739; // think of the casts as an assert that ( child instanceof NoseCone) == true NoseCone nose = (NoseCone) boosters.getChild(0); actCMx = nose.getComponentCG().x; @@ -692,10 +692,10 @@ public void testFalcon9HComponentMOI() { ParallelStage boosters = (ParallelStage) coreStage.getChild(0).getChild(0); { final NoseCone boosterNose = (NoseCone) boosters.getChild(0); - expInertia = 1.82665797857e-5; + expInertia = 1.73189409900e-5; compInertia = boosterNose.getRotationalInertia(); assertEquals(boosterNose.getName() + " Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); - expInertia = 1.96501191666e-7; + expInertia = 4.51796586171e-6; compInertia = boosterNose.getLongitudinalInertia(); assertEquals(boosterNose.getName() + " Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON); @@ -832,8 +832,8 @@ public void testFalcon9HBoosterStructureCM() { final RigidBody actualData = MassCalculator.calculateStructure(config); final Coordinate actualCM = actualData.getCM(); - double expMass = 0.608663395; - double expCMx = 1.073157592; + double expMass = 0.6063562096; + double expCMx = 1.075056887; assertEquals("Heavy Booster Mass is incorrect: ", expMass, actualCM.weight, EPSILON); assertEquals("Heavy Booster CM.x is incorrect: ", expCMx, actualCM.x, EPSILON); @@ -852,11 +852,11 @@ public void testFalcon9HBoosterLaunchCM() { RigidBody actualBoosterLaunchData = MassCalculator.calculateLaunch(config); double actualMass = actualBoosterLaunchData.getMass(); - double expectedMass = 1.592663395; + double expectedMass = 1.5903562096; assertEquals(" Booster Launch Mass is incorrect: ", expectedMass, actualMass, EPSILON); final Coordinate actualCM = actualBoosterLaunchData.getCM(); - double expectedCMx = 1.22216804; + double expectedCMx = 1.22310836; Coordinate expCM = new Coordinate(expectedCMx, 0, 0, expectedMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, actualCM.x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, actualCM.y, EPSILON); @@ -876,8 +876,8 @@ public void testFalcon9HBoosterSpentCM() { RigidBody spentData = MassCalculator.calculateBurnout(config); Coordinate spentCM = spentData.getCM(); - double expSpentMass = 1.12066339; - double expSpentCMx = 1.18334714; + double expSpentMass = 1.11835621; + double expSpentCMx = 1.18460423; Coordinate expLaunchCM = new Coordinate(expSpentCMx, 0, 0, expSpentMass); assertEquals(" Booster Launch Mass is incorrect: ", expLaunchCM.weight, spentCM.weight, EPSILON); assertEquals(" Booster Launch CM.x is incorrect: ", expLaunchCM.x, spentCM.x, EPSILON); @@ -942,11 +942,11 @@ public void testFalcon9HeavyBoosterSpentMOIs() { RigidBody spent = MassCalculator.calculateBurnout(config); - double expMOIRotational = 0.009205665421431532; + double expMOIRotational = 0.00919009169; double boosterMOIRotational = spent.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOIRotational, boosterMOIRotational, EPSILON); - double expMOI_tr = 0.0582250994240395; + double expMOI_tr = 0.0573781722; double boosterMOI_tr = spent.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -962,9 +962,9 @@ public void testFalcon9HeavyBoosterLaunchMOIs() { RigidBody launchData = MassCalculator.calculateLaunch(config); - final double expIxx = 0.01226617242143153; + final double expIxx = 0.0122505987; final double actIxx = launchData.getRotationalInertia(); - final double expIyy = 0.06455356411879717; + final double expIyy = 0.0635943662; final double actIyy = launchData.getLongitudinalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expIxx, actIxx, EPSILON); @@ -1006,11 +1006,11 @@ public void testFalcon9HeavyBoosterStageMassOverride() { assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterSetCM); // Validate MOI - double expMOI_axial = 0.005885793421431532; + double expMOI_axial = 0.00587021969016; double boosterMOI_xx = burnout.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 17.86133586701; + double expMOI_tr = 17.78076176335; double boosterMOI_tr = burnout.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -1052,7 +1052,7 @@ public void testFalcon9HeavyComponentMassOverride() { double expTotalMass = 3.3565872; assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, boosterData.getMass(), EPSILON); - double expCMx = 0.2835089882645608; + double expCMx = 0.2827156156318; Coordinate expCM = new Coordinate(expCMx, 0, 0, expTotalMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterCM.x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterCM.y, EPSILON); @@ -1064,7 +1064,7 @@ public void testFalcon9HeavyComponentMassOverride() { double boosterMOI_xx = boosterData.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 0.34567788938578525; + double expMOI_tr = 0.347611843243; double boosterMOI_tr = boosterData.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } @@ -1095,11 +1095,11 @@ public void testFalcon9HeavyComponentCMxOverride() { RigidBody structure = MassCalculator.calculateStructure(config); - final double expMass = 0.6086633952494; + final double expMass = 0.6063562096046; double calcTotalMass = structure.getMass(); assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON); - final double expCMx = 0.5551303646438673; + final double expCMx = 0.55670993586431; Coordinate expCM = new Coordinate(expCMx, 0, 0, expMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, structure.getCM().x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, structure.getCM().y, EPSILON); @@ -1107,11 +1107,11 @@ public void testFalcon9HeavyComponentCMxOverride() { assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM()); // Validate MOI - final double expMOI_axial = 0.005885793421; + final double expMOI_axial = 0.005870219690; double boosterMOI_xx = structure.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - final double expMOI_tr = 0.040989095911; + final double expMOI_tr = 0.040598624476; double boosterMOI_tr = structure.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } diff --git a/core/test/net/sf/openrocket/simulation/FlightEventsTest.java b/core/test/net/sf/openrocket/simulation/FlightEventsTest.java index 5340eefc01..fe80e83c89 100644 --- a/core/test/net/sf/openrocket/simulation/FlightEventsTest.java +++ b/core/test/net/sf/openrocket/simulation/FlightEventsTest.java @@ -117,7 +117,7 @@ public void testMultiStage() throws SimulationException { new FlightEvent(FlightEvent.Type.IGNITION, 0.0, coreBody), new FlightEvent(FlightEvent.Type.LIFTOFF, 0.1225, null), new FlightEvent(FlightEvent.Type.LAUNCHROD, 0.125, null), - new FlightEvent(FlightEvent.Type.APOGEE, 1.86, rocket), + new FlightEvent(FlightEvent.Type.APOGEE, 1.867, rocket), new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, boosterMotorTubes), new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, boosterStage), new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, boosterStage), @@ -147,7 +147,7 @@ public void testMultiStage() throws SimulationException { new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, boosterMotorTubes), new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, boosterStage), new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, boosterStage), - new FlightEvent(FlightEvent.Type.TUMBLE, 3.551, null), + new FlightEvent(FlightEvent.Type.TUMBLE, 3.428, null), new FlightEvent(FlightEvent.Type.GROUND_HIT, 1200, null), new FlightEvent(FlightEvent.Type.SIMULATION_END, 1200, null) }; diff --git a/install4j/23.xx/openrocket-23.xx.install4j b/install4j/23.xx/openrocket-23.xx.install4j index 10a55f19a8..c9c664d8f1 100644 --- a/install4j/23.xx/openrocket-23.xx.install4j +++ b/install4j/23.xx/openrocket-23.xx.install4j @@ -48,6 +48,7 @@ + diff --git a/swing/.classpath b/swing/.classpath index d57e69a4c4..5aa1c307ee 100644 --- a/swing/.classpath +++ b/swing/.classpath @@ -11,6 +11,7 @@ + diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml index c9df46ca4e..55f1c84b1c 100644 --- a/swing/OpenRocket Swing.iml +++ b/swing/OpenRocket Swing.iml @@ -5,6 +5,7 @@ + @@ -107,6 +108,39 @@ + + + + + + + + + + + + "" + + + + + + + + + + + + + + + + + + + + + diff --git a/swing/build.xml b/swing/build.xml index d5b5070b5a..80ef81afec 100644 --- a/swing/build.xml +++ b/swing/build.xml @@ -74,7 +74,7 @@ Java/JVM detail version: ${java.version} Compiling main classes - + @@ -116,6 +116,22 @@ + + + + + + + + + + + + + + + + diff --git a/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..22945e1ff8 Binary files /dev/null and b/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..964cae4b14 Binary files /dev/null and b/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..e355b8786d Binary files /dev/null and b/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..f2cc41960b Binary files /dev/null and b/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..26fdbb0abb Binary files /dev/null and b/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..6b91bc95e2 Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..246e8d1b12 Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..b1768e88f0 Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..1db5e00e01 Binary files /dev/null and b/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..b39af8f8bf Binary files /dev/null and b/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..e8119013f3 Binary files /dev/null and b/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..8e22ee456a Binary files /dev/null and b/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar new file mode 100644 index 0000000000..139299518e Binary files /dev/null and b/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar differ diff --git a/swing/lib/darklaf/jsvg-0.0.9.jar b/swing/lib/darklaf/jsvg-0.0.9.jar new file mode 100644 index 0000000000..e8fbeba384 Binary files /dev/null and b/swing/lib/darklaf/jsvg-0.0.9.jar differ diff --git a/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar b/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar new file mode 100644 index 0000000000..2d2ec6b86a Binary files /dev/null and b/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar differ diff --git a/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar b/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar new file mode 100644 index 0000000000..5b39901e5b Binary files /dev/null and b/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar differ diff --git a/swing/src/net/sf/openrocket/communication/AssetHandler.java b/swing/src/net/sf/openrocket/communication/AssetHandler.java index a04d0080b6..b0aa4d7b17 100644 --- a/swing/src/net/sf/openrocket/communication/AssetHandler.java +++ b/swing/src/net/sf/openrocket/communication/AssetHandler.java @@ -2,12 +2,10 @@ import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.BuildProperties; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; /** * This class handles assets extracted from a GitHub release page. diff --git a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java index ea09614628..177188785c 100644 --- a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java +++ b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java @@ -3,9 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.ObjectInputStream; import java.util.Collection; -import java.util.List; import net.sf.openrocket.file.iterator.DirectoryIterator; import net.sf.openrocket.file.iterator.FileIterator; @@ -14,7 +12,6 @@ import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.xml.OpenRocketComponentLoader; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Pair; import org.slf4j.Logger; diff --git a/swing/src/net/sf/openrocket/gui/components/BasicTree.java b/swing/src/net/sf/openrocket/gui/components/BasicTree.java index 09d8b3d727..c3d75231e4 100644 --- a/swing/src/net/sf/openrocket/gui/components/BasicTree.java +++ b/swing/src/net/sf/openrocket/gui/components/BasicTree.java @@ -1,5 +1,7 @@ package net.sf.openrocket.gui.components; +import net.sf.openrocket.gui.util.GUIUtil; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; @@ -14,7 +16,6 @@ @SuppressWarnings("serial") public class BasicTree extends JTree { - public BasicTree() { super(); setDefaultOptions(); @@ -36,7 +37,7 @@ private void setDefaultOptions() { plainUI.setLeftChildIndent(15); - this.setBackground(Color.WHITE); + this.setBackground(GUIUtil.getUITheme().getBackgroundColor()); this.setShowsRootHandles(false); } diff --git a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java index 2c666eff20..947f70335a 100644 --- a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java +++ b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java @@ -1,9 +1,9 @@ package net.sf.openrocket.gui.components; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.URLUtil; import java.awt.Color; -import java.awt.Desktop; import java.awt.Dimension; import java.awt.Font; import java.awt.Rectangle; @@ -31,6 +31,8 @@ public class DescriptionArea extends JScrollPane { private final JEditorPane editorPane; + + private final float size; /** @@ -86,15 +88,14 @@ public DescriptionArea(String text, int rows) { public DescriptionArea(String text, int rows, float size, boolean opaque) { super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + this.size = size; editorPane = new JEditorPane("text/html", ""); - Font font = editorPane.getFont(); - editorPane.setFont(font.deriveFont(font.getSize2D() + size)); editorPane.setEditable(false); editorPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - URI uri = null; + URI uri; try { uri = e.getURL().toURI(); } @@ -125,8 +126,8 @@ public void hyperlinkUpdate(HyperlinkEvent e) { // create temporary file and copy resource to it - File of = null; - BufferedOutputStream os = null; + File of; + BufferedOutputStream os; try { of = File.createTempFile(prefix, suffix); os = new BufferedOutputStream(new FileOutputStream(of)); @@ -163,11 +164,11 @@ public void hyperlinkUpdate(HyperlinkEvent e) { } // Calculate correct height - editorPane.setText("abc"); + this.setText("abc"); Dimension oneline = editorPane.getPreferredSize(); - editorPane.setText("abc
def"); + this.setText("abc
def"); Dimension twolines = editorPane.getPreferredSize(); - editorPane.setText(""); + this.setText(""); int lineheight = twolines.height - oneline.height; int extraheight = oneline.height - lineheight; @@ -175,13 +176,20 @@ public void hyperlinkUpdate(HyperlinkEvent e) { Dimension dim = editorPane.getPreferredSize(); dim.height = lineheight * rows + extraheight + 2; this.setPreferredSize(dim); + + editorPane.setBorder(GUIUtil.getUITheme().getBorder()); this.setViewportView(editorPane); this.setText(text); } public void setText(String txt) { - editorPane.setText(txt); + // Set the font size (we can't simply set the font to change the font size, because we're using text/html) + Font defaultFont = editorPane.getFont(); + String fontName = defaultFont.getFontName(); + float fontSize = defaultFont.getSize2D() + size; + + editorPane.setText("" + txt + ""); editorPane.revalidate(); SwingUtilities.invokeLater(new Runnable() { diff --git a/swing/src/net/sf/openrocket/gui/components/URLLabel.java b/swing/src/net/sf/openrocket/gui/components/URLLabel.java index 5656eaa96c..3d9a45321c 100644 --- a/swing/src/net/sf/openrocket/gui/components/URLLabel.java +++ b/swing/src/net/sf/openrocket/gui/components/URLLabel.java @@ -1,23 +1,18 @@ package net.sf.openrocket.gui.components; -import java.awt.Color; import java.awt.Cursor; import java.awt.Desktop; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.font.TextAttribute; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.URLUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sf.openrocket.util.BugException; - /** * A label of a URL that is clickable. Clicking the URL will launch the URL in * the default browser if the Desktop class is supported. @@ -26,7 +21,7 @@ */ public class URLLabel extends SelectableLabel { private static final Logger log = LoggerFactory.getLogger(URLLabel.class); - + /** * Create a label showing the url it will direct to. * @@ -53,7 +48,7 @@ public URLLabel(final String url, String label) { Map map = new HashMap(); map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); this.setFont(this.getFont().deriveFont(map)); - this.setForeground(Color.BLUE); + this.setForeground(GUIUtil.getUITheme().getURLColor()); this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); diff --git a/swing/src/net/sf/openrocket/gui/components/compass/Tester.java b/swing/src/net/sf/openrocket/gui/components/compass/Tester.java index 97ca176b29..edac037995 100644 --- a/swing/src/net/sf/openrocket/gui/components/compass/Tester.java +++ b/swing/src/net/sf/openrocket/gui/components/compass/Tester.java @@ -22,7 +22,7 @@ public static void main(String[] args) throws InterruptedException, InvocationTa BasicApplication baseApp = new BasicApplication(); baseApp.initializeApplication(); - GUIUtil.setBestLAF(); + GUIUtil.applyLAF(); SwingUtilities.invokeAndWait(new Runnable() { @Override diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index d0569ea2f5..f35e5dba20 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -2,7 +2,6 @@ import java.awt.Color; import java.awt.Component; -import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Method; @@ -170,6 +169,7 @@ public void actionPerformed(ActionEvent colorClickEvent) { Color awtColor = ColorConversion.toAwtColor(c); colorChooser.setColor(awtColor); + colorChooser.updateUI(); // Needed for darklaf color chooser to update // Bind a change of color selection to a change in the components color ColorSelectionModel model = colorChooser.getSelectionModel(); @@ -183,7 +183,8 @@ public void stateChanged(ChangeEvent changeEvent) { JDialog d = JColorChooser.createDialog(AppearancePanel.this, trans.get("RocketCompCfg.lbl.Choosecolor"), true, - colorChooser, new ActionListener() { + colorChooser, + new ActionListener() { @Override public void actionPerformed(ActionEvent okEvent) { changeComponentColor(colorChooser.getColor()); @@ -262,8 +263,7 @@ public AppearancePanel(final OpenRocketDocument document, final RocketComponent net.sf.openrocket.util.Color figureColor = c.getColor(); if (figureColor == null) { - figureColor = Application.getPreferences().getDefaultColor( - c.getClass()); + figureColor = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass()); } final JButton figureColorButton = new SelectColorButton( new ColorIcon(figureColor)); @@ -278,8 +278,7 @@ public AppearancePanel(final OpenRocketDocument document, final RocketComponent public void stateChanged(EventObject e) { net.sf.openrocket.util.Color col = c.getColor(); if (col == null) { - col = Application.getPreferences().getDefaultColor( - c.getClass()); + col = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass()); } figureColorButton.setIcon(new ColorIcon(col)); } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java index 1d3ef71b83..98709e7344 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java @@ -9,6 +9,8 @@ import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Ellipse2D; @@ -17,12 +19,14 @@ import java.util.List; import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; @@ -36,6 +40,7 @@ import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; @@ -46,6 +51,7 @@ import net.sf.openrocket.rocketcomponent.RingComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Coordinate; @@ -55,6 +61,9 @@ public class InnerTubeConfig extends RocketComponentConfig { private static final long serialVersionUID = 7900041420864324470L; private static final Translator trans = Application.getTranslator(); + private static final Preferences prefs = Application.getPreferences(); + + private static final String PREF_SEPARATION_RELATIVE = "InnerTubeSeparationRelative"; public InnerTubeConfig(OpenRocketDocument d, RocketComponent c, JDialog parent) { @@ -279,29 +288,88 @@ private JPanel clusterTab() { //// The separation of the tubes, 1.0 = touching each other l.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.TubeSep")); subPanel.add(l); - DoubleModel dm = new DoubleModel(component, "ClusterScale", 1, UnitGroup.UNITS_NONE, 0); - JSpinner spin = new JSpinner(dm.getSpinnerModel()); - spin.setEditor(new SpinnerEditor(spin)); - //// The separation of the tubes, 1.0 = touching each other - spin.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.TubeSep")); - subPanel.add(spin, "growx"); - order.add(((SpinnerEditor) spin.getEditor()).getTextField()); + //// Models + final boolean useRelativeSeparation = prefs.getBoolean(PREF_SEPARATION_RELATIVE, true); + final DoubleModel clusterScaleModelRel = new DoubleModel(component, "ClusterScale", 1, UnitGroup.UNITS_NONE, 0); + final DoubleModel clusterScaleModelAbs = new DoubleModel(component, "ClusterScaleAbsolute", 1, UnitGroup.UNITS_LENGTH); + final DoubleModel clusterScaleModel = useRelativeSeparation ? clusterScaleModelRel : clusterScaleModelAbs; + + final String clusterScaleTtipRel = trans.get("InnerTubeCfg.lbl.ttip.TubeSep"); + final String clusterScaleTtipAbs = trans.get("InnerTubeCfg.lbl.ttip.TubeSepAbs"); + final String clusterScaleTtip = useRelativeSeparation ? clusterScaleTtipRel : clusterScaleTtipAbs; + + JSpinner clusterScaleSpin = new JSpinner(clusterScaleModel.getSpinnerModel()); + clusterScaleSpin.setEditor(new SpinnerEditor(clusterScaleSpin)); + clusterScaleSpin.setToolTipText(clusterScaleTtip); + subPanel.add(clusterScaleSpin, "growx"); + order.add(((SpinnerEditor) clusterScaleSpin.getEditor()).getTextField()); + + UnitSelector clusterScaleUnit = new UnitSelector(clusterScaleModel); + subPanel.add(clusterScaleUnit, "growx"); + + BasicSlider clusterScaleBs = new BasicSlider(clusterScaleModel.getSliderModel(0, 1, 4)); + subPanel.add(clusterScaleBs, "w 100lp, wrap"); + + // Relative/absolute separation + JRadioButton rbRel = new JRadioButton(trans.get("InnerTubeCfg.radioBut.Relative")); + JRadioButton rbAbs = new JRadioButton(trans.get("InnerTubeCfg.radioBut.Absolute")); + rbRel.setToolTipText(trans.get("InnerTubeCfg.radioBut.Relative.ttip")); + rbAbs.setToolTipText(trans.get("InnerTubeCfg.radioBut.Absolute.ttip")); + ButtonGroup bg = new ButtonGroup(); + bg.add(rbRel); + bg.add(rbAbs); + subPanel.add(rbRel, "skip, spanx, split 2"); + subPanel.add(rbAbs, "wrap"); + + rbRel.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) + return; + clusterScaleSpin.setModel(clusterScaleModelRel.getSpinnerModel()); + clusterScaleSpin.setEditor(new SpinnerEditor(clusterScaleSpin)); + clusterScaleUnit.setModel(clusterScaleModelRel); + clusterScaleBs.setModel(clusterScaleModelRel.getSliderModel(0, 1, 4)); + clusterScaleSpin.setToolTipText(clusterScaleTtipRel); + + prefs.putBoolean(PREF_SEPARATION_RELATIVE, false); + } + }); + rbAbs.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) + return; + DoubleModel radiusModelMin = new DoubleModel(component, "OuterRadius", -2, UnitGroup.UNITS_LENGTH); + DoubleModel radiusModelMax = new DoubleModel(component, "OuterRadius", 6, UnitGroup.UNITS_LENGTH); + + clusterScaleSpin.setModel(clusterScaleModelAbs.getSpinnerModel()); + clusterScaleSpin.setEditor(new SpinnerEditor(clusterScaleSpin)); + clusterScaleUnit.setModel(clusterScaleModelAbs); + clusterScaleBs.setModel(clusterScaleModelAbs.getSliderModel(radiusModelMin, radiusModelMax)); + clusterScaleSpin.setToolTipText(clusterScaleTtipAbs); + + prefs.putBoolean(PREF_SEPARATION_RELATIVE, false); + } + }); - BasicSlider bs = new BasicSlider(dm.getSliderModel(0, 1, 4)); - //// The separation of the tubes, 1.0 = touching each other - bs.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.TubeSep")); - subPanel.add(bs, "skip,w 100lp, wrap"); + // Select the button by default + if (prefs.getBoolean(PREF_SEPARATION_RELATIVE, true)) { + rbRel.setSelected(true); + } else { + rbAbs.setSelected(true); + } // Rotation: l = new JLabel(trans.get("InnerTubeCfg.lbl.Rotation")); //// Rotation angle of the cluster configuration l.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.Rotation")); subPanel.add(l); - dm = new DoubleModel(component, "ClusterRotation", 1, UnitGroup.UNITS_ANGLE, + DoubleModel dm = new DoubleModel(component, "ClusterRotation", 1, UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI); - spin = new JSpinner(dm.getSpinnerModel()); + JSpinner spin = new JSpinner(dm.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); //// Rotation angle of the cluster configuration spin.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.Rotation")); @@ -309,7 +377,7 @@ private JPanel clusterTab() { order.add(((SpinnerEditor) spin.getEditor()).getTextField()); subPanel.add(new UnitSelector(dm), "growx"); - bs = new BasicSlider(dm.getSliderModel()); + BasicSlider bs = new BasicSlider(dm.getSliderModel()); //// Rotation angle of the cluster configuration bs.setToolTipText(trans.get("InnerTubeCfg.lbl.ttip.Rotation")); subPanel.add(bs, "w 100lp, wrap para"); @@ -398,10 +466,17 @@ class ClusterSelectionPanel extends JPanel { private static final int BUTTON_SIZE = 50; private static final int MOTOR_DIAMETER = 10; - private static final Color SELECTED_COLOR = Color.RED; - private static final Color UNSELECTED_COLOR = Color.WHITE; - private static final Color MOTOR_FILL_COLOR = Color.GREEN; - private static final Color MOTOR_BORDER_COLOR = Color.BLACK; + private static final Color SELECTED_COLOR; + private static final Color UNSELECTED_COLOR; + private static final Color MOTOR_FILL_COLOR; + private static final Color MOTOR_BORDER_COLOR; + + static { + SELECTED_COLOR = Color.RED; + UNSELECTED_COLOR = GUIUtil.getUITheme().getBackgroundColor(); + MOTOR_FILL_COLOR = Color.GREEN; + MOTOR_BORDER_COLOR = Color.BLACK; + } public ClusterSelectionPanel(Clusterable component) { super(new MigLayout("gap 0 0", @@ -438,6 +513,7 @@ public ClusterButton(Clusterable c, ClusterConfiguration config) { setMaximumSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE)); setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); // setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); + setToolTipText(config.getXMLName()); component.addChangeListener(this); addMouseListener(this); } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index fb9dc9a954..6a8dad3ef2 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -506,7 +506,7 @@ private JPanel overrideTab() { StyledLabel labelMassOverriddenBy = new StyledLabel( String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy"), component.getMassOverriddenBy().getName()), 0, StyledLabel.Style.BOLD); - labelMassOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelMassOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); labelMassOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy.ttip"), component.getMassOverriddenBy().getName())); checkboxes.add(labelMassOverriddenBy, "gapleft 25lp, wrap"); @@ -569,7 +569,7 @@ private JPanel overrideTab() { StyledLabel labelCGOverriddenBy = new StyledLabel( String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy"), component.getCGOverriddenBy().getName()), 0, StyledLabel.Style.BOLD); - labelCGOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelCGOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); labelCGOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy.ttip"), component.getCGOverriddenBy().getName())); checkboxes.add(labelCGOverriddenBy, "gapleft 25lp, wrap"); @@ -663,7 +663,7 @@ private JPanel overrideTab() { StyledLabel labelCDOverriddenBy = new StyledLabel( String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName()), 0, StyledLabel.Style.BOLD); - labelCDOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelCDOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); labelCDOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName())); checkboxes.add(labelCDOverriddenBy, "gapleft 25lp, wrap"); @@ -719,6 +719,7 @@ private JPanel commentTab() { commentTextArea.setLineWrap(true); commentTextArea.setWrapStyleWord(true); commentTextArea.setEditable(true); + commentTextArea.setBorder(GUIUtil.getUITheme().getBorder()); GUIUtil.setTabToFocusing(commentTextArea); commentTextArea.addFocusListener(textFieldListener); commentTextArea.addKeyListener(new TextComponentSelectionKeyListener(commentTextArea)); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java index acd4ee47bf..9835778ca5 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java @@ -27,7 +27,7 @@ public class RocketConfig extends RocketComponentConfig { private static final Translator trans = Application.getTranslator(); - + private TextFieldListener textFieldListener; private JTextArea designerTextArea; @@ -55,6 +55,7 @@ public RocketConfig(OpenRocketDocument d, RocketComponent c, JDialog parent) { designerTextArea.setLineWrap(true); designerTextArea.setWrapStyleWord(true); designerTextArea.setEditable(true); + designerTextArea.setBorder(GUIUtil.getUITheme().getBorder()); GUIUtil.setTabToFocusing(designerTextArea); designerTextArea.addFocusListener(textFieldListener); this.add(new JScrollPane(designerTextArea), "wmin 400lp, height 60lp:60lp:, grow 30, wrap para"); @@ -69,6 +70,7 @@ public RocketConfig(OpenRocketDocument d, RocketComponent c, JDialog parent) { revisionTextArea.setLineWrap(true); revisionTextArea.setWrapStyleWord(true); revisionTextArea.setEditable(true); + revisionTextArea.setBorder(GUIUtil.getUITheme().getBorder()); GUIUtil.setTabToFocusing(revisionTextArea); revisionTextArea.addFocusListener(textFieldListener); diff --git a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java index 394daec86e..c37e8ed2b7 100644 --- a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java +++ b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java @@ -1,6 +1,5 @@ package net.sf.openrocket.gui.customexpression; -import java.awt.Color; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -18,6 +17,7 @@ import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +39,7 @@ public class CustomExpressionPanel extends JPanel { private static final Logger log = LoggerFactory.getLogger(CustomExpressionPanel.class); private static final Translator trans = Application.getTranslator(); - + private JPanel expressionSelectorPanel; private OpenRocketDocument doc; @@ -51,6 +51,7 @@ public CustomExpressionPanel(final OpenRocketDocument doc, final JDialog parentD expressionSelectorPanel.setToolTipText(trans.get("customExpressionPanel.lbl.CalcNote")); JScrollPane scroll = new JScrollPane(expressionSelectorPanel); + expressionSelectorPanel.setBorder(GUIUtil.getUITheme().getBorder()); //Border bdr = BorderFactory.createTitledBorder(trans.get("customExpressionPanel.lbl.CustomExpressions")); //scroll.setBorder(bdr); @@ -170,10 +171,10 @@ private void moveExpression(CustomExpression expression, int move) { * A JPanel which configures a single expression */ private class SingleExpression extends JPanel { - + // Convenience method to make the labels consistent private JLabel setLabelStyle(JLabel l) { - l.setBackground(Color.WHITE); + l.setBackground(GUIUtil.getUITheme().getBackgroundColor()); l.setOpaque(true); l.setBorder(BorderFactory.createRaisedBevelBorder()); l.setText(" " + l.getText() + " "); @@ -191,13 +192,13 @@ private SingleExpression(final CustomExpression expression, boolean showUp, bool JLabel symbolLabel = new JLabel(trans.get("customExpression.Symbol") + " :"); JLabel symbol = new JLabel(expression.getSymbol()); symbol = setLabelStyle(symbol); - symbol.setBackground(Color.WHITE); + symbol.setBackground(GUIUtil.getUITheme().getBackgroundColor()); JLabel unitLabel = new JLabel(trans.get("customExpression.Units") + " :"); UnitSelector unitSelector = new UnitSelector(expression.getType().getUnitGroup()); //JLabel unitSelector = new JLabel ( expression.getUnit() ); //unitSelector = setLabelStyle(unitSelector); - //unitSelector.setBackground(Color.WHITE); + //unitSelector.setBackground(GUIUtil.getUITheme().getBackgroundColor()); JButton editButton = new SelectColorButton(Icons.EDIT_EDIT); editButton.setToolTipText(trans.get("customExpression.Units.but.ttip.Edit")); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java index 4bc5fbf836..711dc0f1da 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java @@ -43,7 +43,7 @@ public class AboutDialog extends JDialog { "Daniel Williams (pod support, maintainer)
" + "Joe Pfeiffer (maintainer)
" + "Billy Olsen (maintainer)
" + - "Sibo Van Gool (RASAero file format, 3D OBJ export, maintainer)
" + + "Sibo Van Gool (RASAero file format, 3D OBJ export, dark theme, maintainer)
" + "Justin Hanney (maintainer)
" + "Neil Weinstock (tester, icons, forum support)
" + "H. Craig Miller (tester)

" + @@ -60,6 +60,9 @@ public class AboutDialog extends JDialog { "
" + "See all contributors at
" + href("https://github.com/openrocket/openrocket/graphs/contributors", false, false) + "
" + "
" + + "Thank you to our financial contributors who have provided us with the necessary resources to continue this project:
" + + href("https://opencollective.com/openrocket", true, true) + "
" + + "
" + "OpenRocket utilizes the following libraries:
" + "
" + "MiG Layout" + href("http://www.miglayout.com", true, true) + "
" + @@ -72,7 +75,8 @@ public class AboutDialog extends JDialog { "Simple Logging Facade for Java" + href("http://www.slf4j.org", true, true) + "
" + "Java library for parsing and rendering CommonMark" + href("https://github.com/commonmark/commonmark-java", true, true) + "
" + "RSyntaxTextArea" + href("http://bobbylight.github.io/RSyntaxTextArea", true, true) + "
" + - "Obj" + href("https://github.com/javagl/Obj", true, true) + "
" + + "Darklaf (dark theme)" + href("https://github.com/weisJ/darklaf", true, true) + "
" + + "Obj" + href("https://github.com/javagl/Obj", true, true) + "
" + "
" + "OpenRocket gratefully acknowledges our use of the following databases:
" + "
" + diff --git a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java index 763f873b0c..117384b512 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.dialogs; +import java.awt.Color; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Window; @@ -177,8 +178,9 @@ public static void showExceptionDialog(Window parent, Thread t, Throwable e) { private static void addBugReportInformation(StringBuilder sb) { sb.append("---------- Bug report ----------\n"); sb.append('\n'); - sb.append("Please include a description about what actions you were " + - "performing when the exception occurred:\n"); + Color color = GUIUtil.getUITheme().getDarkWarningColor(); + sb.append(String.format("Please include a description about what actions you were " + + "performing when the exception occurred:\n", color.getRed(), color.getGreen(), color.getBlue())); sb.append("(You can edit text directly in this window)\n"); sb.append('\n'); sb.append("1. \n"); @@ -204,6 +206,7 @@ private static void addSystemInformation(StringBuilder sb) { sbTemp.append("OpenRocket source: " + BuildProperties.getBuildSource() + "\n"); sbTemp.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n"); sbTemp.append("User-defined thrust curves location: " + preferences.getUserThrustCurveFilesAsString() + "\n"); + sbTemp.append("LAF: " + UIManager.getLookAndFeel().getClass().getName() + "\n"); sbTemp.append("JOGL version: " + JoglVersion.getInstance().getImplementationVersion() + "\n"); sbTemp.append("Current default locale: " + Locale.getDefault() + "\n"); sbTemp.append("System properties:\n"); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java index 12cd39195f..2a9d598d7e 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java @@ -150,6 +150,7 @@ public void stateChanged(ChangeEvent e) { warningList = new JList<>(); JScrollPane scrollPane = new JScrollPane(warningList); + warningList.setBorder(GUIUtil.getUITheme().getBorder()); ////Warnings: scrollPane.setBorder(BorderFactory.createTitledBorder(trans.get("componentanalysisdlg.TitledBorder.warnings"))); panel.add(scrollPane, "gap paragraph, spany 4, w 300lp, grow, height :100lp:, wrap"); @@ -646,7 +647,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, } label.setOpaque(true); - label.setBackground(Color.WHITE); + label.setBackground(GUIUtil.getUITheme().getBackgroundColor()); label.setHorizontalAlignment(SwingConstants.LEFT); if ((row < 0) || (row >= data.size())) @@ -693,7 +694,6 @@ public Component getTableCellRendererComponent(JTable table, Object value, private class DragCellRenderer extends CustomCellRenderer { private static final long serialVersionUID = 1L; - public DragCellRenderer() { super(dragData, 3); } @@ -712,6 +712,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, float val = 1.0f; label.setBackground(Color.getHSBColor(hue, sat, val)); + label.setForeground(Color.BLACK); } if ((row < 0) || (row >= dragData.size())) diff --git a/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java index 4a2e817940..26bbd5ddfc 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java @@ -67,7 +67,7 @@ public class DebugLogDialog extends JDialog { private static final int POLL_TIME = 250; private static final String STACK_TRACE_MARK = "\uFF01"; private static final Translator trans = Application.getTranslator(); - + private static final EnumMap backgroundColors = new EnumMap(LogLevel.class); static { for (LogLevel l : LogLevel.values()) { @@ -343,6 +343,7 @@ public void valueChanged(ListSelectionEvent e) { bottomPanel.add(new JLabel(trans.get("debuglogdlg.lbl.Stacktrace")), "wrap rel"); stackTraceLabel = new JTextArea(8, 80); stackTraceLabel.setEditable(false); + stackTraceLabel.setBorder(GUIUtil.getUITheme().getBorder()); GUIUtil.changeFontSize(stackTraceLabel, -2); bottomPanel.add(new JScrollPane(stackTraceLabel), "grow, pushy 200, growprioy 200"); @@ -503,7 +504,7 @@ public Component getTableCellRendererComponent(JTable table1, Object value, bool if (STACK_TRACE_MARK.equals(value)) { fg = Color.RED; } else { - fg = table1.getForeground(); + fg = Color.BLACK; } bg = backgroundColors.get(buffer.get(row).getLevel()); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java index 335fe25b81..29e74dcde4 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java @@ -3,6 +3,7 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.util.BetterListCellRenderer; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.logging.Error; import net.sf.openrocket.logging.ErrorSet; import net.sf.openrocket.logging.Warning; @@ -15,7 +16,6 @@ import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.ListSelectionModel; -import java.awt.Color; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -25,11 +25,12 @@ */ @SuppressWarnings("serial") public abstract class ErrorWarningDialog { + public static void showErrorsAndWarnings(Component parent, Object message, String title, ErrorSet errors, WarningSet warnings) { JPanel content = new JPanel(new MigLayout("ins 0, fillx")); StyledLabel label = new StyledLabel("Errors"); - label.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + label.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); content.add(label, "wrap, gaptop 15lp"); Error[] e = errors.toArray(new Error[0]); @@ -37,6 +38,7 @@ public static void showErrorsAndWarnings(Component parent, Object message, Strin errorList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); errorList.setCellRenderer(new ErrorListCellRenderer()); JScrollPane errorPane = new JScrollPane(errorList); + errorList.setBorder(GUIUtil.getUITheme().getBorder()); content.add(errorPane, "wrap, growx"); // Deselect items if clicked on blank region @@ -58,6 +60,7 @@ public void mousePressed(MouseEvent e) { final JList warningList = new JList<>(w); warningList.setCellRenderer(new BetterListCellRenderer()); JScrollPane warningPane = new JScrollPane(warningList); + warningList.setBorder(GUIUtil.getUITheme().getBorder()); content.add(warningPane, "wrap, growx"); // Deselect items if clicked on blank region @@ -84,9 +87,9 @@ public Component getListCellRendererComponent(JList list, Object value, int i // Text color if (isSelected) { - label.setForeground(Color.WHITE); + label.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor()); } else { - label.setForeground(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + label.setForeground(GUIUtil.getUITheme().getDarkWarningColor()); } return label; diff --git a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java index 006222aedf..cf84659ca1 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java @@ -1,16 +1,15 @@ package net.sf.openrocket.gui.dialogs; import java.awt.Component; -import java.awt.Desktop; import java.awt.Dimension; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -75,6 +74,7 @@ public UpdateInfoDialog(UpdateInfo info) { // Release information box final JTextPane textPane = new JTextPane(); + textPane.setBorder(BorderFactory.createLineBorder(GUIUtil.getUITheme().getTextColor())); textPane.setEditable(false); textPane.setContentType("text/html"); textPane.setMargin(new Insets(10, 10, 40, 10)); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java index f856bd8a01..ec2a0a34a7 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java @@ -2,23 +2,25 @@ import java.awt.Component; +import javax.swing.BorderFactory; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import net.sf.openrocket.gui.util.BetterListCellRenderer; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.logging.Warning; import net.sf.openrocket.logging.WarningSet; @SuppressWarnings("serial") public abstract class WarningDialog { - public static void showWarnings(Component parent, Object message, String title, WarningSet warnings) { Warning[] w = warnings.toArray(new Warning[0]); final JList list = new JList(w); list.setCellRenderer(new BetterListCellRenderer()); JScrollPane pane = new JScrollPane(list); + pane.setBorder(GUIUtil.getUITheme().getBorder()); JOptionPane.showMessageDialog(parent, new Object[] { message, pane }, title, JOptionPane.WARNING_MESSAGE); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java index ee295ee9fc..88222ec545 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java @@ -1,6 +1,5 @@ package net.sf.openrocket.gui.dialogs.flightconfiguration; -import java.awt.Color; import java.awt.Dialog; import java.awt.Window; import java.awt.event.ActionEvent; @@ -25,7 +24,7 @@ public class RenameConfigDialog extends JDialog { private static final long serialVersionUID = -5423008694485357248L; private static final Translator trans = Application.getTranslator(); - + public RenameConfigDialog(final Window parent, final Rocket rocket, final FlightConfigurationId fcid) { super(parent, trans.get("RenameConfigDialog.title"), Dialog.ModalityType.APPLICATION_MODAL); @@ -47,7 +46,7 @@ public void actionPerformed(ActionEvent e) { RenameConfigDialog.this.setVisible(false); } }); - panel.add(okButton); + panel.add(okButton, "growx"); JButton renameToDefaultButton = new SelectColorButton(trans.get("RenameConfigDialog.but.reset")); renameToDefaultButton.addActionListener(new ActionListener() { @@ -57,7 +56,7 @@ public void actionPerformed(ActionEvent e) { RenameConfigDialog.this.setVisible(false); } }); - panel.add(renameToDefaultButton); + panel.add(renameToDefaultButton, "growx"); JButton cancel = new SelectColorButton(trans.get("button.cancel")); cancel.addActionListener(new ActionListener() { @@ -66,14 +65,14 @@ public void actionPerformed(ActionEvent e) { RenameConfigDialog.this.setVisible(false); } }); - panel.add(cancel, "wrap para"); + panel.add(cancel, "growx, wrap para"); // {motors} & {manufacturers} info String text = "" + CommonStrings.dagger + " " + trans.get("RenameConfigDialog.lbl.infoMotors") + trans.get("RenameConfigDialog.lbl.infoManufacturers") + trans.get("RenameConfigDialog.lbl.infoCombination"); StyledLabel info = new StyledLabel(text, -2); - info.setFontColor(Color.DARK_GRAY); + info.setFontColor(GUIUtil.getUITheme().getDimTextColor()); panel.add(info, "spanx, growx, wrap"); this.add(panel); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java index dcbf04aa06..de63acf0e7 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java @@ -37,14 +37,13 @@ @SuppressWarnings("serial") class MotorInformationPanel extends JPanel { - + private static final Translator trans = Application.getTranslator(); + private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50; private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12; - private static final Color NO_COMMENT_COLOR = Color.GRAY; - private static final Color WITH_COMMENT_COLOR = Color.BLACK; - - private static final Translator trans = Application.getTranslator(); + private static final Color NO_COMMENT_COLOR = GUIUtil.getUITheme().getDimTextColor(); + private static final Color WITH_COMMENT_COLOR = GUIUtil.getUITheme().getTextColor(); // Motors in set private List selectedMotorSet; @@ -190,7 +189,9 @@ public MotorInformationPanel() { changeLabelFont(plot.getDomainAxis(), -2); //// Thrust curve: - chart.setTitle(new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont())); + TextTitle title = new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont()); + title.setPaint(GUIUtil.getUITheme().getTextColor()); + chart.setTitle(title); chart.setBackgroundPaint(this.getBackground()); plot.setBackgroundPaint(Color.WHITE); plot.setDomainGridlinePaint(Color.LIGHT_GRAY); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java b/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java index 64b106336b..8718a48a60 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java @@ -1,6 +1,5 @@ package net.sf.openrocket.gui.dialogs.optimization; -import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.util.Enumeration; @@ -17,6 +16,7 @@ import net.sf.openrocket.gui.components.BasicTree; import net.sf.openrocket.gui.main.ComponentIcons; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier; import net.sf.openrocket.rocketcomponent.Rocket; @@ -37,7 +37,7 @@ public class SimulationModifierTree extends BasicTree { private final List selectedModifiers; private static final Translator trans = Application.getTranslator(); - + /** * Sole constructor. * @@ -151,11 +151,13 @@ public Component getTreeCellRendererComponent( // Set icon (for rocket components, null for others) setIcon(ComponentIcons.getSmallIcon(object.getClass())); - + + // By default, set background to transparent + setOpaque(false); // Set text color/style if (object instanceof RocketComponent) { - setForeground(Color.GRAY); + setForeground(GUIUtil.getUITheme().getDimTextColor()); setFont(componentFont); // Set tooltip @@ -169,21 +171,24 @@ public Component getTreeCellRendererComponent( this.setToolTipText(null); } } else if (object instanceof String) { - setForeground(Color.GRAY); + setForeground(GUIUtil.getUITheme().getDimTextColor()); setFont(stringFont); } else if (object instanceof SimulationModifier) { if (selectedModifiers.contains(object)) { - setForeground(Color.GRAY); + setForeground(GUIUtil.getUITheme().getDimTextColor()); + setFont(stringFont); } else { if (tree.getSelectionRows() != null && IntStream.of(tree.getSelectionRows()).anyMatch(r -> r == row)) { - setForeground(Color.WHITE); + setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor()); + setBackground(GUIUtil.getUITheme().getTextSelectionBackgroundColor()); + setOpaque(true); } else { - setForeground(Color.BLACK); + setForeground(GUIUtil.getUITheme().getTextColor()); } + setFont(modifierFont); } - setFont(modifierFont); setText(((SimulationModifier) object).getName()); setToolTipText(((SimulationModifier) object).getDescription()); } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java index 4457b97867..98e993c6f7 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java @@ -20,9 +20,12 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; +import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -31,6 +34,8 @@ import net.sf.openrocket.communication.UpdateInfo; import net.sf.openrocket.communication.UpdateInfoRetriever; import net.sf.openrocket.communication.UpdateInfoRetriever.ReleaseStatus; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.IntegerModel; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; @@ -40,6 +45,7 @@ import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.PreferencesExporter; import net.sf.openrocket.gui.util.PreferencesImporter; +import net.sf.openrocket.gui.util.UITheme; import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.startup.Preferences; @@ -50,13 +56,17 @@ @SuppressWarnings("serial") public class GeneralPreferencesPanel extends PreferencesPanel { + private final UITheme.Theme currentTheme; + private final int currentFontSize; public GeneralPreferencesPanel(PreferencesDialog parent) { super(parent, new MigLayout("fillx, ins 30lp n n n")); - + + this.currentTheme = GUIUtil.getUITheme(); + this.currentFontSize = preferences.getUIFontSize(); //// Language selector - Locale userLocale = null; + Locale userLocale; { String locale = preferences.getString("locale", null); userLocale = L10N.toLocale(locale); @@ -79,6 +89,7 @@ public GeneralPreferencesPanel(PreferencesDialog parent) { @SuppressWarnings("unchecked") public void actionPerformed(ActionEvent e) { Named selection = (Named) languageCombo.getSelectedItem(); + if (selection == null) return; Locale l = selection.get(); preferences.putString(Preferences.USER_LOCAL, l == null ? null : l.toString()); } @@ -86,8 +97,64 @@ public void actionPerformed(ActionEvent e) { this.add(new JLabel(trans.get("generalprefs.lbl.language")), "gapright para"); this.add(languageCombo, "wrap rel, growx, sg combos"); - this.add(new StyledLabel(trans.get("generalprefs.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2"); - + this.add(new StyledLabel(trans.get("generalprefs.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap rel"); + + //// UI Theme + UITheme.Theme currentTheme = GUIUtil.getUITheme(); + List> themes = new ArrayList<>(); + for (UITheme.Theme t : UITheme.Themes.values()) { + themes.add(new Named<>(t, t.getDisplayName())); + } + Collections.sort(themes); + + final JComboBox themesCombo = new JComboBox<>(themes.toArray()); + for (int i = 0; i < themes.size(); i++) { + if (Utils.equals(currentTheme, themes.get(i).get())) { + themesCombo.setSelectedIndex(i); + } + } + + this.add(new JLabel(trans.get("generalprefs.lbl.UITheme")), "gapright para"); + this.add(themesCombo, "wrap, growx, sg combos"); + + //// Font size + this.add(new JLabel(trans.get("generalprefs.lbl.FontSize")), "gapright para"); + final IntegerModel fontSizeModel = new IntegerModel(preferences, "UIFontSize", 5, 25); + final JSpinner fontSizeSpinner = new JSpinner(fontSizeModel.getSpinnerModel()); + fontSizeSpinner.setEditor(new SpinnerEditor(fontSizeSpinner)); + this.add(fontSizeSpinner, "growx, wrap"); + + //// You need to restart OpenRocket for the theme change to take effect. + final JLabel lblRestartORTheme = new JLabel(); + lblRestartORTheme.setForeground(GUIUtil.getUITheme().getDarkWarningColor()); + this.add(lblRestartORTheme, "spanx, wrap para*2, growx"); + + fontSizeSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (fontSizeModel.getValue() == currentFontSize) { + lblRestartORTheme.setText(""); + return; + } + lblRestartORTheme.setText(trans.get("generalprefs.lbl.themeRestartOR")); + } + }); + themesCombo.addActionListener(new ActionListener() { + @Override + @SuppressWarnings("unchecked") + public void actionPerformed(ActionEvent e) { + Named selection = (Named) themesCombo.getSelectedItem(); + if (selection == null) return; + UITheme.Theme t = selection.get(); + if (t == currentTheme) { + lblRestartORTheme.setText(""); + return; + } + preferences.setUITheme(t); + lblRestartORTheme.setText(trans.get("generalprefs.lbl.themeRestartOR")); + } + }); + //// User-defined thrust curves: this.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap"); final JTextField field = new JTextField(); @@ -180,8 +247,9 @@ public void actionPerformed(ActionEvent e) { this.add(button, "wrap"); //// Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket. - DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false); - desc.setBackground(getBackground()); + DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -1.5f, false); + desc.setBackground(GUIUtil.getUITheme().getBackgroundColor()); + desc.setForeground(GUIUtil.getUITheme().getTextColor()); this.add(desc, "spanx, growx, wrap 40lp"); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java index 459c6f7477..2cb3869544 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java @@ -23,6 +23,7 @@ import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.models.atmosphere.ExtendedISAModel; import net.sf.openrocket.simulation.SimulationOptions; import net.sf.openrocket.unit.UnitGroup; @@ -43,7 +44,7 @@ public LaunchPreferencesPanel() { StyledLabel warning = new StyledLabel(String.format( "%s", trans.get("pref.dlg.lbl.launchWarning")), 0.5f, StyledLabel.Style.BOLD); - warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + warning.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip")); add(warning, "spanx, growx 0, gapbottom para, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java index 92bb729b38..051e312ac6 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java @@ -17,6 +17,7 @@ import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.simulation.RK4SimulationStepper; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.GeodeticComputationStrategy; @@ -24,7 +25,7 @@ public class SimulationPreferencesPanel extends PreferencesPanel { private static final long serialVersionUID = 7983195730016979888L; - + /* * private GeodeticComputationStrategy geodeticComputation = * GeodeticComputationStrategy.SPHERICAL; @@ -41,7 +42,7 @@ public SimulationPreferencesPanel() { confirmDelete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - preferences.setAutoRunSimulations(confirmDelete.isSelected()); + preferences.setConfirmSimDeletion(confirmDelete.isSelected()); } }); this.add(confirmDelete, "wrap, growx, sg combos "); @@ -84,7 +85,7 @@ public void actionPerformed(ActionEvent e) { StyledLabel warning = new StyledLabel(String.format( "%s", trans.get("pref.dlg.lbl.launchWarning")), 0, StyledLabel.Style.BOLD); - warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + warning.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip")); subsub.add(warning, "spanx, wrap para"); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java index 65df4d52f5..a9c2eacf40 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java @@ -10,6 +10,7 @@ import net.sf.openrocket.gui.figure3d.geometry.Geometry; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.ExternalComponent; @@ -80,7 +81,7 @@ public void renderComponent(GL2 gl, Geometry geom, float alpha) { if (defaultColorCache.containsKey(c.getClass())) { figureColor = defaultColorCache.get(c.getClass()); } else { - figureColor = Application.getPreferences().getDefaultColor(c.getClass()); + figureColor = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass()); defaultColorCache.put(c.getClass(), figureColor); } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java index 578817921c..41cac74ba6 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java @@ -42,9 +42,9 @@ public void init(GLAutoDrawable drawable) { gl.glLightModelfv(GL2ES1.GL_LIGHT_MODEL_AMBIENT, new float[] { 0, 0, 0 }, 0); - float amb = 0.3f; - float dif = 1.0f - amb; - float spc = 1.0f; + float amb = 0.4f; + float dif = 0.65f; + float spc = 0.65f; gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT, new float[] { amb, amb, amb, 1 }, 0); gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_DIFFUSE, new float[] { dif, dif, dif, 1 }, 0); gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR, new float[] { spc, spc, spc, 1 }, 0); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java index 38769db88a..cc65d95ae6 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java @@ -34,6 +34,7 @@ import javax.swing.SwingUtilities; import javax.swing.event.MouseInputAdapter; +import net.sf.openrocket.gui.util.GUIUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +65,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(RocketFigure3d.class); - + static { //this allows the GL canvas and things like the motor selection //drop down to z-order themselves. @@ -288,7 +289,9 @@ public void display(final GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); GLU glu = new GLU(); - gl.glClearColor(1, 1, 1, 1); + Color backgroundColor = GUIUtil.getUITheme().getBackgroundColor(); + gl.glClearColor(backgroundColor.getRed()/255f, backgroundColor.getGreen()/255f, + backgroundColor.getBlue()/255f, backgroundColor.getAlpha()/255f); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); setupView(gl, glu); @@ -315,8 +318,9 @@ public void run() { } pickPoint = null; - - gl.glClearColor(1, 1, 1, 1); + + gl.glClearColor(backgroundColor.getRed()/255f, backgroundColor.getGreen()/255f, + backgroundColor.getBlue()/255f, backgroundColor.getAlpha()/255f); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glEnable(GL.GL_MULTISAMPLE); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java index a97ea476fa..92cdb5b6c7 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java @@ -16,6 +16,8 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import java.util.Collections; + public class FinRenderer { private GLUtessellator tess = GLU.gluNewTess(); @@ -96,12 +98,11 @@ public void combine(double[] coords, Object[] data, float[] weight, Object[] out GLU.gluTessBeginPolygon(tess, null); GLU.gluTessBeginContour(tess); gl.glNormal3f(0, 0, 1); - for (int i = tabPoints.length - 1; i >= 0; i--) { - Coordinate c = tabPoints[i]; - double[] p = new double[]{c.x, c.y + finSet.getBodyRadius(), - c.z + finSet.getThickness() / 2.0}; - GLU.gluTessVertex(tess, p, 0, p); - } + for (Coordinate c : tabPoints) { + double[] p = new double[]{c.x, c.y + finSet.getBodyRadius(), + c.z + finSet.getThickness() / 2.0}; + GLU.gluTessVertex(tess, p, 0, p); + } GLU.gluTessEndContour(tess); GLU.gluTessEndPolygon(tess); } @@ -125,7 +126,8 @@ public void combine(double[] coords, Object[] data, float[] weight, Object[] out GLU.gluTessBeginPolygon(tess, null); GLU.gluTessBeginContour(tess); gl.glNormal3f(0, 0, -1); - for (Coordinate c : tabPoints) { + for (int i = tabPoints.length - 1; i >= 0; i--) { + Coordinate c = tabPoints[i]; double[] p = new double[]{c.x, c.y + finSet.getBodyRadius(), c.z - finSet.getThickness() / 2.0}; GLU.gluTessVertex(tess, p, 0, p); @@ -163,7 +165,7 @@ public void combine(double[] coords, Object[] data, float[] weight, Object[] out if (!(finSet instanceof EllipticalFinSet)) gl.glShadeModel(GLLightingFunc.GL_FLAT); gl.glBegin(GL.GL_TRIANGLE_STRIP); - for (int i = 0; i <= tabPoints.length; i++) { + for (int i = tabPoints.length; i >= 0; i--) { Coordinate c = tabPoints[i % tabPoints.length]; // if ( i > 1 ){ Coordinate c2 = tabPoints[(i - 1 + tabPoints.length) diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java index 054ca02f31..d39eabcd88 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java @@ -123,7 +123,7 @@ final class TransitionRenderer { private TransitionRenderer() { } - static final void drawTransition(final GL2 gl, final Transition tr, + static void drawTransition(final GL2 gl, final Transition tr, final int slices, final int stacks, final double offsetRadius) { double da, r, dzBase; @@ -131,14 +131,16 @@ static final void drawTransition(final GL2 gl, final Transition tr, int i; da = 2.0f * Math.PI / slices; - dzBase = (double) tr.getLength() / stacks; + dzBase = tr.getLength() / stacks; double ds = 1.0f / slices; z = 0.0f; - r = (double) tr.getForeRadius(); while (z < tr.getLength()) { double t = z / tr.getLength(); + if (tr.getForeRadius() > tr.getAftRadius()) { + t = 1 - t; + } double dz = t < 0.025 ? dzBase / 8.0 : dzBase; double zNext = Math.min(z + dz, tr.getLength()); 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 76af038230..d43c79ef60 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java @@ -371,9 +371,9 @@ public static void main(String args[]) throws Exception { guiModule.startLoader(); - // Set the best available look-and-feel - log.info("Setting best LAF"); - GUIUtil.setBestLAF(); + // Set the look-and-feel + log.info("Setting LAF"); + GUIUtil.applyLAF(); // Load defaults ((SwingPreferences) Application.getPreferences()).loadDefaultUnits(); diff --git a/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java b/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java index 14a8a677d3..77361260ff 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java @@ -1,5 +1,7 @@ package net.sf.openrocket.gui.figureelements; +import net.sf.openrocket.gui.util.GUIUtil; + import java.awt.Color; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; @@ -16,7 +18,7 @@ public class CGCaret extends Caret { private static final float RADIUS = 7; private static Area caret = null; - + /** * Create a new CGCaret at the specified coordinates. */ @@ -56,7 +58,7 @@ protected Area getCaret() { */ @Override protected Color getColor() { - return Color.BLUE; + return GUIUtil.getUITheme().getCGColor(); } } diff --git a/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java b/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java index 09e9cceba0..a5b66a2333 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java @@ -1,5 +1,7 @@ package net.sf.openrocket.gui.figureelements; +import net.sf.openrocket.gui.util.GUIUtil; + import java.awt.Color; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; @@ -15,7 +17,7 @@ public class CPCaret extends Caret { private static final float RADIUS = 7; private static Area caret = null; - + /** * Create a new CPCaret at the specified coordinates. */ @@ -51,6 +53,6 @@ protected Area getCaret() { */ @Override protected Color getColor() { - return Color.RED; + return GUIUtil.getUITheme().getCPColor(); } } diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java index 1af88b65b5..1836ae6442 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java @@ -3,7 +3,6 @@ import static net.sf.openrocket.util.Chars.ALPHA; import static net.sf.openrocket.util.Chars.THETA; -import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; @@ -11,6 +10,7 @@ import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.logging.Warning; import net.sf.openrocket.logging.WarningSet; @@ -176,7 +176,7 @@ private void drawMainInfo() { GlyphVector massLineWithoutMotors = createText(massTextWithoutMotors); - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getTextColor()); g2.drawGlyphVector(name, x1, y1); g2.drawGlyphVector(lengthLine, x1, y1+line); @@ -234,7 +234,7 @@ private void drawStabilityInfo() { unitWidth = unitWidth + spaceWidth; stabUnitWidth = stabUnitWidth + spaceWidth; - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getTextColor()); // Draw the stability, CG & CP values (and units) g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1); @@ -261,7 +261,7 @@ private void drawStabilityInfo() { atPos = (float)(x2 - atTextRect.getWidth()); } - g2.setColor(Color.GRAY); + g2.setColor(GUIUtil.getUITheme().getDimTextColor()); g2.drawGlyphVector(atText, atPos, y1 + 3*line); } @@ -411,7 +411,7 @@ private void drawWarnings() { float y = y2 - line * (texts.length-1); - g2.setColor(Color.RED); + g2.setColor(GUIUtil.getUITheme().getWarningColor()); for (GlyphVector v: texts) { Rectangle2D rect = v.getVisualBounds(); @@ -427,7 +427,7 @@ private void drawFlightInformation() { if (calculatingData) { //// Calculating... GlyphVector calculating = createText(trans.get("RocketInfo.Calculating")); - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getTextColor()); g2.drawGlyphVector(calculating, x1, (float)(y2-height)); } } @@ -485,11 +485,10 @@ private double drawFlightData() { width += 5; if (!calculatingData) - g2.setColor(new Color(0,0,127)); + g2.setColor(GUIUtil.getUITheme().getFlightDataTextActiveColor()); else - g2.setColor(new Color(0,0,127,127)); + g2.setColor(GUIUtil.getUITheme().getFlightDataTextInactiveColor()); - g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line)); g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line)); g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2)); @@ -502,7 +501,7 @@ private double drawFlightData() { } private synchronized void updateFontSizes() { - float size = Application.getPreferences().getRocketInfoFontSize(); + float size = ((SwingPreferences) Application.getPreferences()).getRocketInfoFontSize(); // No change necessary as the font is the same size, just use the existing version if (font.getSize2D() == size) { return; diff --git a/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java b/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java index 0d9e38155c..7828dfd8db 100644 --- a/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java +++ b/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.help.tours; +import java.awt.Color; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -11,6 +12,7 @@ import javax.swing.text.html.StyleSheet; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.util.BugException; /** @@ -130,6 +132,9 @@ private StyleSheet loadStyleSheet() throws IOException { try { StyleSheet ss = new StyleSheet(); + Color textColor = GUIUtil.getUITheme().getTextColor(); + ss.addRule(String.format("p { color: rgb(%d, %d, %d, %d)", + textColor.getRed(), textColor.getGreen(), textColor.getBlue(), textColor.getAlpha())); InputStreamReader reader = new InputStreamReader(in, "UTF-8"); ss.loadRules(reader, null); return ss; diff --git a/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java b/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java index 07cffdcc69..561ee518b9 100644 --- a/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java +++ b/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java @@ -12,6 +12,7 @@ import javax.swing.text.html.StyleSheet; import net.sf.openrocket.gui.components.ImageDisplayComponent; +import net.sf.openrocket.gui.util.GUIUtil; /** * Component that displays a single slide, with the image on top and @@ -28,7 +29,7 @@ public class SlideShowComponent extends JSplitPane { private final ImageDisplayComponent imageDisplay; private final JEditorPane textPane; - + public SlideShowComponent() { super(VERTICAL_SPLIT); @@ -44,6 +45,7 @@ public SlideShowComponent() { textPane.setPreferredSize(new Dimension(WIDTH, HEIGHT_TEXT)); JScrollPane scrollPanel = new JScrollPane(textPane); + textPane.setBorder(GUIUtil.getUITheme().getBorder()); this.setRightComponent(scrollPanel); this.setResizeWeight(((double) HEIGHT_IMAGE) / (HEIGHT_IMAGE + HEIGHT_TEXT)); diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 194dac8214..a216f7d3cc 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -579,12 +579,12 @@ public void actionPerformed(ActionEvent e) { editMenu.addSeparator(); - JMenu subMenu = new JMenu(trans.get("RocketActions.Select")); - editMenu.add(subMenu); + JMenu selectSubMenu = new JMenu(trans.get("RocketActions.Select")); + editMenu.add(selectSubMenu); item = new JMenuItem(actions.getSelectSameColorAction()); - subMenu.add(item); + selectSubMenu.add(item); item = new JMenuItem(actions.getDeselectAllAction()); - subMenu.add(item); + selectSubMenu.add(item); editMenu.addSeparator(); diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java index 69869526c6..1f194ce05c 100644 --- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java +++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java @@ -4,6 +4,7 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.Rectangle; +import java.awt.Font; import java.awt.event.ActionEvent; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -209,7 +210,7 @@ public ComponentAddButtons(OpenRocketDocument document, TreeSelectionModel model for (col = 0; col < buttons[row].length; col++) { buttons[row][col].setMinimumSize(d); buttons[row][col].setPreferredSize(d); - buttons[row][col].getComponent(0).validate(); + buttons[row][col].validate(); } } @@ -315,29 +316,32 @@ public ComponentButton(String text) { * The label may contain "\n" as a newline. */ public ComponentButton(String text, Icon enabled, Icon disabled) { - super(); - setLayout(new MigLayout("fill, flowy, insets 0, gap 0", "", "")); - - add(new JLabel(), "push, sizegroup spacing"); - - // Add Icon - if (enabled != null) { - JLabel label = new JLabel(enabled); - if (disabled != null) - label.setDisabledIcon(disabled); - add(label, "growx"); + super(text, enabled); + + setVerticalTextPosition(SwingConstants.BOTTOM); // this will put the text below the icon + setHorizontalTextPosition(SwingConstants.CENTER); // this will center the text horizontally beneath the icon + //setIconTextGap(0); // this is optional, it sets the gap between the icon and the text + + // set the disabled icon if it is not null + if (disabled != null) { + setDisabledIcon(disabled); } - - // Add labels - String[] l = text.split("\n"); - for (int i = 0; i < l.length; i++) { - add(new StyledLabel(l[i], SwingConstants.CENTER, -2.0f), "growx"); + + setHorizontalAlignment(SwingConstants.CENTER); // this will center the button itself in its parent component + + // if you have multiline text, you could use html to format it + if (text != null && text.contains("\n")) { + text = "" + text.replace("\n", "
") + ""; + setText(text); + } + + // Initialize enabled status + valueChanged(null); + + // Attach a tree selection listener if selection model is not null + if (selectionModel != null) { + selectionModel.addTreeSelectionListener(this); } - - add(new JLabel(), "push, sizegroup spacing"); - - valueChanged(null); // Update enabled status - selectionModel.addTreeSelectionListener(this); } diff --git a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java index b30a605ce3..f4e7c274dc 100644 --- a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java @@ -61,13 +61,13 @@ public DesignPanel(final BasicFrame parent, final OpenRocketDocument document, f // Remove JTree key events that interfere with menu accelerators InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), "none"); // Highlight all child components of a stage/rocket/podset when it is selected tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @@ -174,6 +174,7 @@ public void valueChanged(TreeSelectionEvent e) { // Place tree inside scroll pane JScrollPane scroll = new JScrollPane(tree); + tree.setBorder(GUIUtil.getUITheme().getBorder()); panel.add(scroll, "spany, wmin 140px, grow, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index 2bfa6e9a65..aa90f9eafc 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -4,6 +4,7 @@ import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; +import java.io.Serial; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -13,15 +14,13 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; -import javax.swing.JCheckBox; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.KeyStroke; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; -import net.miginfocom.swing.MigLayout; 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; @@ -34,7 +33,6 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Pair; import org.slf4j.Logger; @@ -59,6 +57,7 @@ public class RocketActions { Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); public static final KeyStroke EDIT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); + public static final KeyStroke DELETE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); private final OpenRocketDocument document; private final Rocket rocket; @@ -67,8 +66,6 @@ public class RocketActions { private final SimulationPanel simulationPanel; - private final RocketAction deleteComponentAction; - private final RocketAction deleteSimulationAction; private final RocketAction deleteAction; private final RocketAction cutAction; private final RocketAction copyAction; @@ -94,8 +91,6 @@ public RocketActions(OpenRocketDocument document, DocumentSelectionModel selecti // Add action also to updateActions() this.deleteAction = new DeleteAction(); - this.deleteComponentAction = new DeleteComponentAction(); - this.deleteSimulationAction = new DeleteSimulationAction(); this.cutAction = new CutAction(); this.copyAction = new CopyAction(); this.pasteAction = new PasteAction(); @@ -107,10 +102,15 @@ public RocketActions(OpenRocketDocument document, DocumentSelectionModel selecti this.moveUpAction = new MoveUpAction(); this.moveDownAction = new MoveDownAction(); - OpenRocketClipboard.addClipboardListener(this.pasteAction); + OpenRocketClipboard.addClipboardListener(new ClipboardListener() { + @Override + public void clipboardChanged() { + updateActions(); + } + }); updateActions(); - // Update all actions when tree selection or rocket changes + // Update all actions when tree selection, simulation selection or rocket changes selectionModel.addDocumentSelectionListener(new DocumentSelectionListener() { @Override @@ -118,6 +118,12 @@ public void valueChanged(int changeType) { updateActions(); } }); + simulationPanel.addSimulationTableListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + updateActions(); + } + }); document.getRocket().addComponentChangeListener(new ComponentChangeListener() { @Override public void componentChanged(ComponentChangeEvent e) { @@ -130,12 +136,13 @@ public void componentChanged(ComponentChangeEvent e) { * Update the state of all of the actions. */ private void updateActions() { - deleteAction.clipboardChanged(); + simulationPanel.updateActions(); + editAction.clipboardChanged(); cutAction.clipboardChanged(); copyAction.clipboardChanged(); pasteAction.clipboardChanged(); + deleteAction.clipboardChanged(); duplicateAction.clipboardChanged(); - editAction.clipboardChanged(); selectSameColorAction.clipboardChanged(); deselectAllAction.clipboardChanged(); scaleAction.clipboardChanged(); @@ -143,16 +150,8 @@ private void updateActions() { moveDownAction.clipboardChanged(); } - - - public Action getDeleteComponentAction() { - return deleteAction; - } - public Action getDeleteSimulationAction() { - return deleteAction; - } public Action getDeleteAction() { return deleteAction; @@ -313,7 +312,7 @@ private boolean isCopyable(RocketComponent c) { } private boolean isCopyable(List components) { - if (components == null || components.size() == 0) return false; + if (components == null || components.isEmpty()) return false; for (RocketComponent component : components) { if (!isCopyable(component)) return false; } @@ -409,43 +408,14 @@ private void fillInMissingSelections(List selections) { private boolean isSimulationSelected() { Simulation[] selection = selectionModel.getSelectedSimulations(); - return (selection != null && selection.length > 0); - } - - - - private boolean verifyDeleteSimulation() { - 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("RocketActions.checkbox.Donotaskmeagain")); - panel.add(dontAsk,"wrap"); - //// You can change the default operation in the preferences. - panel.add(new StyledLabel(trans.get("RocketActions.lbl.Youcanchangedefop"),-2)); - - int ret = JOptionPane.showConfirmDialog( - parentFrame, - new Object[] { - //// Delete the selected simulations? - trans.get("RocketActions.showConfirmDialog.lbl1"), - //// This operation cannot be undone. - trans.get("RocketActions.showConfirmDialog.lbl2"), - "", - panel }, - //// Delete simulations - trans.get("RocketActions.showConfirmDialog.title"), - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE); - if (ret != JOptionPane.OK_OPTION) - return false; - - if (dontAsk.isSelected()) { - Application.getPreferences().putBoolean(Preferences.CONFIRM_DELETE_SIMULATION, false); + if (selection != null && selection.length > 0) { + List components = selectionModel.getSelectedComponents(); + if (components != null && !components.isEmpty()) { + throw new IllegalStateException("Both simulation and component selected"); } + return true; } - - return true; + return false; } @@ -517,130 +487,60 @@ private abstract class RocketAction extends AbstractAction implements ClipboardL public abstract void clipboardChanged(); } - /** - * Action that deletes the selected component. + * Action to edit the currently selected component. */ - private class DeleteComponentAction extends RocketAction { + private class EditAction extends RocketAction { + @Serial private static final long serialVersionUID = 1L; - public DeleteComponentAction() { - //// Delete - this.putValue(NAME, trans.get("RocketActions.DelCompAct.Delete")); - //// Delete the selected component. - this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DelCompAct.ttip.Delete")); - this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); -// this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); - this.putValue(SMALL_ICON, Icons.EDIT_DELETE); + 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); + 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 = new ArrayList<>(selectionModel.getSelectedComponents()); - if (components.size() == 0) return; - components.sort(Comparator.comparing(c -> c.getParent().getChildPosition(c))); - - if (components.size() == 1) { - document.addUndoPosition("Delete " + components.get(0).getComponentName()); + if (isSimulationSelected()) { + simulationPanel.getEditSimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); } else { - document.addUndoPosition("Delete components"); - } - - for (RocketComponent component : components) { - deleteComponent(component); + editComponents(); + // I wouldn't switch to the design tab here, because the user may be editing in the rocket view window } } - private void deleteComponent(RocketComponent component) { - if (isDeletable(component)) { - ComponentConfigDialog.disposeDialog(); - - try { - component.getRocket().removeComponentChangeListener(ComponentConfigDialog.getDialog()); + private void editComponents() { + List components = selectionModel.getSelectedComponents(); - delete(component); - } catch (IllegalStateException ignored) { } + if (components == null || components.isEmpty()) { + return; } - } - - @Override - public void clipboardChanged() { - this.setEnabled(isDeletable(selectionModel.getSelectedComponent())); - } - } - - - /** - * Action that deletes the selected component. - */ - private class DeleteSimulationAction extends RocketAction { - private static final long serialVersionUID = 1L; - - public DeleteSimulationAction() { - //// Delete - this.putValue(NAME, trans.get("RocketActions.DelSimuAct.Delete")); - //// Delete the selected simulation. - this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DelSimuAct.ttip.Delete")); - this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); -// this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); - this.putValue(SMALL_ICON, Icons.EDIT_DELETE); - clipboardChanged(); - } + if (ComponentConfigDialog.isDialogVisible()) + ComponentConfigDialog.disposeDialog(); - @Override - public void actionPerformed(ActionEvent e) { - Simulation[] sims = selectionModel.getSelectedSimulations(); - if (sims.length > 0) { - if (verifyDeleteSimulation()) { - for (Simulation s: sims) { - document.removeSimulation(s); - } + RocketComponent component = components.get(0); + component.clearConfigListeners(); + if (components.size() > 1) { + for (int i = 1; i < components.size(); i++) { + RocketComponent listener = components.get(i); + listener.clearConfigListeners(); // Make sure all the listeners are cleared (should not be possible, but just in case) + component.addConfigListener(listener); } } + ComponentConfigDialog.showDialog(parentFrame, document, component); } @Override public void clipboardChanged() { - this.setEnabled(isSimulationSelected()); - } - } - - - - /** - * Action that deletes the selected component. - */ - private class DeleteAction extends RocketAction { - private static final long serialVersionUID = 1L; - - public DeleteAction() { - //// Delete - this.putValue(NAME, trans.get("RocketActions.DelAct.Delete")); - //// Delete the selected component or simulation. - this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DelAct.ttip.Delete")); - this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); - this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); - this.putValue(SMALL_ICON, Icons.EDIT_DELETE); - clipboardChanged(); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (isSimulationSelected()) { - deleteSimulationAction.actionPerformed(e); - parentFrame.selectTab(BasicFrame.SIMULATION_TAB); - } else { - deleteComponentAction.actionPerformed(e); - parentFrame.selectTab(BasicFrame.DESIGN_TAB); - } - } - - @Override - public void clipboardChanged() { - this.setEnabled(isDeletable(selectionModel.getSelectedComponent()) || - isSimulationSelected()); + List components = selectionModel.getSelectedComponents(); + this.setEnabled(!components.isEmpty() || simulationPanel.getEditSimulationAction().isEnabled()); } } @@ -650,14 +550,14 @@ public void clipboardChanged() { * Action the cuts the selected component (copies to clipboard and deletes). */ private class CutAction extends RocketAction { + @Serial private static final long serialVersionUID = 1L; public CutAction() { //// Cut this.putValue(NAME, trans.get("RocketActions.CutAction.Cut")); - this.putValue(MNEMONIC_KEY, KeyEvent.VK_T); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_T); // Use the 't' in Cut as mnemonic this.putValue(ACCELERATOR_KEY, CUT_KEY_STROKE); - //// Cut this component or simulation to the clipboard and remove from this design this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.CutAction.ttip.Cut")); this.putValue(SMALL_ICON, Icons.EDIT_CUT); clipboardChanged(); @@ -665,49 +565,49 @@ public CutAction() { @Override public void actionPerformed(ActionEvent e) { + if (isSimulationSelected()) { + simulationPanel.getCutSimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } else { + cutComponents(); + } + } + + private void cutComponents() { List components = selectionModel.getSelectedComponents(); - if (components.size() > 0) { + if (!components.isEmpty()) { components = new ArrayList<>(components); fillInMissingSelections(components); components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); } - Simulation[] sims = selectionModel.getSelectedSimulations(); - if (isDeletable(components) && isCopyable(components)) { - ComponentConfigDialog.disposeDialog(); + if (!(isDeletable(components) && isCopyable(components))) { + return; + } - if (components.size() == 1) { - document.addUndoPosition("Cut " + components.get(0).getComponentName()); - } else { - document.addUndoPosition("Cut components"); - } + ComponentConfigDialog.disposeDialog(); - List copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components)); - copiedComponents.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); + if (components.size() == 1) { + document.addUndoPosition("Cut " + components.get(0).getComponentName()); + } else { + document.addUndoPosition("Cut components"); + } - OpenRocketClipboard.setClipboard(copiedComponents); - delete(components); - parentFrame.selectTab(BasicFrame.DESIGN_TAB); - } else if (isSimulationSelected()) { + List copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components)); + copiedComponents.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); - Simulation[] simsCopy = new Simulation[sims.length]; - for (int i=0; i < sims.length; i++) { - simsCopy[i] = sims[i].copy(); - } - OpenRocketClipboard.setClipboard(simsCopy); + OpenRocketClipboard.setClipboard(copiedComponents); + delete(components); - for (Simulation s: sims) { - document.removeSimulation(s); - } - parentFrame.selectTab(BasicFrame.SIMULATION_TAB); - } + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } @Override public void clipboardChanged() { List components = selectionModel.getSelectedComponents(); - this.setEnabled((isDeletable(components) && isCopyable(components)) || isSimulationSelected()); + this.setEnabled((isDeletable(components) && isCopyable(components)) || + simulationPanel.getCutSimulationAction().isEnabled()); } } @@ -722,46 +622,47 @@ private class CopyAction extends RocketAction { public CopyAction() { //// Copy this.putValue(NAME, trans.get("RocketActions.CopyAct.Copy")); + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.CopyAct.ttip.Copy")); 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.ttip.Copy")); this.putValue(SMALL_ICON, Icons.EDIT_COPY); clipboardChanged(); } @Override public void actionPerformed(ActionEvent e) { + if (isSimulationSelected()) { + simulationPanel.getCopySimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } else { + copyComponents(); + } + } + + private void copyComponents() { List components = selectionModel.getSelectedComponents(); - if (components.size() > 0) { + if (!components.isEmpty()) { components = new ArrayList<>(components); fillInMissingSelections(components); components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); } - Simulation[] sims = selectionModel.getSelectedSimulations(); - - if (isCopyable(components)) { - List copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components)); - copiedComponents.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); - - OpenRocketClipboard.setClipboard(copiedComponents); - parentFrame.selectTab(BasicFrame.DESIGN_TAB); - } 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); + if (!isCopyable(components)) { + return; } + + List copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components)); + copiedComponents.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0)); + + OpenRocketClipboard.setClipboard(copiedComponents); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } @Override public void clipboardChanged() { this.setEnabled(isCopyable(selectionModel.getSelectedComponent()) || - isSimulationSelected()); + simulationPanel.getCopySimulationAction().isEnabled()); } } @@ -779,9 +680,8 @@ private class PasteAction extends RocketAction { public PasteAction() { //// Paste this.putValue(NAME, trans.get("RocketActions.PasteAct.Paste")); - this.putValue(MNEMONIC_KEY, KeyEvent.VK_P); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_V); this.putValue(ACCELERATOR_KEY, PASTE_KEY_STROKE); - //// Paste the component or simulation on the clipboard to the design. this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.PasteAct.ttip.Paste")); this.putValue(SMALL_ICON, Icons.EDIT_PASTE); clipboardChanged(); @@ -789,76 +689,67 @@ public PasteAction() { @Override public void actionPerformed(ActionEvent e) { + if (isSimulationSelected()) { + simulationPanel.getPasteSimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } else { + pasteComponents(); + } + } + + private void pasteComponents() { List components = new LinkedList<>(OpenRocketClipboard.getClipboardComponents()); - Simulation[] sims = OpenRocketClipboard.getClipboardSimulations(); - if (components.size() > 0) { - ComponentConfigDialog.disposeDialog(); - - List pasted = new LinkedList<>(); - for (RocketComponent component : components) { - pasted.add(component.copy()); - } + if (components.isEmpty()) { + return; + } + ComponentConfigDialog.disposeDialog(); + + List pasted = new LinkedList<>(); + for (RocketComponent component : components) { + pasted.add(component.copy()); + } - List> positions = getPastePositions(pasted); + List> positions = getPastePositions(pasted); - if (pasted.size() == 1) { - document.addUndoPosition("Paste " + pasted.get(0).getComponentName()); - } else { - document.addUndoPosition("Paste components"); - } + if (pasted.size() == 1) { + document.addUndoPosition("Paste " + pasted.get(0).getComponentName()); + } else { + document.addUndoPosition("Paste components"); + } - List successfullyPasted = new LinkedList<>(); - for (int i = 0; i < pasted.size(); i++) { - if (positions.get(i) == null) { - JOptionPane.showMessageDialog(null, - String.format(trans.get("RocketActions.PasteAct.invalidPosition.msg"), - pasted.get(i).getComponentName()), - trans.get("RocketActions.PasteAct.invalidPosition.title"), JOptionPane.WARNING_MESSAGE); + List successfullyPasted = new LinkedList<>(); + for (int i = 0; i < pasted.size(); i++) { + if (positions.get(i) == null) { + JOptionPane.showMessageDialog(null, + String.format(trans.get("RocketActions.PasteAct.invalidPosition.msg"), + pasted.get(i).getComponentName()), + trans.get("RocketActions.PasteAct.invalidPosition.title"), JOptionPane.WARNING_MESSAGE); + } else { + RocketComponent parent = positions.get(i).getU(); + RocketComponent child = pasted.get(i); + if (parent != null && parent.isCompatible(child)) { + parent.addChild(child, positions.get(i).getV()); + successfullyPasted.add(pasted.get(i)); } else { - RocketComponent parent = positions.get(i).getU(); - RocketComponent child = pasted.get(i); - if (parent != null && parent.isCompatible(child)) { - parent.addChild(child, positions.get(i).getV()); - successfullyPasted.add(pasted.get(i)); - } else { - log.warn("Pasted component {} is not compatible with {}", child, parent); - } - } - } - - selectionModel.setSelectedComponents(successfullyPasted); - - parentFrame.selectTab(BasicFrame.DESIGN_TAB); - - } else if (sims != null) { - - ArrayList copySims = new ArrayList(); - - 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()); + log.warn("Pasted component {} is not compatible with {}", child, parent); } - document.addSimulation(copy); - copySims.add(copy); } - selectionModel.setSelectedSimulations(copySims.toArray(new Simulation[0])); - - parentFrame.selectTab(BasicFrame.SIMULATION_TAB); } + + selectionModel.setSelectedComponents(successfullyPasted); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } @Override public void clipboardChanged() { - this.setEnabled( - (getPastePositions(OpenRocketClipboard.getClipboardComponents()).size() > 0) || - (OpenRocketClipboard.getClipboardSimulations() != null)); + this.setEnabled(!getPastePositions(OpenRocketClipboard.getClipboardComponents()).isEmpty() || + simulationPanel.getPasteSimulationAction().isEnabled()); } } + /** * Action that duplicates the selected component. */ @@ -870,7 +761,6 @@ public DuplicateAction() { 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(); @@ -878,6 +768,15 @@ public DuplicateAction() { @Override public void actionPerformed(ActionEvent e) { + if (isSimulationSelected()) { + simulationPanel.getDuplicateSimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } else { + duplicateComponents(); + } + } + + private void duplicateComponents() { List components = selectionModel.getSelectedComponents(); List topComponents = new LinkedList<>(); // Components without a parent component in if (components.size() > 0) { @@ -892,136 +791,130 @@ public void actionPerformed(ActionEvent e) { topComponents.add(c); } } - Simulation[] sims = selectionModel.getSelectedSimulations(); - - if (isCopyable(components)) { - if (ComponentConfigDialog.isDialogVisible()) - 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) { - Pair pos; - if (RocketComponent.listContainsParent(duplicateComponents, component)) { - pos = getPastePosition(component, component.getParent()); - } else { - int compIdx = duplicateComponents.indexOf(component); - RocketComponent pasteParent = topComponents.get(compIdx).getParent(); - pos = getPastePosition(component, pasteParent); - } - positions.add(pos); - } - if (duplicateComponents.size() == 1) { - document.addUndoPosition("Duplicate " + duplicateComponents.get(0).getComponentName()); - } else { - document.addUndoPosition("Duplicate components"); - } - - Collections.reverse(duplicateComponents); - Collections.reverse(positions); + if (!isCopyable(components)) { + return; + } - for (int i = 0; i < duplicateComponents.size(); i++) { - positions.get(i).getU().addChild(duplicateComponents.get(i), positions.get(i).getV()); - } + if (ComponentConfigDialog.isDialogVisible()) + ComponentConfigDialog.disposeDialog(); - selectionModel.setSelectedComponents(duplicateComponents); + List copiedComponents = copyComponentsMaintainParent(components); + OpenRocketClipboard.setClipboard(copiedComponents); + copiedComponents = new LinkedList<>(OpenRocketClipboard.getClipboardComponents()); - parentFrame.selectTab(BasicFrame.DESIGN_TAB); - } else if (sims != null && sims.length > 0) { - ArrayList copySims = new ArrayList(); + List duplicateComponents = new LinkedList<>(); + for (RocketComponent component : copiedComponents) { + duplicateComponents.add(component.copy()); + } - // TODO: the undoing doesn't do anything... - if (sims.length == 1) { - document.addUndoPosition("Duplicate " + sims[0].getName()); + List> positions = new LinkedList<>(); + for (RocketComponent component : duplicateComponents) { + Pair pos; + if (RocketComponent.listContainsParent(duplicateComponents, component)) { + pos = getPastePosition(component, component.getParent()); } else { - document.addUndoPosition("Duplicate simulations"); + int compIdx = duplicateComponents.indexOf(component); + RocketComponent pasteParent = topComponents.get(compIdx).getParent(); + pos = getPastePosition(component, pasteParent); } + positions.add(pos); + } - 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); - } + if (duplicateComponents.size() == 1) { + document.addUndoPosition("Duplicate " + duplicateComponents.get(0).getComponentName()); + } else { + document.addUndoPosition("Duplicate components"); + } - selectionModel.setSelectedSimulations(copySims.toArray(new Simulation[0])); + Collections.reverse(duplicateComponents); + Collections.reverse(positions); - parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + 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.DESIGN_TAB); } @Override public void clipboardChanged() { this.setEnabled(isCopyable(selectionModel.getSelectedComponent()) || - isSimulationSelected()); + simulationPanel.getDuplicateSimulationAction().isEnabled()); } } - - + + /** - * Action to edit the currently selected component. + * Action that deletes the selected component. */ - private class EditAction extends RocketAction { + private class DeleteAction extends RocketAction { private static final long serialVersionUID = 1L; - 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); + public DeleteAction() { + //// Delete + this.putValue(NAME, trans.get("RocketActions.DelAct.Delete")); + //// Delete the selected component or simulation. + this.putValue(SHORT_DESCRIPTION, trans.get("RocketActions.DelAct.ttip.Delete")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_DELETE); + this.putValue(ACCELERATOR_KEY, DELETE_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_DELETE); clipboardChanged(); } @Override public void actionPerformed(ActionEvent e) { - List components = selectionModel.getSelectedComponents(); - Simulation[] sims = selectionModel.getSelectedSimulations(); + if (isSimulationSelected()) { + simulationPanel.getDeleteSimulationAction().actionPerformed(e); + parentFrame.selectTab(BasicFrame.SIMULATION_TAB); + } else { + deleteComponents(); + } + } - if (components.size() > 0) { - if (ComponentConfigDialog.isDialogVisible()) - ComponentConfigDialog.disposeDialog(); - - RocketComponent component = components.get(0); - component.clearConfigListeners(); - if (components.size() > 1) { - for (int i = 1; i < components.size(); i++) { - RocketComponent listener = components.get(i); - listener.clearConfigListeners(); // Make sure all the listeners are cleared (should not be possible, but just in case) - component.addConfigListener(listener); - } - } - ComponentConfigDialog.showDialog(parentFrame, document, component); - } else if (sims != null && sims.length > 0 && (simulationPanel != null)) { - simulationPanel.editSimulation(); + private void deleteComponents() { + List components = new ArrayList<>(selectionModel.getSelectedComponents()); + if (components.size() == 0) return; + components.sort(Comparator.comparing(c -> c.getParent().getChildPosition(c))); + + if (components.size() == 1) { + document.addUndoPosition("Delete " + components.get(0).getComponentName()); + } else { + document.addUndoPosition("Delete components"); + } + + for (RocketComponent component : components) { + deleteComponent(component); + } + + parentFrame.selectTab(BasicFrame.DESIGN_TAB); + } + + private void deleteComponent(RocketComponent component) { + if (isDeletable(component)) { + ComponentConfigDialog.disposeDialog(); + + try { + component.getRocket().removeComponentChangeListener(ComponentConfigDialog.getDialog()); + + delete(component); + } catch (IllegalStateException ignored) { } } } @Override public void clipboardChanged() { - List components = selectionModel.getSelectedComponents(); - - this.setEnabled(components.size() > 0 || isSimulationSelected()); + this.setEnabled(isDeletable(selectionModel.getSelectedComponent()) || + simulationPanel.getDeleteSimulationAction().isEnabled()); } } + + /** * Action to select all components with the same color as the currently selected component. */ @@ -1038,7 +931,7 @@ public SelectSameColorAction() { @Override public void actionPerformed(ActionEvent e) { List components = selectionModel.getSelectedComponents(); - if (components.size() == 0) { + if (components.isEmpty()) { return; } diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 7f5aed9e04..36f87e2daa 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -5,6 +5,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; @@ -17,10 +18,11 @@ import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; -import java.util.Arrays; +import java.io.Serial; import java.util.Comparator; import javax.swing.AbstractAction; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -81,6 +83,8 @@ import net.sf.openrocket.file.SimulationTableCSVExport; import net.sf.openrocket.utils.TableRowTraversalPolicy; +import static net.sf.openrocket.gui.main.BasicFrame.SHORTCUT_KEY; + @SuppressWarnings("serial") public class SimulationPanel extends JPanel { private static final Logger log = LoggerFactory.getLogger(SimulationPanel.class); @@ -110,6 +114,9 @@ public class SimulationPanel extends JPanel { private final JPopupMenu pm; private final SimulationAction editSimulationAction; + private final SimulationAction cutSimulationAction; + private final SimulationAction copySimulationAction; + private final SimulationAction pasteSimulationAction; private final SimulationAction runSimulationAction; private final SimulationAction plotSimulationAction; private final SimulationAction duplicateSimulationAction; @@ -129,6 +136,9 @@ public SimulationPanel(OpenRocketDocument doc) { // Simulation actions SimulationAction newSimulationAction = new NewSimulationAction(); editSimulationAction = new EditSimulationAction(); + cutSimulationAction = new CutSimulationAction(); + copySimulationAction = new CopySimulationAction(); + pasteSimulationAction = new PasteSimulationAction(); runSimulationAction = new RunSimulationAction(); plotSimulationAction = new PlotSimulationAction(); duplicateSimulationAction = new DuplicateSimulationAction(); @@ -155,7 +165,7 @@ public SimulationPanel(OpenRocketDocument doc) { RocketActions.tieActionToButton(runButton, runSimulationAction, trans.get("simpanel.but.runsimulations")); runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu")); this.add(runButton, "gapright para"); - + //// Delete simulations button deleteButton = new IconButton(); RocketActions.tieActionToButton(deleteButton, deleteSimulationAction, trans.get("simpanel.but.deletesimulations")); @@ -176,6 +186,7 @@ public SimulationPanel(OpenRocketDocument doc) { simulationTableModel = new SimulationTableModel(); simulationTable = new ColumnTable(simulationTableModel) { + @Serial private static final long serialVersionUID = -5799340181229735630L; }; ColumnTableRowSorter simulationTableSorter = new ColumnTableRowSorter(simulationTableModel); @@ -185,9 +196,21 @@ public SimulationPanel(OpenRocketDocument doc) { simulationTableModel.setColumnWidths(simulationTable.getColumnModel()); simulationTable.setFillsViewportHeight(true); + // Unregister the default actions that would otherwise conflict with RocketActions and their acceleration keys + InputMap im = simulationTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, SHORTCUT_KEY), "none"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, SHORTCUT_KEY), "none"); + // Context menu pm = new JPopupMenu(); pm.add(editSimulationAction); + pm.add(cutSimulationAction); + pm.add(copySimulationAction); + pm.add(pasteSimulationAction); pm.add(duplicateSimulationAction); pm.add(deleteSimulationAction); pm.addSeparator(); @@ -248,7 +271,7 @@ else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { public void valueChanged(ListSelectionEvent event) { if ((simulationTable.getSelectedRow() != previousSelectedRow) || (simulationTable.getSelectedRowCount() != previousSelectedRowCount)) { - updateButtonStates(); + updateActions(); previousSelectedRow = simulationTable.getSelectedRow(); previousSelectedRowCount = simulationTable.getSelectedRowCount(); } @@ -279,7 +302,55 @@ public void componentChanged(ComponentChangeEvent e) { JScrollPane scrollpane = new JScrollPane(simulationTable); this.add(scrollpane, "spanx, grow, wrap rel"); - updateButtonStates(); + updateActions(); + } + + /** + * Returns the action used for editing selected simulations. + * @return the action used for editing selected simulations. + */ + public SimulationAction getEditSimulationAction() { + return editSimulationAction; + } + + /** + * Returns the action used for cutting selected simulations. + * @return the action used for cutting selected simulations. + */ + public SimulationAction getCutSimulationAction() { + return cutSimulationAction; + } + + /** + * Returns the action used for copying selected simulations. + * @return the action used for copying selected simulations. + */ + public SimulationAction getCopySimulationAction() { + return copySimulationAction; + } + + /** + * Returns the action used for pasting simulations. + * @return the action used for pasting simulations. + */ + public SimulationAction getPasteSimulationAction() { + return pasteSimulationAction; + } + + /** + * Returns the action used for duplicating selected simulations. + * @return the action used for duplicating selected simulations. + */ + public SimulationAction getDuplicateSimulationAction() { + return duplicateSimulationAction; + } + + /** + * Returns the action used for deleting selected simulations. + * @return the action used for deleting selected simulations. + */ + public SimulationAction getDeleteSimulationAction() { + return deleteSimulationAction; } public void updatePreviousSelection() { @@ -323,55 +394,62 @@ private void plotSimulation() { openDialog(true, sim); } - private void deleteSimulation() { - int[] selection = simulationTable.getSelectedRows(); - if (selection.length == 0) { + private void deleteSimulations(Simulation[] sims) { + if (sims == null || sims.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); - } + if (!verifyDeleteSimulation()) { + return; } // 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]); + for (Simulation sim : sims) { + document.removeSimulation(sim); } + simulationTableModel.fireTableDataChanged(); updatePreviousSelection(); takeTheSpotlight(); } + private void deleteSimulations() { + deleteSimulations(getSelectedSimulations()); + } + + private boolean verifyDeleteSimulation() { + boolean verify = Application.getPreferences().getConfirmSimDeletion(); + if (!verify) { + return true; + } + + 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 (dontAsk.isSelected()) { + Application.getPreferences().setConfirmSimDeletion(false); + } + + return ret == JOptionPane.OK_OPTION; + } + private void runSimulation() { Simulation[] sims = getSelectedSimulations(); if (sims == null) return; @@ -479,56 +557,113 @@ private Simulation[] getSelectedSimulations() { return sims; } + /** + * Full simulation copying + */ + public void copySimulationsAction() { + Simulation[] sims = getSelectedSimulations(); + + if (sims == null || sims.length == 0) + return; - private void copySimulationAction() { - int numCols=simulationTable.getColumnCount(); - int numRows=simulationTable.getSelectedRowCount(); - int[] rowsSelected=simulationTable.getSelectedRows(); + Simulation[] simsCopy = new Simulation[sims.length]; + for (int i=0; i < sims.length; i++) { + simsCopy[i] = sims[i].copy(); + } - if (numRows!=rowsSelected[rowsSelected.length-1]-rowsSelected[0]+1 || numRows!=rowsSelected.length) { + OpenRocketClipboard.setClipboard(simsCopy); + copySimulationValues(); + } - JOptionPane.showMessageDialog(null, "Invalid Copy Selection", "Invalid Copy Selection", JOptionPane.ERROR_MESSAGE); + /** + * Only copy the simulation table values to the clipboard. (not actual Simulation copying) + */ + public void copySimulationValues() { + int numCols = simulationTable.getColumnCount(); + int numRows = simulationTable.getSelectedRowCount(); + int[] rowsSelected = simulationTable.getSelectedRows(); + + if (numRows != rowsSelected.length) { + JOptionPane.showMessageDialog(this, trans.get("simpanel.msg.invalidCopySelection"), + trans.get("simpanel.msg.invalidCopySelection"), 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"); + StringBuilder valuesStr = new StringBuilder(); + + // Copy the column names + valuesStr.append(trans.get("simpanel.col.Status")).append("\t"); + for (int i = 1; i < numCols; i++) { + valuesStr.append(simulationTable.getColumnName(i)); + if (i < numCols-1) { + valuesStr.append("\t"); } } - excelStr.append("\n"); + valuesStr.append("\n"); + + // Copy the values for (int i = 0; i < numRows; i++) { - for (int j = 1; j < numCols; j++) { - excelStr.append(simulationTable.getValueAt(rowsSelected[i], j)); + for (int j = 0; j < numCols; j++) { + Object value = simulationTable.getValueAt(rowsSelected[i], j); + valuesStr.append(value == null ? "" : value.toString()); if (j < numCols-1) { - excelStr.append("\t"); + valuesStr.append("\t"); } } - excelStr.append("\n"); + valuesStr.append("\n"); } - StringSelection sel = new StringSelection(excelStr.toString()); + StringSelection sel = new StringSelection(valuesStr.toString()); Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); cb.setContents(sel, sel); } - private void duplicateSimulation() { - Simulation[] sims = getSelectedSimulations(); - if (sims == null) return; + private void pasteSimulationsAction() { + Simulation[] sims = OpenRocketClipboard.getClipboardSimulations(); + if (sims == null || sims.length == 0) { + return; + } + + duplicateSimulation(sims, simulationTable.getSelectedRow() + 1); + } + + /** + * Duplicates the provided simulations at a certain index and selects them in the simulation table. + * @param sims The simulations to duplicate + * @param index The index to insert the simulations at (e.g. '1' to insert after the first simulation) + */ + public void duplicateSimulation(Simulation[] sims, int index) { + if (sims == null || sims.length == 0) return; + // TODO: the undoing doesn't do anything... + if (sims.length == 1) { + document.addUndoPosition("Duplicate " + sims[0].getName()); + } else { + document.addUndoPosition("Duplicate simulations"); + } + + index = index >= 0 ? index : document.getSimulationCount(); + int newIndex = index; 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); + document.addSimulation(copy, newIndex); + newIndex++; // Ensure simulations are in the correct order } - simulationTable.getSelectionModel().setSelectionInterval(simulationTable.getRowCount()-sims.length,simulationTable.getRowCount()-1); + simulationTableModel.fireTableDataChanged(); + simulationTable.getSelectionModel().setSelectionInterval(index, newIndex-1); + } + /** + * Duplicates the provided simulations to the end and selects them in the simulation table. + * @param sims The simulations to duplicate. + */ + public void duplicateSimulation(Simulation[] sims) { + duplicateSimulation(sims, document.getSimulationCount()); } /** @@ -542,13 +677,24 @@ public AbstractAction getExportSimulationTableAsCSVAction() { protected void doPopup(MouseEvent e) { pm.show(e.getComponent(), e.getX(), e.getY()); } + + /** + * Add a listener to the simulation table selection model. + * @param listener the listener to add. + */ + public void addSimulationTableListSelectionListener(ListSelectionListener listener) { + simulationTable.getSelectionModel().addListSelectionListener(listener); + } - private void updateButtonStates() { + public void updateActions() { editSimulationAction.updateEnabledState(); + cutSimulationAction.updateEnabledState(); + copySimulationAction.updateEnabledState(); + pasteSimulationAction.updateEnabledState(); + duplicateSimulationAction.updateEnabledState(); deleteSimulationAction.updateEnabledState(); runSimulationAction.updateEnabledState(); plotSimulationAction.updateEnabledState(); - duplicateSimulationAction.updateEnabledState(); simTableExportAction.updateEnabledState(); selectedSimsExportAction.updateEnabledState(); } @@ -595,6 +741,61 @@ public ListSelectionModel getSimulationListSelectionModel() { return simulationTable.getSelectionModel(); } + private String getSimulationToolTip(Simulation sim, boolean includeSimName) { + String tip; + FlightData data = sim.getSimulatedData(); + + tip = ""; + if (includeSimName) { + tip += "" + sim.getName() + "
"; + } + switch (sim.getStatus()) { + case CANT_RUN: + tip += trans.get("simpanel.ttip.noData")+"
"; + break; + case LOADED: + tip += trans.get("simpanel.ttip.loaded") + "
"; + break; + case UPTODATE: + tip += trans.get("simpanel.ttip.uptodate") + "
"; + break; + + case OUTDATED: + tip += trans.get("simpanel.ttip.outdated") + "
"; + break; + + case EXTERNAL: + tip += trans.get("simpanel.ttip.external") + "
"; + return tip; + + case NOT_SIMULATED: + tip += trans.get("simpanel.ttip.notSimulated"); + return tip; + } + + if (data == null) { + tip += trans.get("simpanel.ttip.noData"); + return tip; + } + WarningSet warnings = data.getWarningSet(); + + if (warnings.isEmpty()) { + tip += trans.get("simpanel.ttip.noWarnings"); + return tip; + } + + tip += trans.get("simpanel.ttip.warnings"); + for (Warning w : warnings) { + tip += "
" + w.toString(); + } + + return tip; + } + + private String getSimulationToolTip(Simulation sim) { + return getSimulationToolTip(sim, true); + } + private void openDialog(boolean plotMode, boolean isNewSimulation, final Simulation... sims) { SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, isNewSimulation, sims); if (plotMode) { @@ -627,7 +828,7 @@ private void fireMaintainSelection() { } } - private abstract static class SimulationAction extends AbstractAction { + public abstract static class SimulationAction extends AbstractAction { private static final long serialVersionUID = 1L; public abstract void updateEnabledState(); @@ -653,7 +854,8 @@ public void updateEnabledState() { class EditSimulationAction extends SimulationAction { public EditSimulationAction() { - putValue(NAME, trans.get("simpanel.pop.edit")); + this.putValue(NAME, trans.get("simpanel.pop.edit")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.edit.ttip")); this.putValue(MNEMONIC_KEY, KeyEvent.VK_E); this.putValue(ACCELERATOR_KEY, RocketActions.EDIT_KEY_STROKE); this.putValue(SMALL_ICON, Icons.EDIT_EDIT); @@ -664,16 +866,125 @@ public void actionPerformed(ActionEvent e) { editSimulation(); } + @Override + public void updateEnabledState() { + this.setEnabled(simulationTable.getSelectedRowCount() > 0); + } + } + + class CutSimulationAction extends SimulationAction { + public CutSimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.cut")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.cut.ttip")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_X); + this.putValue(ACCELERATOR_KEY, RocketActions.CUT_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_CUT); + } + + @Override + public void actionPerformed(ActionEvent e) { + Simulation[] sims = getSelectedSimulations(); + + if (sims == null || sims.length == 0) + return; + + copySimulationsAction(); + deleteSimulations(sims); + } + @Override public void updateEnabledState() { setEnabled(simulationTable.getSelectedRowCount() > 0); } } + class CopySimulationAction extends SimulationAction { + public CopySimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.copy")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.copy.ttip")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_C); + this.putValue(ACCELERATOR_KEY, RocketActions.COPY_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_COPY); + } + + @Override + public void actionPerformed(ActionEvent e) { + copySimulationsAction(); + } + + @Override + public void updateEnabledState() { + this.setEnabled(simulationTable.getSelectedRowCount() > 0); + } + } + + class PasteSimulationAction extends SimulationAction { + public PasteSimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.paste")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.paste.ttip")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_V); + this.putValue(ACCELERATOR_KEY, RocketActions.PASTE_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_PASTE); + } + + @Override + public void actionPerformed(ActionEvent e) { + pasteSimulationsAction(); + } + + @Override + public void updateEnabledState() { + this.setEnabled((simulationTable.getSelectedRowCount() > 0) && + (OpenRocketClipboard.getClipboardSimulations() != null) && (OpenRocketClipboard.getClipboardSimulations().length > 0)); + } + } + + class DuplicateSimulationAction extends SimulationAction { + public DuplicateSimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.duplicate")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.duplicate.ttip")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_D); + this.putValue(ACCELERATOR_KEY, RocketActions.DUPLICATE_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_DUPLICATE); + } + + @Override + public void actionPerformed(ActionEvent e) { + duplicateSimulation(getSelectedSimulations()); + } + + @Override + public void updateEnabledState() { + this.setEnabled(simulationTable.getSelectedRowCount() > 0); + } + } + + class DeleteSimulationAction extends SimulationAction { + public DeleteSimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.delete")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.delete.ttip")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_DELETE); + this.putValue(ACCELERATOR_KEY, RocketActions.DELETE_KEY_STROKE); + this.putValue(SMALL_ICON, Icons.EDIT_DELETE); + } + + @Override + public void actionPerformed(ActionEvent e) { + deleteSimulations(); + } + + @Override + public void updateEnabledState() { + this.setEnabled(simulationTable.getSelectedRowCount() > 0); + } + } + + class RunSimulationAction extends SimulationAction { public RunSimulationAction() { - putValue(NAME, trans.get("simpanel.pop.run")); - putValue(SMALL_ICON, Icons.SIM_RUN); + this.putValue(NAME, trans.get("simpanel.pop.run")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.run.ttip")); + this.putValue(SMALL_ICON, Icons.SIM_RUN); } @Override @@ -683,26 +994,25 @@ public void actionPerformed(ActionEvent e) { @Override public void updateEnabledState() { - setEnabled(simulationTable.getSelectedRowCount() > 0); + this.setEnabled(simulationTable.getSelectedRowCount() > 0); } } - class DeleteSimulationAction extends SimulationAction { - 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); + class PlotSimulationAction extends SimulationAction { + public PlotSimulationAction() { + this.putValue(NAME, trans.get("simpanel.pop.plot")); + this.putValue(SHORT_DESCRIPTION, trans.get("simpanel.pop.plot.ttip")); + this.putValue(SMALL_ICON, Icons.SIM_PLOT); } @Override public void actionPerformed(ActionEvent e) { - deleteSimulation(); + plotSimulation(); } @Override public void updateEnabledState() { - setEnabled(simulationTable.getSelectedRowCount() > 0); + this.setEnabled(simulationTable.getSelectedRowCount() == 1); } } @@ -712,8 +1022,8 @@ public void updateEnabledState() { class ExportSimulationTableAsCSVAction extends SimulationAction { public ExportSimulationTableAsCSVAction() { - putValue(NAME, trans.get("simpanel.pop.exportSimTableToCSV")); - putValue(SMALL_ICON, Icons.SIM_TABLE_EXPORT); + this.putValue(NAME, trans.get("simpanel.pop.exportSimTableToCSV")); + this.putValue(SMALL_ICON, Icons.SIM_TABLE_EXPORT); } @Override @@ -723,7 +1033,7 @@ public void actionPerformed(ActionEvent arg0) { @Override public void updateEnabledState() { - setEnabled(simulationTableModel != null && simulationTableModel.getRowCount() > 0); + this.setEnabled(simulationTableModel != null && simulationTableModel.getRowCount() > 0); } } @@ -734,8 +1044,8 @@ public void updateEnabledState() { class ExportSelectedSimulationsAsCSVAction extends SimulationAction { public ExportSelectedSimulationsAsCSVAction() { - putValue(NAME, trans.get("simpanel.pop.exportSelectedSimsToCSV")); - putValue(SMALL_ICON, Icons.SIM_TABLE_EXPORT); + this.putValue(NAME, trans.get("simpanel.pop.exportSelectedSimsToCSV")); + this.putValue(SMALL_ICON, Icons.SIM_TABLE_EXPORT); } @Override @@ -745,47 +1055,13 @@ public void actionPerformed(ActionEvent arg0) { @Override public void updateEnabledState() { - setEnabled(simulationTableModel != null && simulationTableModel.getRowCount() > 0 && + this.setEnabled(simulationTableModel != null && simulationTableModel.getRowCount() > 0 && simulationTable.getSelectedRowCount() > 0); } } - class PlotSimulationAction extends SimulationAction { - public PlotSimulationAction() { - putValue(NAME, trans.get("simpanel.pop.plot")); - putValue(SMALL_ICON, Icons.SIM_PLOT); - } - - @Override - public void actionPerformed(ActionEvent e) { - plotSimulation(); - } - - @Override - public void updateEnabledState() { - setEnabled(simulationTable.getSelectedRowCount() == 1); - } - } - class DuplicateSimulationAction extends SimulationAction { - 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) { - duplicateSimulation(); - } - - @Override - public void updateEnabledState() { - setEnabled(simulationTable.getSelectedRowCount() > 0); - } - } public static class CellTransferable implements Transferable { @@ -852,53 +1128,24 @@ public Component getTableCellRendererComponent(JTable table, } return component; } + } - private String getSimulationToolTip(Simulation sim) { - String tip; - FlightData data = sim.getSimulatedData(); - - tip = "" + sim.getName() + "
"; - switch (sim.getStatus()) { - case CANT_RUN: - tip += trans.get("simpanel.ttip.noData")+"
"; - break; - case LOADED: - tip += trans.get("simpanel.ttip.loaded") + "
"; - break; - case UPTODATE: - tip += trans.get("simpanel.ttip.uptodate") + "
"; - break; - - case OUTDATED: - tip += trans.get("simpanel.ttip.outdated") + "
"; - break; - - case EXTERNAL: - tip += trans.get("simpanel.ttip.external") + "
"; - return tip; - - case NOT_SIMULATED: - tip += trans.get("simpanel.ttip.notSimulated"); - return tip; - } - - if (data == null) { - tip += trans.get("simpanel.ttip.noData"); - return tip; - } - WarningSet warnings = data.getWarningSet(); + private class StatusLabel extends StyledLabel { + private Simulation simulation; - if (warnings.isEmpty()) { - tip += trans.get("simpanel.ttip.noWarnings"); - return tip; - } + public StatusLabel(Simulation simulation, float size) { + super(size); + this.simulation = simulation; + } - tip += trans.get("simpanel.ttip.warnings"); - for (Warning w : warnings) { - tip += "
" + w.toString(); - } + public void replaceSimulation(Simulation simulation) { + this.simulation = simulation; + } - return tip; + @Override + public String toString() { + String text = getSimulationToolTip(simulation, false); + return text.replace("
", "-").replaceAll("<[^>]*>",""); } } @@ -909,31 +1156,33 @@ public SimulationTableModel() { super( //// Status and warning column new Column("") { - private JLabel label = null; + private StatusLabel label = null; @Override public Object getValueAt(int row) { if (row < 0 || row >= document.getSimulationCount()) return null; + Simulation simulation = document.getSimulation(row); + // Initialize the label if (label == null) { - label = new StyledLabel(2f); + label = new StatusLabel(simulation, 2f); label.setIconTextGap(1); // label.setFont(label.getFont().deriveFont(Font.BOLD)); + } else { + label.replaceSimulation(simulation); } // Set simulation status icon - Simulation.Status status = document.getSimulation(row).getStatus(); + Simulation.Status status = simulation.getStatus(); label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status)); // Set warning marker if (status == Simulation.Status.NOT_SIMULATED || status == Simulation.Status.EXTERNAL) { - label.setText(""); - } else { WarningSet w = document.getSimulation(row).getSimulatedWarnings(); @@ -1160,7 +1409,7 @@ public void takeTheSpotlight() { return; } if (previousSelection == null || previousSelection.length == 0) { - simulationTable.setRowSelectionInterval(0, 0); + simulationTable.getSelectionModel().setSelectionInterval(0, 0); } else { simulationTable.clearSelection(); for (int row : previousSelection) { @@ -1170,5 +1419,6 @@ public void takeTheSpotlight() { simulationTable.addRowSelectionInterval(row, row); } } + updateActions(); } } diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java index a852e94b7c..e5d422e36f 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java @@ -1,20 +1,22 @@ package net.sf.openrocket.gui.main.componenttree; +import java.awt.BorderLayout; import java.awt.Component; -import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.util.List; +import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTree; import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import net.sf.openrocket.gui.main.ComponentIcons; -import net.sf.openrocket.gui.util.Icons; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassComponent.MassComponentType; @@ -34,45 +36,83 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus1) { - Component comp = super.getTreeCellRendererComponent(tree, value, sel, - expanded, leaf, row, hasFocus1); + // Create a new JPanel + JPanel panel = new JPanel(); + panel.setOpaque(false); // Set this to false if you want to keep the tree's default background intact + panel.setLayout(new BorderLayout()); + + // Create two JLabels, one for the icon and one for the text + JLabel iconLabel = new JLabel(); + JLabel textLabel = new JLabel(); + + // Retrieve the component from the super method + Component comp = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus1); if (tree == null) return comp; TreePath[] paths = tree.getSelectionPaths(); List components = null; if (paths != null && paths.length > 0) { components = new ArrayList<>(ComponentTreeModel.componentsFromPaths(paths)); } + if (comp instanceof JLabel) { + textLabel.setText(((JLabel) comp).getText()); - // Set icon + // Set the font to the tree font + Font treeFont = UIManager.getFont("Tree.font"); + textLabel.setFont(treeFont); + } + + // Set the icon RocketComponent c = (RocketComponent) value; + Border iconMarginBorder = BorderFactory.createEmptyBorder(0, 0, 0, 4); // 4-pixel gap to the right of the icon if (c.getClass().isAssignableFrom(MassComponent.class)) { MassComponentType t = ((MassComponent) c).getMassComponentType(); - setIcon(ComponentIcons.getSmallMassTypeIcon(t)); + iconLabel.setIcon(ComponentIcons.getSmallMassTypeIcon(t)); + iconLabel.setBorder(iconMarginBorder); + } else { + iconLabel.setIcon(ComponentIcons.getSmallIcon(value.getClass())); + iconLabel.setBorder(iconMarginBorder); + } + + // Add the JLabels to the JPanel + panel.add(iconLabel, BorderLayout.WEST); + panel.add(textLabel, BorderLayout.CENTER); + + // Set the background and foreground colors of the text JLabel + if (sel) { + textLabel.setOpaque(true); + textLabel.setBackground(GUIUtil.getUITheme().getTextSelectionBackgroundColor()); + textLabel.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor()); } else { - setIcon(ComponentIcons.getSmallIcon(value.getClass())); + textLabel.setOpaque(true); // Set this to true to allow the background color to be visible + textLabel.setBackground(GUIUtil.getUITheme().getComponentTreeBackgroundColor()); + textLabel.setForeground(GUIUtil.getUITheme().getComponentTreeForegroundColor()); } + + comp = panel; + + // Add mass/CG/CD overridden icons if (c.isMassOverridden() || c.getMassOverriddenBy() != null || c.isCGOverridden() || c.getCGOverriddenBy() != null || c.isCDOverridden() || c.getCDOverriddenBy() != null) { JPanel p = new JPanel(); p.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1)); - p.setBackground(UIManager.getColor("Tree.textBackground")); - p.setForeground(UIManager.getColor("Tree.textForeground")); + p.setBackground(GUIUtil.getUITheme().getComponentTreeBackgroundColor()); + p.setForeground(GUIUtil.getUITheme().getComponentTreeForegroundColor()); p.add(comp/* , BorderLayout.WEST */); if (c.getMassOverriddenBy() != null) { - p.add(new JLabel(Icons.MASS_OVERRIDE_SUBCOMPONENT)); + p.add(new JLabel(GUIUtil.getUITheme().getMassOverrideSubcomponentIcon())); } else if (c.isMassOverridden()) { - p.add(new JLabel(Icons.MASS_OVERRIDE)); + p.add(new JLabel(GUIUtil.getUITheme().getMassOverrideIcon())); } if (c.getCGOverriddenBy() != null) { - p.add(new JLabel(Icons.CG_OVERRIDE_SUBCOMPONENT)); + p.add(new JLabel(GUIUtil.getUITheme().getCGOverrideSubcomponentIcon())); } else if (c.isCGOverridden()) { - p.add(new JLabel(Icons.CG_OVERRIDE)); + p.add(new JLabel(GUIUtil.getUITheme().getCGOverrideIcon())); } if (c.getCDOverriddenBy() != null) { - p.add(new JLabel(Icons.CD_OVERRIDE_SUBCOMPONENT)); + p.add(new JLabel(GUIUtil.getUITheme().getCDOverrideSubcomponentIcon())); } else if (c.isCDOverridden()) { - p.add(new JLabel(Icons.CD_OVERRIDE)); + p.add(new JLabel(GUIUtil.getUITheme().getCDOverrideIcon())); } // Make sure the tooltip also works on the override icons diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 5baec6802c..ee9e53dec6 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -23,6 +23,7 @@ import javax.swing.table.DefaultTableCellRenderer; import net.sf.openrocket.gui.main.FlightConfigurationPanel; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.util.ArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +45,8 @@ public abstract class FlightConfigurablePanel extends JPanel implements ComponentChangeListener { protected static final Translator trans = Application.getTranslator(); + protected static final SwingPreferences prefs = (SwingPreferences) Application.getPreferences(); + private static final Logger log = LoggerFactory.getLogger(FlightConfigurablePanel.class); protected RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); @@ -387,12 +390,12 @@ private void setSelected(JComponent c, JTable table, boolean isSelected, boolean protected final void shaded(JLabel label) { GUIUtil.changeFontStyle(label, Font.ITALIC); - label.setForeground(Color.GRAY); + label.setForeground(GUIUtil.getUITheme().getDimTextColor()); } protected final void regular(JLabel label) { GUIUtil.changeFontStyle(label, Font.PLAIN); - label.setForeground(Color.BLACK); + label.setForeground(GUIUtil.getUITheme().getTextColor()); } protected abstract JLabel format( T component, FlightConfigurationId configId, JLabel label ); diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index db19ab4967..c2f6db4dba 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -3,8 +3,6 @@ import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; @@ -31,7 +29,6 @@ import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; -import net.sf.openrocket.util.Color; import net.sf.openrocket.gui.widgets.SelectColorButton; import org.jfree.chart.ChartPanel; @@ -44,9 +41,8 @@ * @author Sampo Niskanen */ public class SimulationPlotDialog extends JDialog { - private static final Translator trans = Application.getTranslator(); - + private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) { //// Flight data plot super(parent, simulation.getName()); @@ -77,7 +73,7 @@ private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfigura // Add warning if X axis type is not time if (config.getDomainAxisType() != FlightDataType.TYPE_TIME) { JLabel msg = new StyledLabel(trans.get("PlotDialog.lbl.timeSeriesWarning"), -2); - msg.setForeground(Color.DARK_RED.toAWTColor()); + msg.setForeground(GUIUtil.getUITheme().getDarkWarningColor()); panel.add(msg, "wrap"); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java index d3a4068f0b..edc683f1cc 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java @@ -40,11 +40,6 @@ public static RocketComponentShape[] getShapesSide(final RocketComponent compone Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(correctedTransform, markerRadius); RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); - // Set the color of the shapes - Color color = getColor(component); - for (int i = 0; i < shapes.length - 1; i++) { - shapes[i].setColor(color); - } shapes[shapes.length - 1].setColor(Color.INVISIBLE); return shapes; @@ -69,11 +64,7 @@ public static RocketComponentShape[] getShapesBack(final RocketComponent compone Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(correctedTransform, markerRadius); RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); - // Set the color of the shapes - Color color = getColor(component); - for (int i = 0; i < shapes.length - 1; i++) { - shapes[i].setColor(color); - } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); return shapes; } @@ -86,14 +77,4 @@ public static RocketComponentShape[] getShapesBack(final RocketComponent compone private static double getDisplayRadius(RocketComponent component) { return component.getRocket().getBoundingRadius() * 0.03; } - - private static Color getColor(RocketComponent component) { - if (component instanceof PodSet) { - return new Color(160,160,215); - } else if (component instanceof ParallelStage) { - return new Color(198,163,184); - } else { - return new Color(160, 160, 160); - } - } } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java index 25585320c4..01f5120b60 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java @@ -70,7 +70,7 @@ public AbstractScaleFigure() { this.setPreferredSize(new Dimension(100,100)); setSize(100,100); - setBackground(Color.WHITE); + setBackground(GUIUtil.getUITheme().getBackgroundColor()); setOpaque(true); } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java index 83d5f96009..cf56625745 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java @@ -1,7 +1,6 @@ package net.sf.openrocket.gui.scalefigure; import java.awt.BasicStroke; -import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; @@ -15,6 +14,7 @@ import java.util.LinkedList; import java.util.List; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.rocketcomponent.FreeformFinSet; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; @@ -31,11 +31,7 @@ @SuppressWarnings("serial") public class FinPointFigure extends AbstractScaleFigure { - - //private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); - private static final Color GRID_MAJOR_LINE_COLOR = new Color( 0, 0, 255, 80); - private static final Color GRID_MINOR_LINE_COLOR = new Color( 0, 0, 255, 30); private static final int GRID_LINE_BASE_WIDTH_PIXELS = 1; private static final int LINE_WIDTH_FIN_PIXELS = 1; @@ -45,8 +41,6 @@ public class FinPointFigure extends AbstractScaleFigure { private static final int LINE_WIDTH_BOX_PIXELS = 1; private static final float BOX_WIDTH_PIXELS = 12; private static final float SELECTED_BOX_WIDTH_PIXELS = BOX_WIDTH_PIXELS + 4; - private static final Color POINT_COLOR = new Color(200, 0, 0, 255); - private static final Color SELECTED_POINT_COLOR = new Color(200, 0, 0, 255); private static final double MINOR_TICKS = 10.0; private static final double MAJOR_TICKS = 100.0; @@ -65,9 +59,8 @@ public class FinPointFigure extends AbstractScaleFigure { public FinPointFigure(FreeformFinSet finset) { this.finset = finset; - - // useful for debugging -- shows a contrast against un-drawn space. - setBackground(Color.WHITE); + + setBackground(GUIUtil.getUITheme().getBackgroundColor()); setOpaque(true); updateFigure(); @@ -129,11 +122,11 @@ public void paintBackgroundGrid( Graphics2D g2) { Line2D.Double line = new Line2D.Double(); for (Tick t : verticalTicks) { if (t.major) { - g2.setColor(FinPointFigure.GRID_MAJOR_LINE_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointGridMajorLineColor()); line.setLine( t.value, y0, t.value, y1); g2.draw(line); }else{ - g2.setColor(FinPointFigure.GRID_MINOR_LINE_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointGridMinorLineColor()); line.setLine( t.value, y0, t.value, y1); g2.draw(line); } @@ -143,11 +136,11 @@ public void paintBackgroundGrid( Graphics2D g2) { Tick[] horizontalTicks = unit.getTicks(y0, y1, MINOR_TICKS / this.scale, MAJOR_TICKS / this.scale); for (Tick t : horizontalTicks) { if (t.major) { - g2.setColor(FinPointFigure.GRID_MAJOR_LINE_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointGridMajorLineColor()); line.setLine( x0, t.value, x1, t.value); g2.draw(line); }else{ - g2.setColor(FinPointFigure.GRID_MINOR_LINE_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointGridMinorLineColor()); line.setLine( x0, t.value, x1, t.value); g2.draw(line); } @@ -170,7 +163,7 @@ private void paintBodyTransition( Graphics2D g2){ // setup lines final float bodyLineWidth = (float) ( LINE_WIDTH_BODY_PIXELS / scale ); g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor()); Transition body = (Transition) finset.getParent(); final float xResolution_m = 0.01f; // distance between draw points, in meters @@ -220,7 +213,7 @@ private void paintBodyTube( Graphics2D g2){ final float bodyLineWidth = (float) ( LINE_WIDTH_BODY_PIXELS / scale ); g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor()); g2.draw(shape); } @@ -237,7 +230,7 @@ private void paintFinShape(final Graphics2D g2){ final float finEdgeWidth_m = (float) (LINE_WIDTH_FIN_PIXELS / scale ); g2.setStroke(new BasicStroke( finEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor()); g2.draw(shape); } @@ -251,7 +244,7 @@ private void paintFinHandles(final Graphics2D g2) { final float boxEdgeWidth_m = (float) ( LINE_WIDTH_BOX_PIXELS / scale ); g2.setStroke(new BasicStroke( boxEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2.setColor(POINT_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointPointColor()); finPointHandles = new Rectangle2D.Double[ drawPoints.length]; for (int currentIndex = 0; currentIndex < drawPoints.length; currentIndex++) { @@ -264,11 +257,11 @@ private void paintFinHandles(final Graphics2D g2) { final Rectangle2D.Double selectedPointHighlight = new Rectangle2D.Double(c.x - selBoxHalfWidth, c.y - selBoxHalfWidth, selBoxWidth, selBoxWidth); // switch to the highlight color - g2.setColor(SELECTED_POINT_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointSelectedPointColor()); g2.draw(selectedPointHighlight); // reset to the normal color - g2.setColor(POINT_COLOR); + g2.setColor(GUIUtil.getUITheme().getFinPointPointColor()); } // normal boxes diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index df1ebb08d3..22f4d8d673 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -17,6 +17,7 @@ import java.util.*; import java.util.Map.Entry; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ParallelStage; import net.sf.openrocket.rocketcomponent.PodSet; @@ -257,7 +258,7 @@ else if (currentViewType == RocketPanel.VIEW_TYPE.BackView) // Set component color and line style net.sf.openrocket.util.Color color = rcs.color; if (color == null) { - color = Application.getPreferences().getDefaultColor(c.getClass()); + color = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass()); } g2.setColor(ColorConversion.toAwtColor(color)); @@ -290,8 +291,8 @@ else if (currentViewType == RocketPanel.VIEW_TYPE.BackView) RenderingHints.VALUE_STROKE_NORMALIZE); // Draw motors - Color fillColor = ((SwingPreferences)Application.getPreferences()).getMotorFillColor(); - Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor(); + Color fillColor = GUIUtil.getUITheme().getMotorFillColor(); + Color borderColor = GUIUtil.getUITheme().getMotorBorderColor(); FlightConfiguration config = rocket.getSelectedConfiguration(); for (MotorConfiguration curInstance : config.getActiveMotors()) { diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index bce0980f0e..7cf7642617 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -35,6 +35,7 @@ import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.logging.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; @@ -439,8 +440,7 @@ public void stateChanged(ChangeEvent e) { JPanel bottomRow = new JPanel(new MigLayout("fill, gapy 0, ins 0")); //// Click to select    Shift+click to select other    Double-click to edit    Click+drag to move - infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage")); - infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9)); + infoMessage = new StyledLabel(trans.get("RocketPanel.lbl.infoMessage"), -3); bottomRow.add(infoMessage); //// Show warnings diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java index db7ebad826..e30bfe8407 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java @@ -25,6 +25,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.unit.Tick; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; @@ -398,7 +399,7 @@ protected void paintComponent(Graphics g) { } // Set color & hints - g2.setColor(Color.BLACK); + g2.setColor(GUIUtil.getUITheme().getTextColor()); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.setRenderingHint(RenderingHints.KEY_RENDERING, diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java index c216b4f509..27de23c005 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java @@ -13,7 +13,7 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.util.Icons; -import net.sf.openrocket.gui.widgets.SelectColorButton; +import net.sf.openrocket.gui.widgets.IconButton; import net.sf.openrocket.util.StateChangeListener; @SuppressWarnings("serial") @@ -41,7 +41,7 @@ public ScaleSelector(ScaleScrollPane scroll) { this.scrollPane = scroll; // Zoom out button - zoomOutButton = new SelectColorButton(Icons.ZOOM_OUT); + zoomOutButton = new IconButton(Icons.ZOOM_OUT); zoomOutButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -93,7 +93,7 @@ public void stateChanged(EventObject e) { }); // Zoom in button - zoomInButton = new SelectColorButton(Icons.ZOOM_IN); + zoomInButton = new IconButton(Icons.ZOOM_IN); zoomInButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java index e105af540b..82e49ff9a9 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java @@ -207,6 +207,8 @@ public void actionPerformed(ActionEvent ev) { currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0")); JScrollPane scroll = new JScrollPane(currentExtensions); + currentExtensions.setBorder(GUIUtil.getUITheme().getBorder()); + scroll.setForeground(GUIUtil.getUITheme().getTextColor()); // &#$%! scroll pane will not honor "growy"... sub.add(scroll, "growx, growy, h 100%"); diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java index d926118a9c..ac2f980100 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java @@ -205,7 +205,7 @@ public void itemStateChanged(ItemEvent e) { //// The data will be plotted in time order even if the X axis type is not time. simPlotPanelDesc = new DescriptionArea("", 2, -2f, false); simPlotPanelDesc.setVisible(false); - simPlotPanelDesc.setForeground(Color.DARK_RED.toAWTColor()); + simPlotPanelDesc.setForeground(GUIUtil.getUITheme().getDarkWarningColor()); simPlotPanelDesc.setViewportBorder(BorderFactory.createEmptyBorder()); this.add(simPlotPanelDesc, "width 1px, growx 1, wrap unrel"); diff --git a/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java b/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java index b8bbdd40fe..a91da30c5a 100644 --- a/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java +++ b/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java @@ -1,9 +1,9 @@ package net.sf.openrocket.gui.util; + import javax.swing.DefaultListCellRenderer; import javax.swing.JLabel; import javax.swing.JList; -import java.awt.Color; import java.awt.Component; /** @@ -12,6 +12,7 @@ * @author Sibo Van Gool */ public class BetterListCellRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -20,16 +21,16 @@ public Component getListCellRendererComponent(JList list, Object value, int i // Alternating row colors if (!isSelected) { if (index % 2 == 0) { - label.setBackground(Color.WHITE); + label.setBackground(GUIUtil.getUITheme().getRowBackgroundDarkerColor()); } else { - label.setBackground(new Color(245, 245, 245)); + label.setBackground(GUIUtil.getUITheme().getRowBackgroundLighterColor()); } } // Text color if (isSelected) { - label.setForeground(Color.WHITE); + label.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor()); } else { - label.setForeground(Color.BLACK); + label.setForeground(GUIUtil.getUITheme().getTextColor()); } return label; } diff --git a/swing/src/net/sf/openrocket/gui/util/GUIUtil.java b/swing/src/net/sf/openrocket/gui/util/GUIUtil.java index af1d3d16d0..4fac6edd7a 100644 --- a/swing/src/net/sf/openrocket/gui/util/GUIUtil.java +++ b/swing/src/net/sf/openrocket/gui/util/GUIUtil.java @@ -64,9 +64,13 @@ import javax.swing.tree.TreeModel; import javax.swing.tree.TreeSelectionModel; +import com.github.weisj.darklaf.LafManager; +import com.github.weisj.darklaf.theme.IntelliJTheme; +import net.sf.openrocket.arch.SystemInfo; import net.sf.openrocket.gui.Resettable; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Invalidatable; import net.sf.openrocket.util.MemoryManagement; @@ -76,7 +80,7 @@ public class GUIUtil { private static final Logger log = LoggerFactory.getLogger(GUIUtil.class); - + private static final KeyStroke ESCAPE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); private static final String CLOSE_ACTION_KEY = "escape:WINDOW_CLOSING"; @@ -252,8 +256,24 @@ public void windowClosed(WindowEvent e) { } }); } + + /** + * Get the current theme used for the UI. + * @return the current theme + */ + public static UITheme.Theme getUITheme() { + Preferences prefs = Application.getPreferences(); + Object theme = prefs.getUITheme(); + if (theme instanceof UITheme.Theme) { + return (UITheme.Theme) theme; + } + return UITheme.Themes.LIGHT; + } - + public static void applyLAF() { + UITheme.Theme theme = getUITheme(); + theme.applyTheme(); + } /** * Set the best available look-and-feel into use. @@ -265,8 +285,13 @@ public static void setBestLAF() { * other alternatives. */ try { - // Set system L&F - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + // Linux systems often default to a dark mode LAF, so explicitly use light mode + if (SystemInfo.getPlatform() == SystemInfo.Platform.UNIX) { + LafManager.install(new IntelliJTheme()); + } else { + // Set system L&F + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } // Check whether we have an ugly L&F LookAndFeel laf = UIManager.getLookAndFeel(); @@ -295,6 +320,15 @@ public static void setBestLAF() { } // Set the select foreground for buttons to not be black on a blue background UIManager.put("Button.selectForeground", Color.WHITE); + + // Fix some UI bugs on macOS + if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) { + // Set the foreground of active tabs to black; there was a bug where you had a white background and white foreground + UIManager.put("TabbedPane.foreground", Color.black); + + // Set the select foreground for buttons to not be black on a blue background + UIManager.put("ToggleButton.selectForeground", Color.WHITE); + } } catch (Exception e) { log.warn("Error setting LAF: " + e); } diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java index 85f415983e..2d8a34ac43 100644 --- a/swing/src/net/sf/openrocket/gui/util/Icons.java +++ b/swing/src/net/sf/openrocket/gui/util/Icons.java @@ -94,13 +94,19 @@ public class Icons { public static final Icon NOT_FAVORITE = loadImageIcon("pix/icons/star_silver.png", "Not favorite"); public static final Icon FAVORITE = loadImageIcon("pix/icons/star_gold.png", "Favorite"); - - public static final Icon CG_OVERRIDE = loadImageIcon("pix/icons/cg-override.png", "CG Override"); - public static final Icon CG_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cg-override-subcomponent.png", "CG Override Subcomponent"); - public static final Icon CD_OVERRIDE = loadImageIcon("pix/icons/cd-override.png", "CD Override"); - public static final Icon CD_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cd-override-subcomponent.png", "CD Override Subcomponent"); - public static final Icon MASS_OVERRIDE = loadImageIcon("pix/icons/mass-override.png", "Mass Override"); - public static final Icon MASS_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/mass-override-subcomponent.png", "Mass Override Subcomponent"); + + public static final Icon MASS_OVERRIDE_LIGHT = loadImageIcon("pix/icons/mass-override_light.png", "Mass Override"); + public static final Icon MASS_OVERRIDE_DARK = loadImageIcon("pix/icons/mass-override_dark.png", "Mass Override"); + public static final Icon MASS_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/mass-override-subcomponent_light.png", "Mass Override Subcomponent"); + public static final Icon MASS_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/mass-override-subcomponent_dark.png", "Mass Override Subcomponent"); + public static final Icon CG_OVERRIDE_LIGHT = loadImageIcon("pix/icons/cg-override_light.png", "CG Override"); + public static final Icon CG_OVERRIDE_DARK = loadImageIcon("pix/icons/cg-override_dark.png", "CG Override"); + public static final Icon CG_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/cg-override-subcomponent_light.png", "CG Override Subcomponent"); + public static final Icon CG_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/cg-override-subcomponent_dark.png", "CG Override Subcomponent"); + public static final Icon CD_OVERRIDE_LIGHT = loadImageIcon("pix/icons/cd-override_light.png", "CD Override"); + public static final Icon CD_OVERRIDE_DARK = loadImageIcon("pix/icons/cd-override_dark.png", "CD Override"); + public static final Icon CD_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/cd-override-subcomponent_light.png", "CD Override Subcomponent"); + public static final Icon CD_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/cd-override-subcomponent_dark.png", "CD Override Subcomponent"); // MANUFACTURERS ICONS public static final Icon RASAERO = loadImageIcon("pix/icons/RASAero_16.png", "RASAero Icon"); diff --git a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java index 7e9db6af13..004895c02f 100644 --- a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java +++ b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -20,6 +21,17 @@ import java.util.prefs.Preferences; import net.sf.openrocket.communication.AssetHandler.UpdatePlatform; +import net.sf.openrocket.rocketcomponent.BodyComponent; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.InternalComponent; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.PodSet; +import net.sf.openrocket.rocketcomponent.RailButton; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.TubeFinSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +58,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { public static final String NODE_WINDOWS = "windows"; public static final String NODE_TABLES = "tables"; + private static final String UI_FONT_SIZE = "UIFontSize"; public static final String UPDATE_PLATFORM = "UpdatePlatform"; private static final List SUPPORTED_LOCALES; @@ -58,6 +71,8 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { list.add(new Locale("uk", "UA")); SUPPORTED_LOCALES = Collections.unmodifiableList(list); } + + private final HashMap, String> DEFAULT_COLORS = new HashMap<>(); /** @@ -94,8 +109,22 @@ public SwingPreferences() { } } PREFNODE = root.node(NODENAME); + fillDefaultComponentColors(); } - + + private void fillDefaultComponentColors() { + DEFAULT_COLORS.put(BodyComponent.class, getUIThemeAsTheme().getDefaultBodyComponentColor()); + DEFAULT_COLORS.put(TubeFinSet.class, getUIThemeAsTheme().getDefaultTubeFinSetColor()); + DEFAULT_COLORS.put(FinSet.class, getUIThemeAsTheme().getDefaultFinSetColor()); + DEFAULT_COLORS.put(LaunchLug.class, getUIThemeAsTheme().getDefaultLaunchLugColor()); + DEFAULT_COLORS.put(RailButton.class, getUIThemeAsTheme().getDefaultRailButtonColor()); + DEFAULT_COLORS.put(InternalComponent.class, getUIThemeAsTheme().getDefaultInternalComponentColor()); + DEFAULT_COLORS.put(MassObject.class, getUIThemeAsTheme().getDefaultMassObjectColor()); + DEFAULT_COLORS.put(RecoveryDevice.class, getUIThemeAsTheme().getDefaultRecoveryDeviceColor()); + DEFAULT_COLORS.put(PodSet.class, getUIThemeAsTheme().getDefaultPodSetColor()); + DEFAULT_COLORS.put(ParallelStage.class, getUIThemeAsTheme().getDefaultParallelStageColor()); + } + public String getNodename() { return NODENAME; } @@ -297,6 +326,82 @@ public Preferences getNode(String nodeName) { public static List getSupportedLocales() { return SUPPORTED_LOCALES; } + + /** + * Get the current theme used for the UI. + * @return the current theme + */ + @Override + public Object getUITheme() { + return getUIThemeAsTheme(); + } + + private UITheme.Theme getUIThemeAsTheme() { + String themeName = getString(net.sf.openrocket.startup.Preferences.UI_THEME, UITheme.Themes.LIGHT.name()); + if (themeName == null) return UITheme.Themes.LIGHT; // Default theme + return UITheme.Themes.valueOf(themeName); + } + + /** + * Set the theme used for the UI. + * @param theme the theme to set + */ + @Override + public void setUITheme(Object theme) { + if (!(theme instanceof UITheme.Theme)) return; + putString(net.sf.openrocket.startup.Preferences.UI_THEME, ((UITheme.Theme) theme).name()); + storeVersion(); + } + + /** + * Get the current font size used for the UI. + * @return the current font size + */ + public int getUIFontSize() { + return getInt(UI_FONT_SIZE, getDefaultFontSize()); + } + + public final float getRocketInfoFontSize() { + return (float) ((getUIFontSize() - 2) + 3 * Application.getPreferences().getChoice(net.sf.openrocket.startup.Preferences.ROCKET_INFO_FONT_SIZE, 2, 0)); + } + + private static int getDefaultFontSize() { + javax.swing.UIDefaults uiDefaults = javax.swing.UIManager.getDefaults(); + Object value = uiDefaults.get("defaultFont"); + if (value instanceof javax.swing.plaf.FontUIResource fontUIResource) { + return fontUIResource.getSize(); + } else { + return 12; + } + } + + /** + * Set the font size used for the UI. + * @param size the font size to set + */ + public void setUIFontSize(int size) { + putInt(UI_FONT_SIZE, size); + storeVersion(); + } + + public net.sf.openrocket.util.Color getDefaultColor(Class c) { + String color = get("componentColors", c, DEFAULT_COLORS); + if (color == null) + return net.sf.openrocket.util.Color.fromAWTColor(getUIThemeAsTheme().getTextColor()); + + net.sf.openrocket.util.Color clr = parseColor(color); + if (clr != null) { + return clr; + } else { + return net.sf.openrocket.util.Color.fromAWTColor(getUIThemeAsTheme().getTextColor()); + } + } + + public final void setDefaultColor(Class c, net.sf.openrocket.util.Color color) { + if (color == null) + return; + putString("componentColors", c.getSimpleName(), stringifyColor(color)); + } public File getDefaultDirectory() { String file = getString(net.sf.openrocket.startup.Preferences.DEFAULT_DIRECTORY, null); @@ -432,18 +537,6 @@ public void setUserThrustCurveFiles(List files) { putString(USER_THRUST_CURVES_KEY, str); } - public Color getMotorBorderColor() { - // TODO: MEDIUM: Motor color (settable?) - return new Color(0, 0, 0, 200); - } - - - public Color getMotorFillColor() { - // TODO: MEDIUM: Motor fill color (settable?) - return new Color(0, 0, 0, 100); - } - - public static int getMaxThreadCount() { return Runtime.getRuntime().availableProcessors(); } diff --git a/swing/src/net/sf/openrocket/gui/util/UITheme.java b/swing/src/net/sf/openrocket/gui/util/UITheme.java new file mode 100644 index 0000000000..43805d8700 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/util/UITheme.java @@ -0,0 +1,572 @@ +package net.sf.openrocket.gui.util; + +import com.github.weisj.darklaf.LafManager; +import com.github.weisj.darklaf.theme.DarculaTheme; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.UIManager; +import javax.swing.border.Border; +import java.awt.Color; +import java.awt.Font; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +public class UITheme { + private static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(UITheme.class); + + public interface Theme { + void applyTheme(); + String name(); // Provided by enum, gives the name of the enum constant + String getDisplayName(); + Color getBackgroundColor(); + Color getBorderColor(); + Color getTextColor(); + Color getDimTextColor(); + Color getTextSelectionForegroundColor(); + Color getTextSelectionBackgroundColor(); + Color getWarningColor(); + Color getDarkWarningColor(); + Color getRowBackgroundLighterColor(); + Color getRowBackgroundDarkerColor(); + Color getFlightDataTextActiveColor(); + Color getFlightDataTextInactiveColor(); + + // Component colors + String getDefaultBodyComponentColor(); + String getDefaultTubeFinSetColor(); + String getDefaultFinSetColor(); + String getDefaultLaunchLugColor(); + String getDefaultRailButtonColor(); + String getDefaultInternalComponentColor(); + String getDefaultMassObjectColor(); + String getDefaultRecoveryDeviceColor(); + String getDefaultPodSetColor(); + String getDefaultParallelStageColor(); + + Color getMotorBorderColor(); + Color getMotorFillColor(); + + Color getCGColor(); + Color getCPColor(); + + Color getURLColor(); + + Color getComponentTreeBackgroundColor(); + Color getComponentTreeForegroundColor(); + + Color getFinPointGridMajorLineColor(); + Color getFinPointGridMinorLineColor(); + Color getFinPointPointColor(); + Color getFinPointSelectedPointColor(); + Color getFinPointBodyLineColor(); + + Icon getMassOverrideIcon(); + Icon getMassOverrideSubcomponentIcon(); + Icon getCGOverrideIcon(); + Icon getCGOverrideSubcomponentIcon(); + Icon getCDOverrideIcon(); + Icon getCDOverrideSubcomponentIcon(); + + Border getBorder(); + + void formatScriptTextArea(RSyntaxTextArea textArea); + } + + public enum Themes implements Theme { + LIGHT { + private final String displayName = trans.get("UITheme.Light"); + + @Override + public void applyTheme() { + final SwingPreferences prefs = (SwingPreferences) Application.getPreferences(); + + GUIUtil.setBestLAF(); + setGlobalFontSize(prefs.getUIFontSize()); + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public Color getBackgroundColor() { + return Color.WHITE; + } + + @Override + public Color getBorderColor() { + return Color.BLACK; + } + + @Override + public Color getTextColor() { + return Color.BLACK; + } + + @Override + public Color getDimTextColor() { + return Color.GRAY; + } + + @Override + public Color getTextSelectionForegroundColor() { + return UIManager.getColor("Tree.selectionForeground"); + } + + @Override + public Color getTextSelectionBackgroundColor() { + return UIManager.getColor("Tree.selectionBackground"); + } + + @Override + public Color getWarningColor() { + return Color.RED; + } + + @Override + public Color getDarkWarningColor() { + return new Color(200,0,0); + } + + @Override + public Color getRowBackgroundLighterColor() { + return Color.WHITE; + } + + @Override + public Color getRowBackgroundDarkerColor() { + return new Color(245, 245, 245); + } + + @Override + public Color getFlightDataTextActiveColor() { + return new Color(0,0,127); + } + + @Override + public Color getFlightDataTextInactiveColor() { + return new Color(0,0,127,127); + } + + @Override + public String getDefaultBodyComponentColor() { + return "0,0,240"; + } + @Override + public String getDefaultTubeFinSetColor() { + return "0,0,200"; + } + @Override + public String getDefaultFinSetColor() { + return "0,0,200"; + } + @Override + public String getDefaultLaunchLugColor() { + return "0,0,180"; + } + @Override + public String getDefaultRailButtonColor() { + return "0,0,180"; + } + @Override + public String getDefaultInternalComponentColor() { + return "170,0,100"; + } + @Override + public String getDefaultMassObjectColor() { + return "0,0,0"; + } + @Override + public String getDefaultRecoveryDeviceColor() { + return "255,0,0"; + } + @Override + public String getDefaultPodSetColor() { + return "160,160,215"; + } + @Override + public String getDefaultParallelStageColor() { + return "198,163,184"; + } + + @Override + public Color getMotorBorderColor() { + return new Color(0, 0, 0, 200); + } + + @Override + public Color getMotorFillColor() { + return new Color(0, 0, 0, 100); + } + + @Override + public Color getCGColor() { + return Color.BLUE; + } + + @Override + public Color getCPColor() { + return Color.RED; + } + + @Override + public Color getURLColor() { + return Color.BLUE; + } + + @Override + public Color getComponentTreeBackgroundColor() { + return UIManager.getColor("Tree.textBackground"); + } + + @Override + public Color getComponentTreeForegroundColor() { + return UIManager.getColor("Tree.textForeground"); + } + + @Override + public Color getFinPointGridMajorLineColor() { + return new Color( 0, 0, 255, 80); + } + + @Override + public Color getFinPointGridMinorLineColor() { + return new Color( 0, 0, 255, 30); + } + + @Override + public Color getFinPointPointColor() { + return new Color(200, 0, 0, 255); + } + + @Override + public Color getFinPointSelectedPointColor() { + return new Color(200, 0, 0, 255); + } + + @Override + public Color getFinPointBodyLineColor() { + return Color.BLACK; + } + + @Override + public Icon getMassOverrideIcon() { + return Icons.MASS_OVERRIDE_LIGHT; + } + + @Override + public Icon getMassOverrideSubcomponentIcon() { + return Icons.MASS_OVERRIDE_SUBCOMPONENT_LIGHT; + } + + @Override + public Icon getCGOverrideIcon() { + return Icons.CG_OVERRIDE_LIGHT; + } + + @Override + public Icon getCGOverrideSubcomponentIcon() { + return Icons.CG_OVERRIDE_SUBCOMPONENT_LIGHT; + } + + @Override + public Icon getCDOverrideIcon() { + return Icons.CD_OVERRIDE_LIGHT; + } + + @Override + public Icon getCDOverrideSubcomponentIcon() { + return Icons.CD_OVERRIDE_SUBCOMPONENT_LIGHT; + } + + @Override + public Border getBorder() { + return null; + } + + @Override + public void formatScriptTextArea(RSyntaxTextArea textArea) { + try { + org.fife.ui.rsyntaxtextarea.Theme theme = org.fife.ui.rsyntaxtextarea.Theme.load(getClass().getResourceAsStream( + "/org/fife/ui/rsyntaxtextarea/themes/default.xml")); + theme.apply(textArea); + textArea.setCurrentLineHighlightColor(new Color(255, 255, 230)); + } catch (IOException ioe) { + log.warn("Unable to load RSyntaxTextArea theme", ioe); + } + } + }, + DARK { + private final String displayName = trans.get("UITheme.Dark"); + @Override + public void applyTheme() { + final SwingPreferences prefs = (SwingPreferences) Application.getPreferences(); + + LafManager.install(new DarculaTheme()); + setGlobalFontSize(prefs.getUIFontSize()); + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public Color getBackgroundColor() { + return new Color(73, 76, 79); + } + + @Override + public Color getBorderColor() { + return new Color(97, 99, 101); + } + + @Override + public Color getTextColor() { + return UIManager.getColor("Label.foreground"); + } + + @Override + public Color getDimTextColor() { + return new Color(162, 162, 162); + } + + @Override + public Color getTextSelectionForegroundColor() { + return Color.WHITE; + } + + @Override + public Color getTextSelectionBackgroundColor() { + return new Color(75, 110, 175); + } + + @Override + public Color getWarningColor() { + return new Color(246, 143, 143); + } + + @Override + public Color getDarkWarningColor() { + return new Color(229, 103, 103); + } + + @Override + public Color getRowBackgroundLighterColor() { + return new Color(65, 69, 71); + } + + @Override + public Color getRowBackgroundDarkerColor() { + return new Color(60, 63, 65); + } + + @Override + public Color getFlightDataTextActiveColor() { + return new Color(145, 183, 231); + } + + @Override + public Color getFlightDataTextInactiveColor() { + return new Color(128, 166, 230, 127); + } + + @Override + public String getDefaultBodyComponentColor() { + return "150,162,255"; + } + @Override + public String getDefaultTubeFinSetColor() { + return "150,178,255"; + } + @Override + public String getDefaultFinSetColor() { + return "150,178,255"; + } + @Override + public String getDefaultLaunchLugColor() { + return "142,153,238"; + } + @Override + public String getDefaultRailButtonColor() { + return "142,153,238"; + } + @Override + public String getDefaultInternalComponentColor() { + return "181,128,151"; + } + @Override + public String getDefaultMassObjectColor() { + return "210,210,210"; + } + @Override + public String getDefaultRecoveryDeviceColor() { + return "220,90,90"; + } + @Override + public String getDefaultPodSetColor() { + return "190,190,235"; + } + @Override + public String getDefaultParallelStageColor() { + return "210,180,195"; + } + + @Override + public Color getMotorBorderColor() { + return new Color(0, 0, 0, 100); + } + + @Override + public Color getMotorFillColor() { + return new Color(0, 0, 0, 50); + } + + @Override + public Color getCGColor() { + return new Color(85, 133, 253); + } + + @Override + public Color getCPColor() { + return new Color(255, 72, 106); + } + + @Override + public Color getURLColor() { + return new Color(150, 167, 255); + } + + @Override + public Color getComponentTreeBackgroundColor() { + return getBackgroundColor(); + } + + @Override + public Color getComponentTreeForegroundColor() { + return getTextColor(); + } + + @Override + public Color getFinPointGridMajorLineColor() { + return new Color(135, 135, 199, 197); + } + + @Override + public Color getFinPointGridMinorLineColor() { + return new Color(121, 121, 189, 69); + } + + @Override + public Color getFinPointPointColor() { + return new Color(217, 108, 108, 255); + } + + @Override + public Color getFinPointSelectedPointColor() { + return new Color(232, 78, 78, 255); + } + + @Override + public Color getFinPointBodyLineColor() { + return Color.WHITE; + } + + @Override + public Icon getMassOverrideIcon() { + return Icons.MASS_OVERRIDE_DARK; + } + + @Override + public Icon getMassOverrideSubcomponentIcon() { + return Icons.MASS_OVERRIDE_SUBCOMPONENT_DARK; + } + + @Override + public Icon getCGOverrideIcon() { + return Icons.CG_OVERRIDE_DARK; + } + + @Override + public Icon getCGOverrideSubcomponentIcon() { + return Icons.CG_OVERRIDE_SUBCOMPONENT_DARK; + } + + @Override + public Icon getCDOverrideIcon() { + return Icons.CD_OVERRIDE_DARK; + } + + @Override + public Icon getCDOverrideSubcomponentIcon() { + return Icons.CD_OVERRIDE_SUBCOMPONENT_DARK; + } + + @Override + public Border getBorder() { + return BorderFactory.createLineBorder(getBorderColor()); + } + + @Override + public void formatScriptTextArea(RSyntaxTextArea textArea) { + try { + org.fife.ui.rsyntaxtextarea.Theme theme = org.fife.ui.rsyntaxtextarea.Theme.load(getClass().getResourceAsStream( + "/org/fife/ui/rsyntaxtextarea/themes/dark.xml")); + theme.apply(textArea); + } catch (IOException ioe) { + log.warn("Unable to load RSyntaxTextArea theme", ioe); + } + } + } + } + + private static void setGlobalFontSize(int size) { + // Some fonts have different sizes for different components, so we need to adjust them + final Map fontOffsets = new HashMap<>(); + fontOffsets.put("MenuBar.font", 1f); + fontOffsets.put("Tree.font", -1f); + fontOffsets.put("Slider.font", -2f); + fontOffsets.put("TableHeader.font", -2f); + fontOffsets.put("ColorChooser.font", -1f); + fontOffsets.put("Menu.acceleratorFont", 1f); + fontOffsets.put("InternalFrame.optionDialogTitleFont", 1f); + fontOffsets.put("InternalFrame.paletteTitleFont", 1f); + fontOffsets.put("MenuItem.font", 1f); + fontOffsets.put("PopupMenu.font", 1f); + fontOffsets.put("MenuItem.acceleratorFont", 1f); + fontOffsets.put("RadioButtonMenuItem.font", 1f); + fontOffsets.put("Table.font", -1f); + //fontOffsets.put("IconButton.font", -2f); // The default doesn't really look nice, we want the normal font size instead + fontOffsets.put("InternalFrame.titleFont", 1f); + fontOffsets.put("List.font", -1f); + fontOffsets.put("RadioButtonMenuItem.acceleratorFont", 1f); + fontOffsets.put("CheckBoxMenuItem.acceleratorFont", 1f); + fontOffsets.put("Menu.font", 1f); + fontOffsets.put("TabbedPane.smallFont", -2f); + fontOffsets.put("CheckBoxMenuItem.font", 1f); + fontOffsets.put("ToolTip.font", -2f); + + // Iterate over all keys in the UIManager defaults and set the font + for (Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();) { + Object key = keys.nextElement(); + Object value = UIManager.get(key); + if (value instanceof Font) { + Font newFont = (Font) value; + float offset = fontOffsets.getOrDefault(key.toString(), 0f); + newFont = newFont.deriveFont(Integer.valueOf(size).floatValue() + offset); + UIManager.put(key, newFont); + } + } + } + +} diff --git a/swing/src/net/sf/openrocket/gui/widgets/SelectColorButton.java b/swing/src/net/sf/openrocket/gui/widgets/SelectColorButton.java index c9229887c2..db7b4d4d9b 100644 --- a/swing/src/net/sf/openrocket/gui/widgets/SelectColorButton.java +++ b/swing/src/net/sf/openrocket/gui/widgets/SelectColorButton.java @@ -1,5 +1,7 @@ package net.sf.openrocket.gui.widgets; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.UITheme; import javax.swing.Action; import javax.swing.Icon; @@ -7,11 +9,11 @@ import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.Graphics; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; public class SelectColorButton extends JButton { + public SelectColorButton() { addChangeListenerSelectColor(); } @@ -37,8 +39,9 @@ public SelectColorButton(String text, Icon icon) { } private void addChangeListenerSelectColor() { - if (UIManager.getColor("Button.selectForeground") == null - || UIManager.getColor("Button.foreground") == null) + if ((GUIUtil.getUITheme() != UITheme.Themes.LIGHT) || + (UIManager.getColor("Button.selectForeground") == null) || + (UIManager.getColor("Button.foreground") == null)) return; // Fixes the issue of the background of the button not being blue when selected on macOS diff --git a/swing/src/net/sf/openrocket/gui/widgets/SelectColorToggleButton.java b/swing/src/net/sf/openrocket/gui/widgets/SelectColorToggleButton.java index 8ed0873b06..a29355f26d 100644 --- a/swing/src/net/sf/openrocket/gui/widgets/SelectColorToggleButton.java +++ b/swing/src/net/sf/openrocket/gui/widgets/SelectColorToggleButton.java @@ -1,5 +1,8 @@ package net.sf.openrocket.gui.widgets; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.UITheme; + import javax.swing.JToggleButton; import javax.swing.Action; import javax.swing.Icon; @@ -14,6 +17,7 @@ * @author Sibo Van Gool */ public class SelectColorToggleButton extends JToggleButton { + public SelectColorToggleButton(Action a) { super(a); addChangeListenerSelectColor(); @@ -60,8 +64,9 @@ public SelectColorToggleButton(String text, Icon icon, boolean selected) { * This is to fix an issue on OSX devices where the foreground color would be black on blue (hardly readable) */ private void addChangeListenerSelectColor() { - if (UIManager.getColor("ToggleButton.selectForeground") == null - || UIManager.getColor("ToggleButton.foreground") == null) + if ((GUIUtil.getUITheme() != UITheme.Themes.LIGHT) || + (UIManager.getColor("ToggleButton.selectForeground") == null) || + (UIManager.getColor("ToggleButton.foreground") == null)) return; // Fixes the issue of the background of the button not being blue when selected on macOS diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/JavaCodeConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/impl/JavaCodeConfigurator.java index ba9fe43b6e..eea237e112 100644 --- a/swing/src/net/sf/openrocket/simulation/extension/impl/JavaCodeConfigurator.java +++ b/swing/src/net/sf/openrocket/simulation/extension/impl/JavaCodeConfigurator.java @@ -9,11 +9,11 @@ import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.plugin.Plugin; import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Color; @Plugin public class JavaCodeConfigurator extends AbstractSwingSimulationExtensionConfigurator { @@ -35,7 +35,7 @@ protected JComponent getConfigurationComponent(final JavaCode extension, Simulat classNameField = new JTextField(extension.getClassName()); panel.add(classNameField, "growx, wrap"); this.errorMsg = new StyledLabel(); - errorMsg.setFontColor(Color.DARK_RED.toAWTColor()); + errorMsg.setFontColor(GUIUtil.getUITheme().getDarkWarningColor()); errorMsg.setVisible(false); panel.add(errorMsg, "growx, wrap"); diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java index 178d15cca7..a49a47b759 100644 --- a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java +++ b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java @@ -19,6 +19,7 @@ import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.plugin.Plugin; import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator; import net.sf.openrocket.gui.widgets.SelectColorButton; @@ -41,7 +42,7 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi private ScriptingExtension extension; private Simulation simulation; - + public ScriptingConfigurator() { super(ScriptingExtension.class); } @@ -70,7 +71,7 @@ public void actionPerformed(ActionEvent e) { text.setLineWrap(true); text.setWrapStyleWord(true); text.setEditable(true); - text.setCurrentLineHighlightColor(new Color(255, 255, 230)); + GUIUtil.getUITheme().formatScriptTextArea(text); text.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent event) { diff --git a/swing/src/net/sf/openrocket/startup/OSXSetup.java b/swing/src/net/sf/openrocket/startup/OSXSetup.java index d2c14140ee..cd78b53b9e 100644 --- a/swing/src/net/sf/openrocket/startup/OSXSetup.java +++ b/swing/src/net/sf/openrocket/startup/OSXSetup.java @@ -9,8 +9,6 @@ import java.io.File; import net.sf.openrocket.communication.UpdateInfoRetriever; -import net.sf.openrocket.gui.main.MRUDesignFile; -import net.sf.openrocket.gui.util.DummyFrameMenuOSX; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +18,6 @@ import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.main.BasicFrame; -import javax.swing.*; /** * Static code for initialization of OSX UI Elements: Menu, Icon, Name and @@ -117,12 +114,6 @@ static void setupOSX() { final Taskbar osxTaskbar = Taskbar.getTaskbar(); osxTaskbar.setIconImage(dockIcon); - // Set the foreground of active tabs to black; there was a bug where you had a white background and white foreground - UIManager.put("TabbedPane.foreground", Color.black); - - // Set the select foreground for buttons to not be black on a blue background - UIManager.put("ToggleButton.selectForeground", Color.WHITE); - } catch (final Throwable t) { // None of the preceding is critical to the app, // so at worst case log an error and continue diff --git a/swing/src/net/sf/openrocket/startup/SwingStartup.java b/swing/src/net/sf/openrocket/startup/SwingStartup.java index 3ee7c32f9f..35763cddc5 100644 --- a/swing/src/net/sf/openrocket/startup/SwingStartup.java +++ b/swing/src/net/sf/openrocket/startup/SwingStartup.java @@ -29,6 +29,7 @@ import net.sf.openrocket.gui.main.SwingExceptionHandler; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.util.UITheme; import net.sf.openrocket.logging.LoggingSystemSetup; import net.sf.openrocket.logging.PrintStreamToSLF4J; import net.sf.openrocket.plugin.PluginModule; @@ -198,9 +199,15 @@ private void runInEDT(String[] args) { // Start update info fetching final UpdateInfoRetriever updateRetriever = startUpdateChecker(); - // Set the best available look-and-feel - log.info("Setting best LAF"); - GUIUtil.setBestLAF(); + // Set the look-and-feel + log.info("Setting LAF"); + String cmdLAF = System.getProperty("openrocket.laf"); + if (cmdLAF != null) { + log.info("Setting cmd line LAF '{}'", cmdLAF); + Preferences prefs = Application.getPreferences(); + prefs.setUITheme(UITheme.Themes.valueOf(cmdLAF)); + } + GUIUtil.applyLAF(); // Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively. ToolTipManager.sharedInstance().setDismissDelay(30000);