From 71aac59573fea2a81ec85f3b78e841b604417db0 Mon Sep 17 00:00:00 2001 From: ffsup2 Date: Wed, 19 Jan 2011 22:56:56 +0100 Subject: [PATCH] Merge from branch 3.0 --- rtdata/languages/Francais | 4 +-- rtengine/improcfun.cc | 11 +++--- rtengine/improcfun.h | 3 +- rtgui/crophandler.cc | 7 ++++ rtgui/cropwindow.cc | 76 +++++++++++++++++++++++---------------- rtgui/cropwindow.h | 16 +++++---- rtgui/editorpanel.cc | 29 +++++++-------- rtgui/guiutils.cc | 1 - rtgui/imagearea.cc | 14 ++++++++ 9 files changed, 96 insertions(+), 65 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 4b3f7992d..dd30ace49 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1,4 +1,3 @@ - #00 Français #01 1.3.2008: Initial translation by Hombre ADJUSTER_RESET_TO_DEFAULT;Réglages par défaut @@ -148,8 +147,6 @@ HISTORY_MSG_104;Égaliseur TSV HISTORY_MSG_105;Corr. aberr. chromatique HISTORY_MSG_106;Corr. a.c. - Rayon HISTORY_MSG_107;Corr. a.c. - Seuil -HISTORY_MSG_108;Redim. - boîte englobante -HISTORY_MSG_109;Redim. s'applique à HISTORY_MSG_10;Compression des ombres HISTORY_MSG_11;Courbe tonale HISTORY_MSG_12;Exposition auto @@ -461,6 +458,7 @@ PREFERENCES_DMETHOD;Méthode PREFERENCES_EDITORCMDLINE;Autre ligne de commande PREFERENCES_EDITORLAYOUT;Disposition de l'éditeur PREFERENCES_EXPOS;Facteur de correction\nd'exposition (linéaire) +PREFERENCES_PRESER;Correct. d'expo. préservant\nles hautes lumières (EV) PREFERENCES_EXTERNALEDITOR;Éditeur externe PREFERENCES_FALSECOLOR;Itérations pour la suppression\ndes fausses couleurs PREFERENCES_FBROWSEROPTS;Options du navigateur de fichiers diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 872ea6025..a4833e66f 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -134,9 +134,8 @@ void ImProcFunctions::setScale (double iscale) { scale = iscale; } -void ImProcFunctions::firstAnalysis_ (Image16* original, Glib::ustring wprofile, unsigned int* histogram, int* chroma_radius, int row_from, int row_to) { +void ImProcFunctions::firstAnalysis_ (Image16* original, const TMatrix &wprof, unsigned int* histogram, int* chroma_radius, int row_from, int row_to) { - TMatrix wprof = iccStore->workingSpaceMatrix (wprofile); int toxyz[3][3]; toxyz[0][0] = round(32768.0 * wprof[0][0] / 0.96422); toxyz[1][0] = round(32768.0 * wprof[1][0] / 0.96422); @@ -192,7 +191,7 @@ void ImProcFunctions::firstAnalysis_ (Image16* original, Glib::ustring wprofile, void ImProcFunctions::firstAnalysis (Image16* original, const ProcParams* params, unsigned int* histogram, double gamma) { // set up monitor transform - Glib::ustring wprofile = params->icm.working; + TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); if (monitorTransform) cmsDeleteTransform (monitorTransform); monitorTransform = NULL; @@ -232,12 +231,12 @@ void ImProcFunctions::firstAnalysis (Image16* original, const ProcParams* params int blk = H/nthreads; if (tidheight); + firstAnalysis_ (original, wprof, hist[0], &cr[0], 0, original->height); #endif chroma_radius = cr[0]; for (int i=0; i #include #include +#include namespace rtengine { @@ -54,7 +55,7 @@ class ImProcFunctions { void transformNonSep (Image16* original, Image16* transformed, int cx, int cy, int sx, int sy, int oW, int oH); void transformSep (Image16* original, Image16* transformed, int cx, int cy, int sx, int sy, int oW, int oH); void sharpenHaloCtrl (LabImage* lab, unsigned short** blurmap, unsigned short** base, int W, int H); - void firstAnalysis_ (Image16* original, Glib::ustring wprofile, unsigned int* histogram, int* chroma_radius, int row_from, int row_to); + void firstAnalysis_ (Image16* original, const TMatrix &wprof, unsigned int* histogram, int* chroma_radius, int row_from, int row_to); void dcdamping (float** aI, unsigned short** aO, float damping, int W, int H); bool needsCA (); diff --git a/rtgui/crophandler.cc b/rtgui/crophandler.cc index 9843a1aa1..5f1489d0f 100644 --- a/rtgui/crophandler.cc +++ b/rtgui/crophandler.cc @@ -153,6 +153,9 @@ void CropHandler::getPosition (int& x, int& y) { } +/* + * Create the piece of preview image that will be integrally copied in the preview area + */ int createpixbufs (void* data) { gdk_threads_enter (); @@ -191,9 +194,13 @@ int createpixbufs (void* data) { if (imh>ch->wh) imh = ch->wh; + // Create a temporary pixbuf to copy the piece of the full size image Glib::RefPtr tmpPixbuf = Gdk::Pixbuf::create_from_data (ch->cropimg, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, ch->cropimg_height, 3*ch->cropimg_width); + // Create the real preview image ch->cropPixbuf = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); + // Rescale the piece of the full size image and put it in the preview image tmpPixbuf->scale (ch->cropPixbuf, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST); + // Delete the temporary pixbuf tmpPixbuf.clear (); } delete [] ch->cropimg; diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 72f76658f..8359d7ac8 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -53,7 +53,7 @@ CropWindow::CropWindow (ImageArea* parent, rtengine::StagedImageProcessor* ipc_) : onResizeArea(false), deleted(false), fitZoomEnabled(true), fitZoom(false), backColor(options.bgcolor), decorated(true), titleHeight(30), sideBorderWidth(3), lowerBorderWidth(3), upperBorderWidth(1), sepWidth(2), - imgX(0), imgY(0), imgW(1), imgH(1), xpos(30), ypos(30), iarea(parent), + xpos(30), ypos(30), imgX(0), imgY(0), imgW(1), imgH(1), iarea(parent), cropZoom(0), cropgl(NULL), pmlistener(NULL), observedCropWin(NULL) { Glib::RefPtr context = parent->get_pango_context () ; @@ -307,6 +307,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { else if (iarea->getToolMode () == TMCropSelect && cropgl) { state = SCropSelecting; translateCoord (x, y, press_x, press_y); + cropHandler.cropParams.enabled = true; cropHandler.cropParams.x = press_x; cropHandler.cropParams.y = press_y; cropHandler.cropParams.w = cropHandler.cropParams.h = 1; @@ -588,16 +589,16 @@ void CropWindow::updateCursor (int x, int y) { void CropWindow::expose (Cairo::RefPtr cr) { - MyTime t1, t2, t3, t4; + //MyTime t1, t2, t3, t4; - t1.set (); + //t1.set (); if (decorated) drawDecoration (cr); int x = xpos, y = ypos, h = height, w = width; - // draw border + // draw the background if (backColor==0) { Gdk::Color cback = iarea->get_style()->get_bg(Gtk::STATE_NORMAL); cr->set_source_rgb (cback.get_red_p(), cback.get_green_p(), cback.get_blue_p()); @@ -606,8 +607,9 @@ void CropWindow::expose (Cairo::RefPtr cr) { cr->set_source_rgb (0,0,0); else if (backColor==2) cr->set_source_rgb (1,1,1); - - cr->rectangle (x+imgAreaX+0.5, y+imgAreaY+0.5, imgAreaW, imgAreaH); + + cr->set_line_width (0.); + cr->rectangle (x+imgAreaX, y+imgAreaY, imgAreaW, imgAreaH); cr->stroke_preserve (); cr->fill (); @@ -624,18 +626,20 @@ void CropWindow::expose (Cairo::RefPtr cr) { Glib::RefPtr rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom); if (rough) { iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), rough, 0, 0, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, -1, -1, Gdk::RGB_DITHER_NORMAL, 0, 0); -// if (cropHandler.cropParams.enabled) +// if (cropHandler.cropParams.enabled) // drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams); } + if (observedCropWin) + drawObservedFrame (cr); } - else { + else { if (cropHandler.cropPixbuf) { imgW = cropHandler.cropPixbuf->get_width (); imgH = cropHandler.cropPixbuf->get_height (); imgX = imgAreaX + (imgAreaW-imgW)/2; imgY = imgAreaY + (imgAreaH-imgH)/2; // PERFORMANCE BOTTLENECK STARTS HERE - t3.set (); + //t3.set (); bool showcs = iarea->indClippedPanel->showClippedShadows(); bool showch = iarea->indClippedPanel->showClippedHighlights(); @@ -662,36 +666,36 @@ void CropWindow::expose (Cairo::RefPtr cr) { else if (showcs && (curr[0]<=options.shadowThreshold || curr[1]<=options.shadowThreshold || curr[2]<=options.shadowThreshold)) curr[0] = curr[1] = curr[2] = 255; } - iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); + iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); } else - iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); - t4.set (); + iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); + //t4.set (); // END OF BOTTLENECK if (cropHandler.cropParams.enabled) { int cropX, cropY; cropHandler.getPosition (cropX, cropY); drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams); } + if (observedCropWin) + drawObservedFrame (cr); } else { + // cropHandler.cropPixbuf is null int cropX, cropY; cropHandler.getPosition (cropX, cropY); Glib::RefPtr rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom); if (rough) { iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), rough, 0, 0, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, -1, -1, Gdk::RGB_DITHER_NORMAL, 0, 0); if (cropHandler.cropParams.enabled) { - int cropX, cropY; - cropHandler.getPosition (cropX, cropY); - drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams); + drawCrop (cr, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, rough->get_width(), rough->get_height(), cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams); } + if (observedCropWin) + drawObservedFrame (cr, rough->get_width(), rough->get_height()); } } } - if (observedCropWin) - drawObservedFrame (cr); - // if cursor stays above resize area, draw the icon if (decorated && (state==SCropWinResize || onResizeArea)) { int rw = resizeSurface->get_width (); @@ -713,7 +717,7 @@ void CropWindow::expose (Cairo::RefPtr cr) { if (state==SNormal && iarea->getToolMode () == TMSpotWB) drawSpotWBRectangle (cr); - t2.set (); + //t2.set (); cropHandler.cimg.unlock (); // printf ("etime --> %d, %d\n", t2.etime (t1), t4.etime (t3)); } @@ -792,11 +796,15 @@ void CropWindow::redrawNeeded (LWButton* button) { void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery) { - cropZoom = zoom; - if (cropZoom<0) - cropZoom = 0; - else if (cropZoom>MAXZOOMSTEPS) - cropZoom = MAXZOOMSTEPS; + if (zoom<0) + zoom = 0; + else if (zoom>MAXZOOMSTEPS) + zoom = MAXZOOMSTEPS; + if (cropZoom == zoom) + // We are already at the start/end of the zoom range, so we do nothing + return; + else + cropZoom = zoom; cropLabel = zoomSteps[cropZoom].label; cropHandler.setZoom (zoomSteps[cropZoom].czoom, centerx, centery); @@ -968,24 +976,30 @@ void CropWindow::drawSpotWBRectangle (Cairo::RefPtr cr) { cr->reset_clip (); } -void CropWindow::getObservedFrameArea (int& x, int& y, int& w, int& h) { - +void CropWindow::getObservedFrameArea (int& x, int& y, int& w, int& h, int rw, int rh) { + int cropX, cropY, cropW, cropH; observedCropWin->getCropRectangle (cropX, cropY, cropW, cropH); int myCropX, myCropY, myCropW, myCropH; getCropRectangle (myCropX, myCropY, myCropW, myCropH); // translate it to screen coordinates - x = xpos + imgX + (cropX-myCropX)*zoomSteps[cropZoom].zoom; - y = ypos + imgY + (cropY-myCropY)*zoomSteps[cropZoom].zoom; + if (rw) { + x = xpos + imgAreaX+(imgAreaW-rw)/2 + (cropX-myCropX)*zoomSteps[cropZoom].zoom; + y = ypos + imgAreaY+(imgAreaH-rh)/2 + (cropY-myCropY)*zoomSteps[cropZoom].zoom; + } + else { + x = xpos + imgX + (cropX-myCropX)*zoomSteps[cropZoom].zoom; + y = ypos + imgY + (cropY-myCropY)*zoomSteps[cropZoom].zoom; + } w = cropW * zoomSteps[cropZoom].zoom; h = cropH * zoomSteps[cropZoom].zoom; } -void CropWindow::drawObservedFrame (Cairo::RefPtr cr) { - +void CropWindow::drawObservedFrame (Cairo::RefPtr cr, int rw, int rh) { + int x, y, w, h; - getObservedFrameArea (x, y, w, h); + getObservedFrameArea (x, y, w, h, rw, rh); cr->set_source_rgb (1.0, 1.0, 1.0); cr->set_line_width (4); diff --git a/rtgui/cropwindow.h b/rtgui/cropwindow.h index 396227a4a..99d816b8b 100644 --- a/rtgui/cropwindow.h +++ b/rtgui/cropwindow.h @@ -59,11 +59,15 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { int backColor; bool decorated; - // sizes, positions + // crop frame description int titleHeight, sideBorderWidth, lowerBorderWidth, upperBorderWidth, sepWidth, minWidth; - int imgAreaX, imgAreaY, imgAreaW, imgAreaH; - int imgX, imgY, imgW, imgH; + // size & position of the crop relative to the top left corner + // of the main preview area (to be confirmed) int xpos, ypos, width, height; + // size & pos of the drawable area relative to the top left corner of the crop + int imgAreaX, imgAreaY, imgAreaW, imgAreaH; + // size & pos of the piece of preview image relative to the top left corner of the crop + int imgX, imgY, imgW, imgH; // image handling @@ -72,7 +76,7 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { // crop gui listener CropGUIListener* cropgl; - PointerMotionListener* pmlistener; + PointerMotionListener* pmlistener; std::list listeners; CropWindow* observedCropWin; @@ -82,10 +86,10 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { void drawDecoration (Cairo::RefPtr cr); void drawStraightenGuide (Cairo::RefPtr cr); void drawSpotWBRectangle (Cairo::RefPtr cr); - void drawObservedFrame (Cairo::RefPtr cr); + void drawObservedFrame (Cairo::RefPtr cr, int rw=0, int rh=0); void translateCoord (int phyx, int phyy, int& imgx, int& imgy); void changeZoom (int zoom, bool notify=true, int centerx=-1, int centery=-1); - void getObservedFrameArea(int& x, int& y, int& w, int& h); + void getObservedFrameArea(int& x, int& y, int& w, int& h, int rw=0, int rh=0); public: CropHandler cropHandler; diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index c4e66cfb3..3b062ae52 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -163,25 +163,19 @@ EditorPanel::EditorPanel (FilePanel* filePanel) : beforePreviewHandler(NULL), be // Save buttons Gtk::HBox* iops = Gtk::manage (new Gtk::HBox ()); - Gtk::HBox * saveButtonBox = Gtk::manage(new Gtk::HBox()); - saveButtonBox->pack_start(*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON)), Gtk::PACK_SHRINK, 2); - saveButtonBox->pack_start(*Gtk::manage (new Gtk::Label (M("MAIN_BUTTON_SAVE"))), Gtk::PACK_SHRINK, 2); + Gtk::Image *saveButtonImage = Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON)); saveimgas = Gtk::manage (new Gtk::Button ()); - saveimgas->add(*saveButtonBox); + saveimgas->add(*saveButtonImage); saveimgas->set_tooltip_markup(M("MAIN_BUTTON_SAVE_TOOLTIP")); - Gtk::HBox * queueButtonBox = Gtk::manage(new Gtk::HBox()); - queueButtonBox->pack_start(*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-execute"), Gtk::ICON_SIZE_BUTTON)), Gtk::PACK_SHRINK, 2); - queueButtonBox->pack_start(*Gtk::manage (new Gtk::Label (M("MAIN_BUTTON_PUTTOQUEUE"))), Gtk::PACK_SHRINK, 2); + Gtk::Image *queueButtonImage = Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-execute"), Gtk::ICON_SIZE_BUTTON)); queueimg = Gtk::manage (new Gtk::Button ()); - queueimg->add(*queueButtonBox); + queueimg->add(*queueButtonImage); queueimg->set_tooltip_markup(M("MAIN_BUTTON_PUTTOQUEUE_TOOLTIP")); - Gtk::HBox * sendToEditorButtonBox = Gtk::manage(new Gtk::HBox()); - sendToEditorButtonBox->pack_start(*Gtk::manage (new Gtk::Image (argv0+"/images/gimp.png")), Gtk::PACK_SHRINK, 2); - sendToEditorButtonBox->pack_start(*Gtk::manage (new Gtk::Label (M("MAIN_BUTTON_SENDTOEDITOR"))), Gtk::PACK_SHRINK, 2); + Gtk::Image *sendToEditorButtonImage = Gtk::manage (new Gtk::Image (argv0+"/images/gimp.png")); sendtogimp = Gtk::manage (new Gtk::Button ()); - sendtogimp->add(*sendToEditorButtonBox); + sendtogimp->add(*sendToEditorButtonImage); sendtogimp->set_tooltip_markup(M("MAIN_BUTTON_SENDTOEDITOR_TOOLTIP")); iops->pack_start (*saveimgas, Gtk::PACK_SHRINK); @@ -454,14 +448,13 @@ void EditorPanel::close () { delete previewHandler; previewHandler= NULL; - rtengine::StagedImageProcessor::destroy (ipc); - ipc = NULL; - if(iarea) { iarea->imageArea->setPreviewHandler (NULL); iarea->imageArea->setImProcCoordinator (NULL); } + rtengine::StagedImageProcessor::destroy (ipc); + ipc = NULL; navigator->previewWindow->setPreviewHandler (NULL); // If the file was deleted somewhere, the openThm.descreaseRef delete the object, but we don't know here @@ -667,7 +660,7 @@ void EditorPanel::error (Glib::ustring descr) { void EditorPanel::info_toggled () { Glib::ustring infoString; - + if (!ipc || !openThm) return; const rtengine::ImageMetaData* idata = ipc->getInitialImage()->getMetaData(); if (idata && idata->hasExif()) // infoString = Glib::ustring::compose ("%1 %2\nF/%3 %4 sec\n%5: %6\n%7: %8 mm\n", @@ -977,7 +970,7 @@ bool EditorPanel::idle_imageSaved(ProgressConnector *pc,rtengine::IImage16* } void EditorPanel::saveAsPressed () { - + if (!ipc || !openThm) return; bool fnameOK = false; Glib::ustring fname; @@ -1057,11 +1050,13 @@ void EditorPanel::saveAsPressed () { } void EditorPanel::queueImgPressed () { + if (!ipc || !openThm) return; saveProfile (); parent->addBatchQueueJob (createBatchQueueEntry ()); } void EditorPanel::sendToGimpPressed () { + if (!ipc || !openThm) return; // develop image rtengine::procparams::ProcParams pparams; ipc->getParams (&pparams); diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index ef6d01ba2..496c1c690 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -75,7 +75,6 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int cr->set_source_rgba (options.cutOverlayBrush[0], options.cutOverlayBrush[1], options.cutOverlayBrush[2], options.cutOverlayBrush[3]); - // TODO: not sure if this is right. Seems to leave a thin border on the left/top, but might be bug in calling code cr->rectangle (imx, imy, imw, c1y); cr->rectangle (imx, imy+c2y, imw, imh-c2y); cr->rectangle (imx, imy+c1y, c1x, c2y-c1y+1); diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc index 175ac32b1..99f9cf6b9 100644 --- a/rtgui/imagearea.cc +++ b/rtgui/imagearea.cc @@ -76,8 +76,22 @@ void ImageArea::on_resized (Gtk::Allocation& req) { } void ImageArea::setImProcCoordinator (rtengine::StagedImageProcessor* ipc_) { + if( !ipc_ ){ + focusGrabber = NULL; + std::list::iterator i = cropWins.begin(); + if( i!=cropWins.end() ){ + (*i)->getPosition (lastClosedX, lastClosedY); + (*i)->getSize (lastClosedW, lastClosedH); + } + for( ;i!=cropWins.end();i++ ){ + delete *i; + } + cropWins.clear(); + mainCropWindow->setObservedCropWin (NULL); + } ipc = ipc_; + } void ImageArea::setPreviewHandler (PreviewHandler* ph) {