1010 ******************************************************************************/
1111package ch .elexis .core .findings .ui .composites ;
1212
13+ import java .lang .reflect .InvocationTargetException ;
1314import java .time .LocalDate ;
1415import java .util .Comparator ;
1516import java .util .List ;
2223import org .eclipse .jface .action .MenuManager ;
2324import org .eclipse .jface .action .ToolBarManager ;
2425import org .eclipse .jface .dialogs .Dialog ;
26+ import org .eclipse .jface .dialogs .ProgressMonitorDialog ;
2527import org .eclipse .jface .resource .ImageDescriptor ;
2628import org .eclipse .jface .viewers .ISelection ;
2729import org .eclipse .jface .viewers .StructuredSelection ;
3436import org .eclipse .swt .layout .GridLayout ;
3537import org .eclipse .swt .widgets .Composite ;
3638import org .eclipse .swt .widgets .Display ;
39+ import org .eclipse .swt .widgets .Shell ;
3740import org .eclipse .swt .widgets .ToolBar ;
3841import org .eclipse .ui .forms .widgets .ScrolledForm ;
42+ import org .slf4j .LoggerFactory ;
3943
4044import ca .odell .glazedlists .BasicEventList ;
4145import ca .odell .glazedlists .EventList ;
5054import ch .elexis .core .findings .ui .services .CodingServiceComponent ;
5155import ch .elexis .core .findings .ui .services .FindingsServiceComponent ;
5256import ch .elexis .core .l10n .Messages ;
57+ import ch .elexis .core .lock .types .LockResponse ;
5358import ch .elexis .core .model .IPatient ;
5459import ch .elexis .core .services .LocalConfigService ;
5560import ch .elexis .core .services .holder .ConfigServiceHolder ;
5661import ch .elexis .core .services .holder .ContextServiceHolder ;
5762import ch .elexis .core .text .docx .util .TextUtil ;
5863import ch .elexis .core .ui .icons .Images ;
59- import ch .elexis .core .ui .locks .AcquireLockBlockingUi ;
6064import ch .elexis .core .ui .locks .AcquireLockUi ;
6165import ch .elexis .core .ui .locks .ILockHandler ;
66+ import ch .elexis .core .ui .locks .LockResponseHelper ;
6267import ch .elexis .core .ui .util .NatTableFactory ;
6368import ch .elexis .core .ui .util .NatTableWrapper ;
6469import ch .elexis .core .ui .util .NatTableWrapper .IDoubleClickListener ;
@@ -76,6 +81,12 @@ public class DiagnoseListComposite extends Composite {
7681 private NatTableWrapper natTableWrapper ;
7782 private ToolBarManager toolbarManager ;
7883
84+ private ConditionEditDialog editDialog ;
85+
86+ private static final String PROPERTY_PRELOAD = "elexis.diagnose.preloadEditor" ; //$NON-NLS-1$
87+
88+ private static final int PRELOAD_DELAY_MILLIS = 1500 ;
89+
7990 private EventList <ICondition > dataList = new BasicEventList <>();
8091
8192 @ SuppressWarnings ("deprecation" )
@@ -120,32 +131,36 @@ private Object getAlternativeFormattedText(ICondition condition) {
120131
121132 ConditionStatus status = condition .getStatus ();
122133 text .append (status .getLocalized ());
123- text . append ( "<br/>" );
134+ StringBuilder secondLine = new StringBuilder ( );
124135 Optional <String > start = condition .getStart ();
125136 if (start .isPresent () && StringUtils .isNotBlank (start .get ())) {
126- text .append (start .get ());
137+ secondLine .append (start .get ());
127138 }
128139
129140 Optional <String > end = condition .getEnd ();
130141 if (end .isPresent () && StringUtils .isNotBlank (end .get ())) {
131- text .append (" - " ).append (end .get ());
142+ secondLine .append (" - " ).append (end .get ());
132143 }
133144
134145 List <ICoding > codings = condition .getCoding ();
135146 if (codings != null && !codings .isEmpty ()) {
136147 for (ICoding iCoding : codings ) {
137- text .append (" [" ).append (CodingServiceComponent .getService ().getShortLabel (iCoding ))
148+ secondLine .append (" [" )
149+ .append (CodingServiceComponent .getService ().getShortLabel (iCoding ))
138150 .append ("]" );
139151 }
140152 }
153+ if (secondLine .length () > 0 ) {
154+ text .append ("<br/>" ).append (secondLine );
155+ }
141156 text .append ("</strong>" );
142157
143158 boolean hasText = condition .getText ().isPresent ()
144159 && StringUtils .isNotBlank (condition .getText ().get ());
145160 boolean hasNotes = !condition .getNotes ().isEmpty ();
146161
147162 if (hasText || hasNotes ) {
148- text .append ("<br/ ><br/>" );
163+ text .append ("<p ><br/>" );
149164 }
150165
151166 if (hasText ) {
@@ -162,8 +177,6 @@ private Object getAlternativeFormattedText(ICondition condition) {
162177 }
163178 }
164179
165- text .append ("<br/>" );
166-
167180 return text .toString ();
168181 }
169182
@@ -226,23 +239,11 @@ public void setDataValue(ICondition condition, int arg1, Object arg2) {
226239 public void doubleClick (NatTableWrapper source , ISelection selection ) {
227240 if (selection instanceof StructuredSelection && !selection .isEmpty ()) {
228241 ICondition condition = (ICondition ) ((StructuredSelection ) selection ).getFirstElement ();
229- AcquireLockBlockingUi .aquireAndRun (condition , new ILockHandler () {
230- @ Override
231- public void lockFailed () {
232- // do nothing
233- }
234-
235- @ Override
236- public void lockAcquired () {
237- ConditionEditDialog dialog = new ConditionEditDialog (condition ,
238- Display .getDefault ().getActiveShell ());
239- if (dialog .open () == Dialog .OK ) {
240- dialog .getCondition ().ifPresent (c -> {
241- source .getNatTable ().refresh ();
242- });
243- }
244- }
245- });
242+ if (editDialog != null && editDialog .isOpen ()) {
243+ editDialog .getShell ().setActive ();
244+ return ;
245+ }
246+ editCondition (condition , source );
246247 }
247248 }
248249 });
@@ -258,6 +259,95 @@ public void lockAcquired() {
258259 ToolBar toolbar = toolbarManager .createControl (this );
259260 toolbar .setLayoutData (new GridData (SWT .RIGHT , SWT .TOP , false , false ));
260261 toolbar .setBackground (parent .getBackground ());
262+
263+ addDisposeListener (e -> disposeEditDialog ());
264+ }
265+
266+ /**
267+ * Locks the condition, edits it, releases the lock. The lock is acquired exactly
268+ * as {@code AcquireLockBlockingUi} does it blocking, with a cancelable progress
269+ * dialog but that dialog is closed again before the editor opens, and the lock
270+ * is only released once editing is done.
271+ * <p>
272+ * The order is what makes a kept editor possible at all. {@code AcquireLockBlockingUi}
273+ * calls its handler while its progress dialog is still up, and that dialog is
274+ * application modal and disposed right afterwards: an editor built next to it would
275+ * be blocked by its modality, and one built below it would be disposed with it.
276+ * Since {@link ICondition} is an {@code Identifiable}, it takes the overload without
277+ * the standalone shortcut, so that progress dialog appears even without a lock server.
278+ */
279+ private void editCondition (ICondition condition , NatTableWrapper source ) {
280+ LockResponse lockResponse = acquireLockBlocking (condition );
281+ if (lockResponse == null ) {
282+ return ;
283+ }
284+ if (!lockResponse .isOk ()) {
285+ LockResponseHelper .showInfo (lockResponse , condition , LoggerFactory .getLogger (getClass ()));
286+ return ;
287+ }
288+ try {
289+ if (openEditDialog (condition , condition .getCategory (), getShell ()) == Dialog .OK && editDialog != null ) {
290+ editDialog .getCondition ().ifPresent (c -> {
291+ source .getNatTable ().refresh ();
292+ });
293+ }
294+ } finally {
295+ LocalLockServiceHolder .get ().releaseLock (lockResponse .getLockInfo ());
296+ }
297+ }
298+
299+ private LockResponse acquireLockBlocking (ICondition condition ) {
300+ LockResponse [] result = new LockResponse [1 ];
301+ try {
302+ new ProgressMonitorDialog (getShell ()).run (true , true ,
303+ monitor -> result [0 ] = LocalLockServiceHolder .get ().acquireLockBlocking (condition , 30 , monitor ));
304+ } catch (InvocationTargetException | InterruptedException e ) {
305+ LoggerFactory .getLogger (getClass ()).warn ("Exception during acquire lock." , e ); //$NON-NLS-1$
306+ return null ;
307+ }
308+ return result [0 ];
309+ }
310+
311+ private int openEditDialog (ICondition condition , ConditionCategory category , Shell parentShell ) {
312+ if (editDialog != null && !editDialog .canReuseFor (parentShell , category )) {
313+ disposeEditDialog ();
314+ }
315+ if (editDialog == null ) {
316+ editDialog = condition != null ? new ConditionEditDialog (condition , parentShell )
317+ : new ConditionEditDialog (category , parentShell );
318+ editDialog .setReusable (true );
319+ } else {
320+ editDialog .setCondition (condition );
321+ }
322+ return editDialog .open ();
323+ }
324+
325+ private void disposeEditDialog () {
326+ if (editDialog != null ) {
327+ editDialog .dispose ();
328+ editDialog = null ;
329+ }
330+ }
331+
332+ private void preloadEditDialog () {
333+ if (editDialog != null || isDisposed () || getShell () == null
334+ || !Boolean .parseBoolean (System .getProperty (PROPERTY_PRELOAD , Boolean .TRUE .toString ()))) {
335+ return ;
336+ }
337+ getDisplay ().timerExec (PRELOAD_DELAY_MILLIS , () -> {
338+ if (editDialog != null || isDisposed () || getShell () == null || getShell ().isDisposed ()) {
339+ return ;
340+ }
341+ try {
342+ ConditionEditDialog preloaded = new ConditionEditDialog (ConditionCategory .PROBLEMLISTITEM , getShell ());
343+ preloaded .setReusable (true );
344+ preloaded .preload ();
345+ editDialog = preloaded ;
346+ } catch (Exception e ) {
347+ LoggerFactory .getLogger (getClass ()).warn ("Could not preload the condition dialog" , e ); //$NON-NLS-1$
348+ disposeEditDialog ();
349+ }
350+ });
261351 }
262352
263353 private static void appendFormattedLine (StringBuilder text , String line ) {
@@ -286,6 +376,8 @@ public int compare(ICondition left, ICondition right) {
286376 dataList .addAll (conditions );
287377 natTableWrapper .getNatTable ().refresh ();
288378
379+ preloadEditDialog ();
380+
289381 Display .getDefault ().asyncExec (() -> {
290382 if (!isDisposed ()) {
291383 Composite parent = getParent ();
@@ -396,9 +488,13 @@ public String getText() {
396488 public void run () {
397489 IPatient selectedPatient = ContextServiceHolder .get ().getActivePatient ().orElse (null );
398490 if (selectedPatient != null ) {
399- ConditionEditDialog dialog = new ConditionEditDialog (ConditionCategory .PROBLEMLISTITEM , getShell ());
400- if (dialog .open () == Dialog .OK ) {
401- dialog .getCondition ().ifPresent (c -> {
491+ if (editDialog != null && editDialog .isOpen ()) {
492+ editDialog .getShell ().setActive ();
493+ return ;
494+ }
495+ if (openEditDialog (null , ConditionCategory .PROBLEMLISTITEM , getShell ()) == Dialog .OK
496+ && editDialog != null ) {
497+ editDialog .getCondition ().ifPresent (c -> {
402498 c .setPatientId (selectedPatient .getId ());
403499 FindingsServiceComponent .getService ().saveFinding (c );
404500 // touch after creation
0 commit comments