Skip to content

Commit 6f258bb

Browse files
committed
Add ability to copy peak CSV to pasteboard.
On user request, added capability to copy peak CSV to pasteboard, rather than having to download the CSV file. Seems to work, but actually the data is copied to the clipboard as plain text and HTML, instead of CSV, because apparently that (and png) are the only types browsers will let you copy to the pasteboard in JS.
1 parent 3a188b1 commit 6f258bb

File tree

13 files changed

+508
-101
lines changed

13 files changed

+508
-101
lines changed

InterSpec/PeakInfoDisplay.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ class PeakInfoDisplay : public Wt::WContainerWidget
8080
void removeAllPeaks();
8181
void assignNuclidesFromRefLines();
8282

83+
/** Copies the current peak CSV data to `$(this).data({'CsvFullData', 'CsvNoHeaderData', 'CsvCompactData'})`
84+
Called menu copy button is clicked, and copy options menu shown.
85+
*/
86+
void copyCsvPeakDataToClient();
87+
/** Removes the data from the JS; called when copy menu is hidden. */
88+
void removeCsvPeakDatafromClient();
8389

8490
protected:
8591
PeakModel *m_model;

InterSpec/PeakModel.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ class PeakModel: public Wt::WAbstractItemModel
137137

138138
std::shared_ptr<const std::deque< PeakModel::PeakShrdPtr > > peaks() const;
139139
std::vector<PeakDef> peakVec() const;
140+
141+
const std::deque<std::shared_ptr<const PeakDef>> &sortedPeaks() const;
140142

141143
//definePeakXRangeAndChi2(...): Inorder to save cpu (and mostly memorry access
142144
// time) later on, this function will define the lower and upper energy range
@@ -379,10 +381,21 @@ class PeakModel: public Wt::WAbstractItemModel
379381
static std::vector<PeakDef> gadras_peak_csv_to_peaks( std::shared_ptr<const SpecUtils::Measurement> meas,
380382
std::istream &csv );
381383

384+
enum class PeakCsvType
385+
{
386+
Full,
387+
NoHeader,
388+
Compact,
389+
FullHtml,
390+
NoHeaderHtml,
391+
CompactHtml
392+
};
393+
382394
/** Writes the peaks to a CSV file output - e.g., what the user gets when they click on the CSV download on the "Peak Manager" tab.
383395
*/
384396
static void write_peak_csv( std::ostream &outstrm,
385397
std::string specfilename,
398+
const PeakCsvType type,
386399
const std::deque<std::shared_ptr<const PeakDef>> &peaks,
387400
const std::shared_ptr<const SpecUtils::Measurement> &data );
388401

InterSpec_resources/InterSpec.css

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -216,20 +216,6 @@ width:100%;
216216
}
217217

218218

219-
220-
.Wt-btn .with-label {
221-
font-size: 0.9em;
222-
font-weight: bold;
223-
vertical-align: middle;
224-
border: 1px solid #e1e1e1;
225-
color: #FFFFFF;
226-
background-color: #888888;
227-
border-radius: 5px;
228-
height: 23px;
229-
white-space: nowrap;
230-
}
231-
232-
233219
/* A lighter-appearing button, with no background color, and text matching border.
234220
We will also allow applying this style to WSplitButton
235221
*/
@@ -645,37 +631,56 @@ ul.Wt-popupmenu > li > a > span > span:hover
645631
}
646632

647633

