Fixed text file permissions
[snark14.git] / tools / Display / displayprojection.cpp
1 /** @file displayprojection.cpp
2     @package snark14Display
3     @author Bruno M. Carvalho and Deniz Sarioz
4     licensed under (open-source) QPL v1.0
5     which accompanies this distribution in the file QPL
6         
7     modification: Joanna Klukowska 1/13/2009
8             added choice of grayScale display for plot
9             in addition to color display
10  */
11
12 #include "variables.hpp"
13 #include "verbosity.hpp"
14 #include "displaywidget.hpp"
15 #include "displayprojection.hpp"
16 #include "lines.hpp"
17 #include "SnarkDisplay.hpp"
18 #include "line_real_t.hpp"
19 #include "plot_t.hpp"
20 #include "displaylines.hpp"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <qwidget.h>
26 #include <qlabel.h>
27 #include <qlineedit.h>
28 #include <qpushbutton.h>
29 #include <qradiobutton.h>
30 #include <qslider.h>
31 #include <qlayout.h>
32 #include <qvariant.h>
33 #include <qtooltip.h>
34 #include <qwhatsthis.h>
35 #include <qimage.h>
36 #include <qpainter.h>
37 #include <qpen.h>
38 #include <qpixmap.h>
39 #include <qpoint.h>
40 #include <qcolor.h>
41 #include <qmessagebox.h>
42 #include <qfiledialog.h>
43 #include <qfont.h>
44 #include <qregexp.h>
45 #include <qstring.h>
46 #include <qscrollview.h>
47
48 #ifndef MIN
49 #define MIN(a, b) (((a < b) ? (a) : (b)))
50 #endif
51
52 extern Snarkimage **imageset;
53 QImage projection;
54 bool convproj;
55
56 /* 
57  *  Constructs a displayprojection which is a child of 'parent', with the 
58  *  name 'name' and widget flags set to 'f' 
59  *
60  *  The dialog will by default be modeless, unless you set 'modal' to
61  *  TRUE to construct a modal dialog.
62  */
63 displayprojection::displayprojection(QWidget* parent, const char* name, bool modal, WFlags fl, int x, int y)
64 : QDialog(parent, name, modal, fl) {
65     unsigned int i, j, minvalrange, maxvalrange;
66     QPixmap disp;
67     QPoint topleft;
68
69     windowname.sprintf("Sinogram: %s", name);
70     windowname = windowname.simplifyWhiteSpace();
71     imagename.sprintf("%s", name);
72     imagename = imagename.simplifyWhiteSpace();
73     setName(windowname);
74     setCaption(windowname);
75
76     topleft.setX(x + 30);
77     topleft.setY(y + 30);
78     this->move(topleft);
79     //    projfil->readProj(projformat);
80     projfil->readProj();
81     maxzoom = 2 * maxSizex / prjnum;
82     if (maxzoom <= 1) maxzoom = 2;
83     proj = projfil->getProj();
84     lowthreshold = minval = projfil->getMin();
85     highthreshold = maxval = projfil->getMax();
86     range = highthreshold - lowthreshold;
87
88     setMinimumSize(QSize(190 + prjnum + 10, 600));
89     resize(190 + prjnum + 10, usrays + 20);
90     //    if((10+(usrays*maxzoom)+10)>600)
91     //      setMaximumSize( QSize( 190+(prjnum*maxzoom)+10, 10+(usrays*maxzoom)+10 ) );
92     //    else 
93     //      setMaximumSize( QSize( 190+(prjnum*maxzoom)+10, 600 ) );
94
95     projection = QImage(prjnum, usrays, 8, 256);
96     for (i = 0; i < 256; i++)
97         projection.setColor(i, qRgb(i, i, i));
98     for (i = 0; i < usrays; i++)
99         for (j = 0; j < prjnum; j++)
100             *(projection.scanLine(i) + j) = (unsigned char) (((proj[j][i] - lowthreshold) / range)*255);
101
102     disp = QPixmap(prjnum, usrays);
103     convproj = disp.convertFromImage(projection, QPixmap::Auto);
104
105     displaywidget = new displayWidget(this, "displaywidget");
106     displaywidget->setGeometry(QRect(190, 10, prjnum, usrays));
107     displaywidget->setBackgroundPixmap(disp);
108     //displaywidget->setScaledContents( TRUE );
109     QObject::connect(displaywidget, SIGNAL(clicked(int, int)), this, SLOT(updateClickedPixel(int, int)));
110
111     maxvalrange = (int) (maxval * 1000);
112     minvalrange = (int) (minval * 1000);
113
114     zoomlabel = new QLabel(this, "zoomlabel");
115     zoomlabel->setGeometry(QRect(60, 5, 45, 20));
116     zoomlabel->setText(tr("Zoom"));
117
118     zoomslider = new QSlider(this, "zoomslider");
119     zoomslider->setGeometry(QRect(5, 30, 170, 20));
120     zoomslider->setOrientation(QSlider::Horizontal);
121     zoomslider->setRange(1, maxzoom);
122     zoomslider->setValue(1);
123     zoomslider->setLineStep(1);
124     zoomslider->setPageStep(2);
125     QObject::connect(zoomslider, SIGNAL(valueChanged(int)), this, SLOT(updateZoom()));
126     zoomval = 1;
127
128     zoom = new QLineEdit(this, "zoom");
129     zoom->setGeometry(QRect(5, 55, 80, 30));
130     zoom->setText("1");
131
132     setzoombutton = new QPushButton(this, "setzoombutton");
133     setzoombutton->setGeometry(QRect(90, 50, 90, 40));
134     setzoombutton->setText(tr("Set Value"));
135     QObject::connect(setzoombutton, SIGNAL(clicked()), this, SLOT(updateZoomslider()));
136
137     globalthresholdbutton = new QRadioButton(this, "globalthresholdbutton");
138     globalthresholdbutton->setGeometry(QRect(10, 100, 160, 25));
139     globalthresholdbutton->setText(tr("Global Thresholds"));
140     globalthresholdbutton->setEnabled(false);
141
142     differencebutton = new QRadioButton(this, "differencebutton");
143     differencebutton->setGeometry(QRect(10, 135, 160, 25));
144     differencebutton->setText(tr("Difference"));
145     differencebutton->setEnabled(false);
146
147     intensitybutton = new QRadioButton(this, "intensitybutton");
148     intensitybutton->setGeometry(QRect(10, 170, 160, 25));
149     intensitybutton->setText(tr("Intensity"));
150     QObject::connect(intensitybutton, SIGNAL(clicked()), this, SLOT(intensity()));
151
152     lowthreshlabel = new QLabel(this, "lowthreshlabel");
153     lowthreshlabel->setGeometry(QRect(30, 205, 110, 20));
154     lowthreshlabel->setText(tr("Low Threshold"));
155
156     lowthreshslider = new QSlider(this, "Slider4_2");
157     lowthreshslider->setGeometry(QRect(5, 230, 170, 20));
158     lowthreshslider->setOrientation(QSlider::Horizontal);
159     lowthreshslider->setRange(minvalrange, maxvalrange);
160     lowthreshslider->setValue(minvalrange);
161     QObject::connect(lowthreshslider, SIGNAL(valueChanged(int)), this, SLOT(updateLowthresh()));
162
163     lowthresh = new QLineEdit(this, "lowthresh");
164     lowthresh->setGeometry(QRect(5, 260, 80, 30));
165     char s[6];
166     sprintf(s, "%7.4f", minval);
167     lowthresh->setText(s);
168
169     setlowthreshbutton = new QPushButton(this, "setlowthreshbutton");
170     setlowthreshbutton->setGeometry(QRect(90, 255, 90, 40));
171     setlowthreshbutton->setText(tr("Set Value"));
172     QObject::connect(setlowthreshbutton, SIGNAL(clicked()), this, SLOT(updateLowthreshslider()));
173
174     highthreshlabel = new QLabel(this, "highthreshlabel");
175     highthreshlabel->setGeometry(QRect(30, 300, 115, 20));
176     highthreshlabel->setText(tr("High Threshold"));
177
178     highthreshslider = new QSlider(this, "Slider4_3");
179     highthreshslider->setGeometry(QRect(5, 325, 170, 20));
180     highthreshslider->setOrientation(QSlider::Horizontal);
181     highthreshslider->setRange(minvalrange, maxvalrange);
182     highthreshslider->setValue(maxvalrange);
183     QObject::connect(highthreshslider, SIGNAL(valueChanged(int)), this, SLOT(updateHighthresh()));
184
185     highthresh = new QLineEdit(this, "highthresh");
186     highthresh->setGeometry(QRect(5, 355, 80, 30));
187     sprintf(s, "%7.4f", maxval);
188     highthresh->setText(s);
189
190     sethighthreshbutton = new QPushButton(this, "sethighthreshbutton");
191     sethighthreshbutton->setGeometry(QRect(90, 350, 90, 40));
192     sethighthreshbutton->setText(tr("Set Value"));
193     QObject::connect(sethighthreshbutton, SIGNAL(clicked()), this, SLOT(updateHighthreshslider()));
194
195     pixelposlabel = new QLabel(this, "pixelposlabel");
196     pixelposlabel->setGeometry(QRect(30, 395, 45, 20));
197     pixelposlabel->setText(tr("Pixel"));
198
199     pixelpos = new QLineEdit(this, "pixelpos");
200     pixelpos->setGeometry(QRect(5, 420, 80, 30));
201
202     pixelvaluelabel = new QLabel(this, "pixelvaluelabel");
203     pixelvaluelabel->setGeometry(QRect(110, 395, 45, 20));
204     pixelvaluelabel->setText(tr("Value"));
205
206     pixelvalue = new QLineEdit(this, "pixelvalue");
207     pixelvalue->setGeometry(QRect(95, 420, 80, 30));
208
209     savebutton = new QPushButton(this, "savebutton");
210     savebutton->setGeometry(QRect(15, 460, 150, 40));
211     savebutton->setText(tr("Save Image"));
212     QObject::connect(savebutton, SIGNAL(clicked()), this, SLOT(saveImage()));
213
214     linesbutton = new QPushButton(this, "linesbutton");
215     linesbutton->setGeometry(QRect(15, 505, 150, 40));
216     linesbutton->setText(tr("Display Lines"));
217     // QObject::connect(linesbutton,SIGNAL(clicked()),this,SLOT(callLines_old()));
218     QObject::connect(linesbutton, SIGNAL(clicked()), this, SLOT(callLines()));
219
220     closebutton = new QPushButton(this, "closebutton");
221     closebutton->setGeometry(QRect(15, 550, 150, 40));
222     closebutton->setText(tr("Close Window"));
223     QObject::connect(closebutton, SIGNAL(clicked()), this, SLOT(accept()));
224
225     scrollv = new QScrollView(this, "qscrollview");
226     scrollv->viewport()->setBackgroundColor(colorGroup().background());
227     scrollv->setFrameShape(QFrame::NoFrame);
228     scrollv->setGeometry(190, 10, maxSizex - 210, maxSizey - 110);
229     scrollv->addChild(displaywidget);
230 }
231
232 void displayprojection::resetscroll() {
233     scrollv->resize(MIN(maxSizex - 210, 20 + (prjnum * zoomval)),
234             MIN(maxSizey - 110, 20 + (usrays * zoomval)));
235     scrollv->resizeContents(prjnum * zoomval + 10, usrays * zoomval + 10);
236 }
237
238 /*  
239  *  Destroys the object and frees any allocated resources
240  */
241 displayprojection::~displayprojection() {
242     // no need to delete child widgets, Qt does it all for us
243 }
244
245 void displayprojection::updateClickedPixel(int cx, int cy) {
246     int ix, iy;
247     char s[12];
248
249     ix = cx / zoomval;
250     iy = cy / zoomval;
251     sprintf(s, "(%d,%d)", ix, iy);
252     pixelpos->setText(s);
253     sprintf(s, "%7.4f", proj[ix][iy]);
254     pixelvalue->setText(s);
255 }
256
257 void displayprojection::updateZoom() {
258     char s[4];
259     int v = zoomslider->value();
260     sprintf(s, "%d", v);
261     zoom->setText(s);
262     zoomval = v;
263     displaywidget->resize(prjnum*zoomval, usrays * zoomval);
264     resize(MIN(maxSizex, 210 + (prjnum * zoomval)),
265             MIN(maxSizey, 20 + (usrays * zoomval)));
266     resetImage();
267     resetscroll();
268 }
269
270 void displayprojection::updateZoomslider() {
271     int v;
272     bool ok = false;
273     v = zoom->text().toInt(&ok, 10);
274     if (ok && v > 0 && v <= maxzoom)
275         zoomslider->setValue(v);
276     else
277         QMessageBox::information(this, "SnarkDisplay", "Error!\n"
278             "Zoom value invalid or out of range.");
279 }
280
281 void displayprojection::intensity() {
282     int l;
283
284     l = windowname.length();
285     if (intensitybutton->isChecked()) {
286         windowname.append("_i");
287         setCaption(windowname);
288     } else {
289         windowname.remove(l - 2, 2);
290         setCaption(windowname);
291     }
292     resetImage();
293 }
294
295 /** obtain threshold value from the low threshold slider position
296     update the text of the slider
297     pre: low threshold slider not greater than high threshold slider
298          Have fixed this
299  */
300 void displayprojection::updateLowthresh() {
301     char s[12];
302     int v = lowthreshslider->value();
303     int hv = highthreshslider->value();
304     if (v > hv) {
305         v = hv;
306         lowthreshslider->setValue(hv);
307     }
308     double fv = ((double) v) / 1000;
309     lowthreshold = fv;
310     sprintf(s, "%7.4f", fv);
311     lowthresh->setText(s);
312     resetImage();
313 }
314
315 double displayprojection::getLowthresh() {
316     return lowthreshold;
317 }
318
319 void displayprojection::updateLowthreshslider() {
320     double fv;
321     int v = 0;
322     bool ok = false;
323     fv = lowthresh->text().toDouble(&ok);
324     if (ok) {
325         if (fv > highthreshold) {
326             QMessageBox::information(this, "SnarkDisplay", "Error!\n"
327                     "Entered low threshold higher than high threshold.");
328             return;
329         }
330         if (fv > 0)
331             v = (int) (fv * 1000 + 0.5);
332         else
333             v = (int) (fv * 1000 - 0.5);
334         if (v < lowthreshslider->minValue())
335             lowthreshslider->setMinValue(v);
336         lowthreshslider->setValue(v);
337         lowthreshold = fv;
338     }
339 }
340
341 /** have fixed this */
342 void displayprojection::updateHighthresh() {
343     char s[12];
344     int v = highthreshslider->value();
345     int Lv = lowthreshslider->value();
346     if (v < Lv) {
347         v = Lv;
348         highthreshslider->setValue(Lv);
349     }
350     double fv = ((double) v) / 1000;
351     highthreshold = fv;
352     sprintf(s, "%7.4f", fv);
353     highthresh->setText(s);
354     resetImage();
355 }
356
357 double displayprojection::getHighthresh() {
358     return highthreshold;
359 }
360
361 void displayprojection::updateHighthreshslider() {
362     double fv;
363     int v = 0;
364     bool ok = false;
365     fv = highthresh->text().toDouble(&ok);
366     if (ok) {
367         if (fv < lowthreshold) {
368             QMessageBox::information(this, "SnarkDisplay", "Error!\n"
369                     "Entered high threshold lower than low threshold.");
370             return;
371         }
372         if (fv > 0)
373             v = (int) (fv * 1000 + 0.5);
374         else
375             v = (int) (fv * 1000 - 0.5);
376         if (v > highthreshslider->maxValue())
377             highthreshslider->setMaxValue(v);
378         highthreshslider->setValue(v);
379         highthreshold = fv;
380     }
381 } // --displayprojection::updateHighthreshslider()
382
383 void displayprojection::resetImage() {
384     /* adjust image to current thresholds and zoom */
385     unsigned int i, j;
386     int m, n, base, point;
387     double t = 0;
388     unsigned char tuc;
389     QPixmap disp;
390
391     //  if((20+(usrays*zoomval)+1)>530)
392     //    resize(190+(prjnum*zoomval)+10, 20+(usrays*zoomval)+10);
393     //  else
394     //    resize(190+(prjnum*zoomval)+10, 530);
395
396     projection = projection.smoothScale(prjnum*zoomval, usrays * zoomval);
397     projection.setNumColors(256);
398     for (i = 0; i < 256; i++)
399         projection.setColor(i, qRgb(i, i, i));
400     range = highthreshold - lowthreshold;
401
402     for (i = 0; i < usrays; i++) {
403         base = i*zoomval;
404         for (j = 0; j < prjnum; j++) {
405             point = j*zoomval;
406             t = ((proj[j][i] - lowthreshold) / range);
407             if (t == 0.0 || t < 0.0) {
408                 for (m = 0; m < zoomval; m++)
409                     for (n = 0; n < zoomval; n++)
410                         *(projection.scanLine(base + m) + point + n) = 0;
411             } else {
412                 if (t > 1.0 || t == 1.0) {
413                     for (m = 0; m < zoomval; m++)
414                         for (n = 0; n < zoomval; n++)
415                             *(projection.scanLine(base + m) + point + n) = 255;
416                 } else {
417                     if (intensitybutton->isChecked()) {
418                         tuc = (unsigned char) (sqrt(t)*255);
419                         for (m = 0; m < zoomval; m++)
420                             for (n = 0; n < zoomval; n++) {
421                                 *(projection.scanLine(base + m) + point + n) = tuc;
422                             }
423                     } else {
424                         tuc = (unsigned char) (t * 255);
425                         for (m = 0; m < zoomval; m++)
426                             for (n = 0; n < zoomval; n++) {
427                                 *(projection.scanLine(base + m) + point + n) = tuc;
428                             }
429                     }
430                 }
431             }
432         }
433     }
434     convproj = disp.convertFromImage(projection, QPixmap::Auto);
435     //  displaywidget->resize(prjnum*zoomval,usrays*zoomval);
436     displaywidget->setBackgroundPixmap(disp);
437     resetscroll();
438 } // --displayprojection::resetImage()
439
440 void displayprojection::resetImageRange() {
441     /* adjust image to current thresholds */
442     unsigned int i, j;
443     double t = 0;
444     QPixmap disp;
445
446     range = highthreshold - lowthreshold;
447
448     for (i = 0; i < usrays; i++)
449         for (j = 0; j < prjnum; j++) {
450             t = ((proj[i][j] - lowthreshold) / range);
451             if (t <= 0)
452                 *(projection.scanLine(i) + j) = 0;
453             else
454                 if (t >= 1)
455                 *(projection.scanLine(i) + j) = 255;
456             else
457                 *(projection.scanLine(i) + j) = (unsigned char) (t * 255);
458         }
459     convproj = disp.convertFromImage(projection, QPixmap::Auto);
460     displaywidget->setBackgroundPixmap(disp);
461 } // --displayprojection::resetImageRange()
462
463 /** Starts a lines window 
464 @param void
465 @author Bruno M. Carvalho
466 @version 1.0 */
467 void displayprojection::callLines() {
468     int i, j;
469     //  unsigned int l[4];
470     int l[4], linetype, c, x, y, sx = 400, sy = 300, xn, yn;
471     int a1, minx, maxx, xrange;
472     int topleftx, toplefty;
473     double miny, maxy, yrange, t1, t2, a2;
474     bool above = false, below = false;
475     char s[11];
476     QString title, xaxis, yaxis, temp, profilename;
477     QPoint topleft;
478     QString projname_qstr(projname);
479     projname_qstr = projname_qstr.simplifyWhiteSpace();
480     lineswindow* nw = new lineswindow(this, projname_qstr, lowthreshold, highthreshold, true, 0);
481     c = nw->exec();
482     if (c == QDialog::Accepted) {
483         linetype = nw->getLinetype();
484         QString rowOrColumn = ((linetype) ? ("Column ") : ("Row "));
485         QString vs = ((linetype) ? ("Rows") : ("Columns"));
486         //    std::cerr << "Plotting " << rowOrColumn << "vs " << vs << std::endl;
487         //    std::cerr << "There are " << prjnum << " columns and " << usrays << " rows" << std::endl;
488         int i_upperbnd = static_cast<int> ((linetype) ? (usrays) : (prjnum));
489         int elljay_upperbnd = static_cast<int> ((linetype) ? (prjnum) : (usrays));
490         //    std::cerr << i_upperbnd << " is upperbound of independent" << std::endl;
491         l[0] = nw->getLine1();
492         l[1] = nw->getLine2();
493         l[2] = nw->getLine3();
494         l[3] = nw->getLine4();
495         title = nw->getTitle();
496         xaxis = nw->getXAxis();
497         yaxis = nw->getYAxis();
498         minx = nw->getMinX();
499         maxx = nw->getMaxX();
500         miny = nw->getMinY();
501         maxy = nw->getMaxY();
502         topleft = this->pos();
503         topleftx = topleft.x();
504         toplefty = topleft.y();
505         for (j = 0; j < 4; j++) { // loop through selection
506             int&row = ((linetype) ? (i) : (l[j]));
507             int&col = ((linetype) ? (l[j]) : (i));
508             if ((l[j] >= 0) && (l[j] < elljay_upperbnd)) { // different plot for each valid j
509                 line_real_set_t myLRS(minx, maxx, miny, maxy); // create a brand new LRS for brand new plot
510                 myLRS.clear(); // and be sure of it!
511                 topleftx += 30;
512                 toplefty += 30;
513                 QPixmap* pixmap = new QPixmap(sd_line_t::WIDTH, sd_line_t::HEIGHT);
514                 pixmap->fill(Qt::white);
515                 QPainter picasso;
516                 picasso.begin(pixmap);
517                 picasso.setBackgroundColor(Qt::white);
518                 QString whichRowOrCol;
519                 whichRowOrCol.setNum(l[j]);
520                 QString plotSuffix = (rowOrColumn + whichRowOrCol);
521                 //QString plotName = projname_qstr.simplifyWhiteSpace();
522                 QString plotName = title.simplifyWhiteSpace(); // bug 227, wei, 6/13/2007
523                 plotName += (" (sinogram):: " + plotSuffix);
524                 //      std::cerr << "plotName : " << plotName << std::endl;
525                 line_real_t ourProjLine(projname_qstr);
526                 for (i = 0; i < i_upperbnd; i++) {
527                     ourProjLine.add(i, proj[col][row]);
528                 }
529                 myLRS.add(ourProjLine);
530                 plot_t myPlot(myLRS);
531                 myPlot.setName(plotName);
532                 myPlot.setXAxisName(xaxis); // for all three of these names
533                 myPlot.setYAxisName(yaxis);
534                 // jk 1/13/2009 adding grayScale option for graphs
535                 if (nw->isGrayScaleChecked()) { // picasso paints on QPixmap pointed to by pmp
536                     myPlot.plotGray(picasso);
537                 } else {
538                     myPlot.plot(picasso);
539                 }
540                 displaylineswindow* dw = new displaylineswindow(this, plotName, pixmap, false, 0, topleftx, toplefty);
541                 dw->show();
542             } // --if on l[j] being ok
543         } // --loop through the j's [0,4)
544     }
545 } // --displayprojection::callLines()
546
547 /** Saves the current image
548 @param void
549 @author Bruno M. Carvalho
550 @version 1.0 */
551 void displayprojection::saveImage() {
552     int i, j;
553
554     QString prefixForSaving = projname;
555     prefixForSaving = prefixForSaving.simplifyWhiteSpace();
556     prefixForSaving.replace(QRegExp("\\="), "_EQ_");
557     prefixForSaving.replace(QRegExp("\\+"), "_PLUS_");
558     prefixForSaving.replace(QRegExp("\\@"), "_AT_");
559     //  prefixForSaving.replace( QRegExp("-"), "_MINUS_"); // maybe
560     // convert everything non-alphanumeric to underscore
561     prefixForSaving.replace(QRegExp("[^a-zA-Z0-9]"), "_");
562     // allow maximum of 2 consecutive underscores
563     prefixForSaving.replace(QRegExp("__+"), "__");
564     // chuck underscore from beginning and end
565     prefixForSaving.replace(QRegExp("^_+"), "");
566     prefixForSaving.replace(QRegExp("_+$"), "");
567
568     QString myExtensionFilter =
569             "png (PNG file);;"
570             "bmp (BMP file);;"
571             "flt (ASCII float file);;"
572             "pbm (PBM file);;"
573             "pgm (PGM file);;"
574             "ppm (PPM file);;"
575             "xbm (XBM file);;"
576             "xpm (XPM file)";
577
578     QString mySaveAs = "Save Image as";
579     QString startInDir = "./";
580     QFileDialog fd(startInDir, myExtensionFilter, this, mySaveAs, true);
581     fd.setMode(QFileDialog::AnyFile);
582     fd.setSelection(prefixForSaving);
583     if (QDialog::Accepted != fd.exec()) {
584         if (std::verbose >= 2) std::cout << "--displaylineswindow::saveImage() in a hurry" << std::endl;
585         return; // happens, say, when cancel is pressed.  just leave, no harm.
586     }
587     QString userFileSavePrefix = fd.selectedFile();
588     QString userFileSaveExtension = fd.selectedFilter();
589     if (userFileSavePrefix.isEmpty()) { // double-check for absurdity
590         if (std::verbose >= 2) std::cout << "--displaylineswindow::saveImage() in a hurry" << std::endl;
591         return; // happens, say, when cancel is pressed.  just leave, no harm.
592     }
593     if (std::verbose >= 2) std::cout << "Got file name as: \"" << userFileSavePrefix << "\"" << std::endl;
594     if (std::verbose >= 2) std::cout << "Got selected ext: \"" << userFileSaveExtension << "\"" << std::endl;
595     /* NB: userFileSavePrefix now contains directory info as well.
596      * if user entered additional forward-slashes without
597      * knowing that it signified directory, that's NOT our problem.
598      * (the user MAY legitimately enter "a/myImage" as file name if there is a directory "a"
599      *   within the currently open directory in the save dialog window)
600      * do part only after last forward-slash (i.e., what user entered)...
601      * don't want to burn ourselves if path in fact had legit non-alphanumericounderscore chars
602      */
603     QString justFilePrefix, justPath;
604     // funnylooking because written to work with Qt BOTH 2.3 and 3.3
605     int posLastSlash = userFileSavePrefix.findRev('/'); // returns pos if found, -1 if not.
606     if (-1 != posLastSlash) {
607         justPath = userFileSavePrefix.left(posLastSlash + 1); // +1 to include last '/'
608         justFilePrefix = userFileSavePrefix;
609         QRegExp fakeRegex("^" + justPath); // it just *looks* fake...
610         justFilePrefix.replace(fakeRegex, ""); // chuck away including last '/' for justFilePrefix
611         if (std::verbose >= 2) std::cout << "just after regex replace, justFilePrefix: \"" << justFilePrefix << "\"" << std::endl;
612     } else { // if for some reason no forwardslash, handle that gracefully too.
613         justFilePrefix = userFileSavePrefix;
614         justPath = "";
615     }
616     if (std::verbose >= 2) std::cout << "justPath is: \"" << justPath << "\"" << std::endl;
617     if (std::verbose >= 2) std::cout << "justFilePrefix is first: \"" << justFilePrefix << "\"" << std::endl;
618     // remove justFilePrefix from ONLY the end of 'userFileSavePrefix'.
619     justFilePrefix.replace(QRegExp("="), "_EQ_");
620     justFilePrefix.replace(QRegExp("+"), "_PLUS_");
621     justFilePrefix.replace(QRegExp("[^a-zA-Z0-9]"), "_"); // no forward slash in regex
622     // allow maximum of 2 consecutive underscores
623     justFilePrefix.replace(QRegExp("__+"), "__");
624     // chuck underscore from beginning and end
625     justFilePrefix.replace(QRegExp("^_+"), "");
626     justFilePrefix.replace(QRegExp("_+$"), "");
627     if (std::verbose >= 2) std::cout << "justFilePrefix is later: \"" << justFilePrefix << "\"" << std::endl;
628     // reset userFileSavePrefix by putting back together justPath and the refined justFilePrefix
629     userFileSavePrefix = justPath + justFilePrefix;
630     userFileSaveExtension.truncate(3);
631     if (std::verbose >= 2) std::cout << "My userFileSavePrefix is \"" << userFileSavePrefix << "\"" << std::endl;
632     if (std::verbose >= 2) std::cout << "My userFileSaveExtension is \"" << userFileSaveExtension << "\"" << std::endl;
633     QString extensionUpper = userFileSaveExtension.upper();
634     QString saveFullNameWithPath = userFileSavePrefix + "." + extensionUpper;
635     if (std::verbose >= 2) std::cout << "extensionUpper is now \"" << extensionUpper << "\"" << std::endl;
636     if (std::verbose >= 2) std::cout << "saveFullNameWithPath is now \"" << saveFullNameWithPath << "\"" << std::endl;
637     if (extensionUpper.compare(QString("FLT"))) { // if NOT "FLT" extension ("FLT" is Bruno's / Linkoping's bunchafloats format)
638         QImageIO recio;
639         recio.setImage(projection);
640         recio.setFormat(extensionUpper);
641         recio.setFileName(saveFullNameWithPath);
642         if (recio.write()) {
643             if (std::verbose >= 1) {
644                 std::cout << "successfully saved: \"" << saveFullNameWithPath << "\"" << std::endl;
645                 return;
646             }
647         } else {
648             std::cerr << "Error opening " << saveFullNameWithPath << std::endl;
649             return;
650         }
651     } else {
652         FILE* fp;
653         if ((fp = fopen(saveFullNameWithPath.latin1(), "w")) == NULL) {
654             std::cerr << "UNsuccessfully tried to save as: \"" << saveFullNameWithPath << "\"" << std::endl;
655         } else {
656             for (i = 0; i < (static_cast<int> (usrays)); i++) {
657                 for (j = 0; j < (static_cast<int> (prjnum)); j++) {
658                     fprintf(fp, "%f ", proj[i][j]);
659                     if (!((j + 1) % 5))
660                         fprintf(fp, "\n");
661                 }
662                 fprintf(fp, "\n");
663             }
664             fclose(fp);
665             if (std::verbose >= 1) {
666                 std::cout << "(we think) successfully saved: \"" << saveFullNameWithPath << "\"" << std::endl;
667             }
668         }
669     }
670 }