Skip to content

Commit c9d1d78

Browse files
andyclaude
andcommitted
Windows alpha.9 reports: redraws, focus, MOs panel, Launcher line, layouts, switch to local
- Organizer: the ecce_url_* notices of one Save or Launch each rebuilt the tree and the summary. They are queued, de-duplicated and applied after 150 ms under one Freeze (queueUpdate/applyQueuedUpdates). Test command "counts" reports notices, updates and batches. - Calculation Editor: setContext fills the fields under a wxWindowUpdateLocker, so the several relayouts show once. - Builder: the MOs panel is no longer the default panel (list + detail) nor shown by default in the classic layout; Energies comes first. - New calculation: the name prompt ran with the Organizer disabled by startDisabler(); on MSW the closing dialog then activated the window behind it (Explorer). The disabler is paused around the prompt. Test command "newprompt". - Launcher: the "Remote Machine Access" heading line was grown to the heading's height with wxDOUBLE_BORDER, which MSW draws as a grey bar. - Organizer summary: the panel was not laid out again after refresh(), so a longer value (formula) ran into the next column. The stored formula was Fragment::formula() (H4C) while the Calculation Editor showed generateEmpiricalFormula() (CH4); both are now the Hill formula (alphabetical when there is no carbon). Icons without a run state are drawn at the right of their slot, next to the label. - Calculation Editor: an 8 DIP gap between the settings summary's labels and values; the message pane's drop button takes its natural size instead of the bitmap's 50x60, which MSW clipped. - Windows: the Organizer has the login window, so "Use this computer instead" ends it and it starts ecce.cmd again with ECCE_SWITCHED_LOCAL: local, no first-start question, no "From the next start" message. ecce_cmd_test.py covers it against a stub server that wants a login. - tests/windows: a9_look.py (+ a9task.ps1, framelog.ps1) checks these through the test hooks and logs the window states seen on screen. The apps stay DPI-unaware on Windows (no dpiAware in the manifest), so Windows scales them as a bitmap at 150/200 %: the layout at 100 % is the layout at every scale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTQ7mniRtDdhU7VF9tEVBD
1 parent 0f9b768 commit c9d1d78

17 files changed

Lines changed: 557 additions & 48 deletions

File tree