648-
.PeakInfoDisplayButtonDiv {
634+
.PeakInfoDisplayBottomDiv {
649635
padding-left: 3px;
650636
display: flex;
651637
align-items: center;
652638
}
653639

654-
.PeakInfoDisplayButtonDiv > .LinkBtn.DownloadBtn.Wt-btn.with-label
655-
{
656-
margin-left: auto;
640+
.PeakInfoDisplayBottomDiv button, .PeakInfoDisplayBottomDiv button:hover {
641+
display: flex;
642+
align-items: center;
643+
}
644+
645+
/* Shrink the button sizes a little bit for narrower display */
646+
@media (max-width: 800px) {
647+
.PeakInfoDisplayBottomDiv button, .PeakInfoDisplayBottomDiv button:hover {
648+
font-size: 0.8em;
649+
font-weight: normal;
650+
}
651+
652+
.PeakInfoDisplayBottomDiv .Wt-btn.with-label img{
653+
padding-right: 3px;
654+
}
657655
}
658656

657+
659658
.PeakInfoHlpBtn {
660659
margin-right: 5px;
661660
position: absolute;
662661
left: 1px;
663662
bottom: 0px;
664663
}
665664

666-
.PeakSearchBtn {
665+
.PeakInfoDisplayButtonsDiv, .PeakInfoDisplayCsvBtns {
667666
margin-left: 25px;
668-
}
669-
670-
.PeakInfoDisplayButtonDiv > button {
671-
margin-right: 5px;
667+
display: flex;
668+
flex-wrap: nowrap;
669+
flex-direction: row;
670+
align-items: center;
671+
justify-content: flex-start;
672+
column-gap: 5px
672673
}
673674

674675
.PeakInfoDisplay .PeakEditHint
675676
{
676-
color: gray;
677-
margin-left: 2em;
678-
font-size:66%
677+
color: gray;
678+
margin-left: 2em;
679+
font-size: 66%;
680+
white-space: nowrap;
681+
overflow: hidden;
682+
flex-grow: 1;
683+
flex-shrink: 1;
679684
}
680685

681686

@@ -853,8 +858,6 @@ button, button:hover,
853858
border:1px solid #e1e1e1;
854859
color: #FFFFFF;
855860
background-color: #888888;
856-
-moz-border-radius: 5px;
857-
-webkit-border-radius: 5px;
858861
border-radius: 5px;
859862
height: 23px;
860863
}
@@ -877,6 +880,10 @@ button[disabled]
877880
filter: grayscale(100%);
878881
}
879882

883+
button, button:hover {
884+
padding-top: 2px;
885+
padding-bottom: 2px;
886+
}
880887

881888
li{ padding-top: 0; }
882889
table { margin-bottom:0; }

InterSpec_resources/InterSpecSafeArea.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ ul.PopupDivMenuPhone > li:last-of-type
111111
margin-bottom: calc( max(env(safe-area-inset-bottom) - 8px, 0px) + max(env(safe-area-inset-bottom) - 8px, 0px) );
112112
}
113113

114-
.LandscapeLeft .PeakInfoDisplayButtonDiv, .LandscapeRight .PeakInfoDisplayButtonDiv {
114+
.LandscapeLeft .PeakInfoDisplayBottomDiv, .LandscapeRight .PeakInfoDisplayBottomDiv {
115115
margin-bottom: 2px;
116116
}
117117

InterSpec_resources/app_text/PeakInfoDisplay.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@
2222
<message id="pid-better-editor-hint">Right click on peaks for better editor</message>
2323
<message id="pid-better-editor-hint-mobile">Tap and hold on peaks for better editor</message>
2424
<message id="pid-tt-csv-export">Export information about the identified peaks to a comma separated format.</message>
25+
<message id="pid-tt-csv-copy">Copy peak CSV information to your pasteboard, in a CSV format.</message>
2526
</messages>
Lines changed: 4 additions & 0 deletions
Loading

src/BatchActivity.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,7 +1756,8 @@ void fit_activities_in_files( const std::string &exemplar_filename,
17561756
warnings.push_back( "Failed to open '" + outcsv + "', for writing.");
17571757
}else
17581758
{
1759-
PeakModel::write_peak_csv( output_csv, leaf_name, fit_peaks, peak_fit_res.spectrum );
1759+
PeakModel::write_peak_csv( output_csv, leaf_name, PeakModel::PeakCsvType::Full,
1760+
fit_peaks, peak_fit_res.spectrum );
17601761
cout << "Have written '" << outcsv << "'" << endl;
17611762
}
17621763
}//if( SpecUtils::is_file( outcsv ) ) / else
@@ -1766,7 +1767,8 @@ void fit_activities_in_files( const std::string &exemplar_filename,
17661767
{
17671768
const string leaf_name = SpecUtils::filename(filename);
17681769
cout << "peaks for '" << leaf_name << "':" << endl;
1769-
PeakModel::write_peak_csv( cout, leaf_name, fit_peaks, peak_fit_res.spectrum );
1770+
PeakModel::write_peak_csv( cout, leaf_name, PeakModel::PeakCsvType::Full,
1771+
fit_peaks, peak_fit_res.spectrum );
17701772
cout << endl;
17711773
}
17721774
}//if( fit_results.m_peak_fit_results )

src/BatchPeak.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ void fit_peaks_in_files( const std::string &exemplar_filename,
345345
warnings.push_back( "Failed to open '" + outcsv + "', for writing.");
346346
}else
347347
{
348-
PeakModel::write_peak_csv( output_csv, leaf_name, fit_peaks, fit_results.spectrum );
348+
PeakModel::write_peak_csv( output_csv, leaf_name,
349+
PeakModel::PeakCsvType::Full, fit_peaks, fit_results.spectrum );
349350
cout << "Have written '" << outcsv << "'" << endl;
350351
}
351352
}//if( SpecUtils::is_file( outcsv ) ) / else
@@ -355,7 +356,8 @@ void fit_peaks_in_files( const std::string &exemplar_filename,
355356
{
356357
const string leaf_name = SpecUtils::filename(filename);
357358
cout << "peaks for '" << leaf_name << "':" << endl;
358-
PeakModel::write_peak_csv( cout, leaf_name, fit_peaks, fit_results.spectrum );
359+
PeakModel::write_peak_csv( cout, leaf_name, PeakModel::PeakCsvType::Full,
360+
fit_peaks, fit_results.spectrum );
359361
cout << endl;
360362
}
361363
}//for( const string filename : files )

src/FluxTool.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ extern void android_download_workaround( Wt::WResource *resource, std::string de
8686

8787

8888
#if( FLUX_USE_COPY_TO_CLIPBOARD )
89+
90+
// See also CopyUrlToClipboard in QrCode.cpp
8991
WT_DECLARE_WT_MEMBER
9092
(CopyFluxDataTextToClipboard, Wt::JavaScriptFunction, "CopyFluxDataTextToClipboard",
9193
function( sender, event, id )
@@ -118,11 +120,11 @@ WT_DECLARE_WT_MEMBER
118120
console.log( 'Will try to copy HTML to copyboard' );
119121

120122
//We failed to copy richtext, lets just copy the HTML as text.
121-
// ToDo: We could probably try the clipboard API to copy formated text
123+
// ToDo: We could probably try the clipboard API to copy formatted text
122124
// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard
123125

124126
if( window.clipboardData && window.clipboardData.setData ) {
125-
return clipboardData.setData("Text", text); // IE
127+
return window.clipboardData.setData("Text", text); // IE
126128
}else if( document.queryCommandSupported && document.queryCommandSupported("copy") ) {
127129
var temparea = document.createElement("textarea");
128130
temparea.textContent = text;

src/InterSpecApp.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,27 @@ void InterSpecApp::miscSignalHandler( const std::string &signal )
15621562
}//if( SpecUtils::istarts_with( signal, "showMultimedia" ) )
15631563

15641564

1565+
if( SpecUtils::istarts_with( signal, "peakCsvCopy-" ) )
1566+
{
1567+
string msg = signal.substr(12);
1568+
WarningWidget::WarningMsgLevel level = WarningWidget::WarningMsgLevel::WarningMsgInfo;
1569+
1570+
if( SpecUtils::istarts_with( msg, "success-" ) )
1571+
{
1572+
msg = msg.substr(8);
1573+
level = WarningWidget::WarningMsgLevel::WarningMsgInfo;
1574+
}else if( SpecUtils::istarts_with( msg, "error-" ) )
1575+
{
1576+
msg = msg.substr(6);
1577+
level = WarningWidget::WarningMsgLevel::WarningMsgHigh;
1578+
}
1579+
1580+
passMessage( msg, level );
1581+
return;
1582+
}//if( SpecUtils::istarts_with( signal, "showMultimedia" ) )
1583+
1584+
1585+
15651586
// shouldnt ever make it here..
15661587
const string errmsg = "InterSpecApp::miscSignalHandler: unhandled signal '" + signal + "'";
15671588
passMessage( errmsg, WarningWidget::WarningMsgHigh );

0 commit comments

Comments
 (0)