‎packaging/windows/ecce.cmd‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@ if not defined ECCE_TMPDIR set "ECCE_TMPDIR=%TEMP:\=/%"
1818
set ECCE_SESSION_LIVENESS=lease
1919
rem Where the user keeps their work is asked once, at the first start (#240):
2020
rem the exit status 3 is "Quit" in that window; any other failure goes on.
21-
if not defined ECCE_NO_FIRST_START if exist "%ECCE_ROOT%\python\python3w.exe" (
21+
rem ECCE_SWITCHED_LOCAL: the Organizer starts this again after the login
22+
rem window's "Use this computer instead"; that start is local and not asked.
23+
if not defined ECCE_NO_FIRST_START if not defined ECCE_SWITCHED_LOCAL if exist "%ECCE_ROOT%\python\python3w.exe" (
2224
start /wait "" "%ECCE_ROOT%\python\python3w.exe" "%ECCE_ROOT%\bin\ecce-first-start"
2325
if errorlevel 3 if not errorlevel 4 exit /b 0
2426
)
2527
rem A server given by the installation, or chosen in that window, makes this a
2628
rem -remote session (what ecce-session-lib.sh does on the other systems).
2729
set "ECCE_SERVER_SESSION="
30+
if defined ECCE_SWITCHED_LOCAL (
31+
set "ECCE_SWITCHED_LOCAL="
32+
goto local
33+
)
2834
if defined ECCE_LOCAL_DATA goto local
2935
if defined ECCE_REMOTE_SERVER set "ECCE_SERVER_SESSION=1"
3036
if exist "%ECCE_ROOT%\siteconfig\RemoteServer\DataServers" set "ECCE_SERVER_SESSION=1"

‎src/apps/builder/Builder.C‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5918,7 +5918,7 @@ void Builder::addPropertyPanel(PropertyPanel *panel, const string& name)
59185918
// MO Diagram opened by itself on every calculation that had
59195919
// orbital energies.
59205920
static const set<string> defaultShown = {
5921-
"Calculation Summary", "Energies", "MOs"
5921+
"Calculation Summary", "Energies"
59225922
};
59235923
bool show = defaultShown.find(name) != defaultShown.end();
59245924
if (p_panelMode == PANELS_DETAIL) {

‎src/apps/builder/BuilderPanels.C‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ const char *MODE_NAMES[] = { "classic", "stacked", "accordion", "detail" };
5454

5555
// What opens first when a layout shows one panel and the user has not
5656
// chosen: the data people came for, not the summary the header repeats.
57+
// Not the MOs: that panel draws orbitals and is opened when wanted.
5758
const char *DEFAULT_PANEL_ORDER[] = {
58-
"MOs", "Vibrational Frequencies", "Energies", "Calculation Summary"
59+
"Energies", "Vibrational Frequencies", "Calculation Summary"
5960
};
6061

6162

‎src/apps/calced/CalcEd.C‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <wx/combo.h>
1616

17+
#include <wx/wupdlock.h>
1718
#include "util/Ecce.H"
1819
#include "util/BrowserHelp.H"
1920
#include "wxgui/WxHelpViewer.H"
@@ -530,6 +531,9 @@ void CalcEd::setContext(const string& url, const string& codeName)
530531
}
531532

532533
if (doit) {
534+
// Filling the fields relays the window out several times; show only
535+
// the result.
536+
wxWindowUpdateLocker freeze(this);
533537
freeContext();
534538
bool msgFlag = p_iCalc != (ICalculation*)0;
535539
p_iCalc = givenCalc;
@@ -2496,6 +2500,9 @@ void CalcEd::populateSummaryField(const string& summaryType)
24962500
sizer->Clear(true);
24972501
sizer->Layout();
24982502
sizer->SetSizeHints(this);
2503+
// A gap between label and value: a long label ("SCF Conv. Algorithm")
2504+
// otherwise runs into its value.
2505+
sizer->SetHGap(FromDIP(8));
24992506

25002507
if (p_code && p_GUIValues) {
25012508
SummaryIterator *it = p_code->getSummaryIterator(summaryType);

‎src/apps/launcher/WxLauncherGUI.C‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,10 @@ void WxLauncherGUI::CreateControls()
560560
ewxStaticText* itemStaticText84 = new ewxStaticText( itemPanel3, wxID_STATIC, _("Remote Machine Access"), wxDefaultPosition, wxDefaultSize, 0 );
561561
itemBoxSizer82->Add(itemStaticText84, 0, wxALIGN_TOP|wxTOP, 3);
562562

563-
ewxStaticLine* itemStaticLine85 = new ewxStaticLine( itemPanel3, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL|wxDOUBLE_BORDER );
564-
itemBoxSizer82->Add(itemStaticLine85, 1, wxGROW|wxLEFT|wxRIGHT|wxTOP, 3);
563+
// Centred, not grown: stretched to the heading's height, MSW draws the
564+
// line as a grey bar.
565+
ewxStaticLine* itemStaticLine85 = new ewxStaticLine( itemPanel3, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
566+
itemBoxSizer82->Add(itemStaticLine85, 1, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 3);
565567

566568
ewxPanel* itemPanel86 = new ewxPanel( itemPanel3, ID_PANEL_WXLAUNCHER_6, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL );
567569
itemBoxSizer4->Add(itemPanel86, 0, wxGROW, 3);

‎src/apps/organizer/CalcMgr.C‎

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <fstream>
2626
#include <memory>
27+
#include <algorithm>
2728
#include <sstream>
2829
#include <iostream>
2930
using std::cerr;
@@ -88,6 +89,7 @@ static std::string s_registerMessage;
8889
#include "comm/RunMgmt.H"
8990

9091
#include "wxgui/ewxProgressDialog.H"
92+
#include <wx/wupdlock.h>
9193
#include "wxgui/EcceTool.H"
9294
#include "wxgui/ewxWindowUtils.H"
9395
#include "wxgui/ewxBitmap.H"
@@ -409,6 +411,11 @@ bool CalcMgr::Create( wxWindow* parent, wxWindowID id, const wxString& caption,
409411
* new <parent url> <type>
410412
* File > New with resource type <type> (gromacs_md_study,
411413
* gromacs_md_energy) in <parent>, as the New menu makes it
414+
* counts broker notices, the updates made for them and their
415+
* batches since the last counts
416+
* newprompt <parent url> <code>
417+
* File > New <code> Calculation with the name prompt,
418+
* answered OK after 1.5 s
412419
* contextmenu <url> right-click <url> in the tree; the New submenu's items
413420
*/
414421
void CalcMgr::runTestCommand(const string& line)
@@ -646,6 +653,44 @@ void CalcMgr::runTestCommand(const string& line)
646653
} else if (command == "tail-close") {
647654
if (p_testTail) p_testTail->Close();
648655
outcome = "ok";
656+
} else if (command == "counts") {
657+
// Broker notices received, tree/summary updates made for them, and
658+
// the batches they came in, since the last "counts".
659+
outcome = "notices=" + std::to_string(p_testNotices) +
660+
" applied=" + std::to_string(p_testApplied) +
661+
" batches=" + std::to_string(p_testBatches);
662+
p_testNotices = p_testApplied = p_testBatches = 0;
663+
} else if (command == "newprompt") {
664+
// "newprompt PARENT-URL CODE": File > New <code> Calculation with the
665+
// name prompt, as the menu does it (disabler included); the prompt is
666+
// answered OK after 1.5 s.
667+
std::istringstream args(calcName);
668+
string parentUrl, code;
669+
args >> parentUrl >> code;
670+
ResourceType *rt = ResourceDescriptor::getResourceDescriptor()
671+
.getResourceType("virtual_document", "ecceCalculation", code);
672+
WxResourceTreeItemData *parentNode = findNode(EcceURL(parentUrl),
673+
true, true);
674+
if (!rt || !parentNode) {
675+
outcome = !rt ? "no resource type " + code : "no parent node";
676+
} else {
677+
wxTimer answer;
678+
answer.Bind(wxEVT_TIMER, [](wxTimerEvent&) {
679+
for (wxWindowList::compatibility_iterator n =
680+
wxTopLevelWindows.GetFirst(); n; n = n->GetNext()) {
681+
wxDialog *d = dynamic_cast<wxDialog*>(n->GetData());
682+
if (d && d->IsModal()) d->EndModal(wxID_OK);
683+
}
684+
});
685+
answer.StartOnce(1500);
686+
bool ask = GetMenuBar()->IsChecked(wxID_ASKFORNAME);
687+
GetMenuBar()->Check(wxID_ASKFORNAME, true);
688+
startDisabler();
689+
createResource(rt, parentNode, false);
690+
stopDisabler();
691+
GetMenuBar()->Check(wxID_ASKFORNAME, ask);
692+
outcome = "ok";
693+
}
649694
} else if (command == "start") {
650695
// "start TOOL [URL]": what a tool button does (startApp), e.g.
651696
// "start CalculationEditor <calc url>".
@@ -5213,7 +5258,18 @@ void CalcMgr::createResource(ResourceType * resType,
52135258
infoStr = invalidMsg + infoStr;
52145259
ewxTextEntryDialog dialog(this, infoStr, "New " + name + " Name",
52155260
name, wxOK | wxCANCEL);
5216-
if (dialog.ShowModal() == wxID_OK) {
5261+
// The dialog disables the windows itself. Left disabled by
5262+
// startDisabler(), the Organizer cannot take the activation back when
5263+
// the dialog closes, and on MSW the window behind it (another
5264+
// program) comes to the front.
5265+
const bool paused = p_disabler != 0;
5266+
if (paused) {
5267+
delete p_disabler;
5268+
p_disabler = 0;
5269+
}
5270+
const int answer = dialog.ShowModal();
5271+
if (paused) p_disabler = new wxWindowDisabler;
5272+
if (answer == wxID_OK) {
52175273
name = dialog.GetValue();
52185274
if (resType->isValidResourceName(name))
52195275
break;
@@ -5441,6 +5497,44 @@ void CalcMgr::updateUrl(const EcceURL & url)
54415497
}
54425498

54435499

5500+
void CalcMgr::queueUpdate(UpdateKind kind, const EcceURL & url)
5501+
{
5502+
p_testNotices++;
5503+
std::pair<UpdateKind, string> item(kind, url.toString());
5504+
// A repeated change notice adds nothing; additions and removals keep
5505+
// their order, only an immediate repeat is dropped.
5506+
bool dup = kind == UPDATE_CHANGED
5507+
? std::find(p_queuedUpdates.begin(), p_queuedUpdates.end(), item) !=
5508+
p_queuedUpdates.end()
5509+
: !p_queuedUpdates.empty() && p_queuedUpdates.back() == item;
5510+
if (!dup) p_queuedUpdates.push_back(item);
5511+
if (!p_updateTimer) {
5512+
p_updateTimer = new wxTimer(this);
5513+
Bind(wxEVT_TIMER, [this](wxTimerEvent&) { applyQueuedUpdates(); },
5514+
p_updateTimer->GetId());
5515+
}
5516+
// The notices of one Save arrive within a few ms of each other.
5517+
if (!p_updateTimer->IsRunning()) p_updateTimer->StartOnce(150);
5518+
}
5519+
5520+
5521+
void CalcMgr::applyQueuedUpdates()
5522+
{
5523+
std::vector<std::pair<UpdateKind, string> > todo;
5524+
todo.swap(p_queuedUpdates);
5525+
if (todo.empty()) return;
5526+
p_testBatches++;
5527+
wxWindowUpdateLocker freeze(this);
5528+
for (size_t i = 0; i < todo.size(); i++) {
5529+
p_testApplied++;
5530+
EcceURL url(todo[i].second);
5531+
if (todo[i].first == UPDATE_ADDED) updateAddNode(url);
5532+
else if (todo[i].first == UPDATE_REMOVED) updateRemoveNode(url);
5533+
else updateUrl(url);
5534+
}
5535+
}
5536+
5537+
54445538
/**
54455539
* Refresh the context panel and tree ctrl when the resource associated with
54465540
* the URL got added

‎src/apps/organizer/CalcMgr.H‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#define _CALCMGR_H_
99

1010
#include <wx/weakref.h>
11+
#include <wx/timer.h>
12+
#include <utility>
13+
#include <vector>
1114
#include "wxgui/WxTailWindow.H"
1215
#include <wx/frame.h>
1316
#include <wx/toolbar.h>
@@ -262,6 +265,11 @@ public:
262265
void updateUrl(const EcceURL & url);
263266
void updateAddNode(const EcceURL & url);
264267
void updateRemoveNode(const EcceURL & url);
268+
// The broker's ecce_url_* notices. One Save or Launch sends several in a
269+
// row, and each rebuilt the tree and the summary (a visible flicker on
270+
// MSW); they are collected and applied together under one Freeze.
271+
enum UpdateKind { UPDATE_CHANGED, UPDATE_ADDED, UPDATE_REMOVED };
272+
void queueUpdate(UpdateKind kind, const EcceURL & url);
265273
void setMessage(const string & message,
266274
WxFeedback::Severity severity=WxFeedback::WARNING,
267275
bool clear = false);
@@ -498,6 +506,12 @@ protected:
498506
// Set by the "new" test command: createResource reports its error
499507
// here instead of in a modal dialog.
500508
string *p_testCreateError = 0;
509+
510+
void applyQueuedUpdates();
511+
std::vector<std::pair<UpdateKind, string> > p_queuedUpdates;
512+
wxTimer *p_updateTimer = 0;
513+
// For the "counts" test command.
514+
int p_testNotices = 0, p_testApplied = 0, p_testBatches = 0;
501515
};
502516

503517

‎src/apps/organizer/CalcMgrApp.C‎

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,43 @@
3434
#include "wxgui/WxJMSSubscriber.H"
3535
#include "wxgui/WxState.H"
3636

37+
#include "wxgui/WxDavAuth.H"
38+
3739
#include "CalcMgrApp.H"
3840
#include "CalcMgr.H"
3941

42+
#ifdef __WXMSW__
43+
#include <windows.h>
44+
#include <stdlib.h>
45+
#include "util/Ecce.H"
46+
47+
// The login window's "Use this computer instead": this start was set up for
48+
// the server, so ecce.cmd runs again, once, for a local session and without
49+
// the first-start question (ECCE_SWITCHED_LOCAL), as `ecce` does on Linux.
50+
static void restartInLocalMode()
51+
{
52+
const char *root = getenv("ECCE_ROOT");
53+
string script = (root && *root ? string(root) : string(Ecce::ecceHome())) +
54+
"/ecce.cmd";
55+
for (const char *v : {"ECCE_REMOTE_SERVER", "ECCE_REMOTE_DIR",
56+
"ECCE_SERVER_SESSION", "ECCE_SESSION_ID"})
57+
_putenv_s(v, "");
58+
_putenv_s("ECCE_SWITCHED_LOCAL", "1");
59+
const char *comspec = getenv("COMSPEC");
60+
string line = "\"" + string(comspec && *comspec ? comspec : "cmd.exe") +
61+
"\" /d /c \"\"" + script + "\"\"";
62+
STARTUPINFOA si;
63+
PROCESS_INFORMATION pi;
64+
memset(&si, 0, sizeof(si));
65+
si.cb = sizeof(si);
66+
if (CreateProcessA(NULL, &line[0], 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0,
67+
&si, &pi)) {
68+
CloseHandle(pi.hThread);
69+
CloseHandle(pi.hProcess);
70+
}
71+
}
72+
#endif
73+
4074

4175
IMPLEMENT_APP(CalcMgrApp)
4276

@@ -68,6 +102,10 @@ string CalcMgrApp::getName() const
68102
bool CalcMgrApp::OnInit()
69103
{
70104
ewxApp::OnInit();
105+
#ifdef __WXMSW__
106+
// No gateway here: the Organizer has the session's login window.
107+
WxDavAuth::setRestartsOnSwitch(true);
108+
#endif
71109

72110
if (argc>2 && strcmp(argv[1].ToStdString().c_str(),"-pipe")==0) {
73111
AuthCache::getCache().pipeIn(argv[2].ToStdString());
@@ -160,7 +198,9 @@ void CalcMgrApp::openUrlWhenReady(const string& url, int tries)
160198

161199
int CalcMgrApp::OnExit()
162200
{
163-
// cerr << "Enter CalcMgrApp::OnExit()\n";
201+
#ifdef __WXMSW__
202+
if (WxDavAuth::switchedToLocal()) restartInLocalMode();
203+
#endif
164204
return 0;
165205
}
166206

@@ -239,21 +279,21 @@ void CalcMgrApp::msgRenameMCB(JMSMessage& msg)
239279
void CalcMgrApp::msgURLCreatedMCB(JMSMessage& msg)
240280
{
241281
EcceURL url(msg.getProperty("url"));
242-
p_calcMgr->updateAddNode(url);
282+
p_calcMgr->queueUpdate(CalcMgr::UPDATE_ADDED, url);
243283
}
244284

245285

246286
void CalcMgrApp::msgURLRemovedMCB(JMSMessage& msg)
247287
{
248288
EcceURL url(msg.getProperty("url"));
249-
p_calcMgr->updateRemoveNode(url);
289+
p_calcMgr->queueUpdate(CalcMgr::UPDATE_REMOVED, url);
250290
}
251291

252292

253293
void CalcMgrApp::msgChangedMCB(JMSMessage& msg)
254294
{
255295
EcceURL url(msg.getProperty("url"));
256-
p_calcMgr->updateUrl(url);
296+
p_calcMgr->queueUpdate(CalcMgr::UPDATE_CHANGED, url);
257297
}
258298

259299

‎src/apps/organizer/CalculationContextPanel.C‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ void CalculationContextPanel::refresh()
130130
p_properties->findValue(VDoc::getEcceNamespace() + ":annotation", annotation);
131131
annotationCtrl->SetValue(annotation);
132132

133+
// New values (a longer formula after a Save) need their columns widened.
134+
Layout();
135+
FitInside();
136+
133137
// get the thumbnail and replace the builder button icon
134138
// variables to get the jpeg thumbnail resource
135139
SFile *thumbnail = TempStorage::getTempFile();

‎src/dsm/edsiimpl/ChemistryTask.C‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ bool ChemistryTask::fragment(Fragment* frag)
122122

123123
// empiricalFormula property
124124
results[0].name = VDoc::getEcceNamespace() + ":empiricalFormula";
125-
frag->generateEmpiricalFormula();
126-
results[0].value = frag->formula();
125+
// The Hill formula, as the Calculation Editor shows it.
126+
results[0].value = frag->generateEmpiricalFormula();
127127

128128
// charge property
129129
results[1].name = VDoc::getEcceNamespace() + ":charge";

0 commit comments

Comments
 (0)