Drawable::Drawable (const Drawable& other) : Component (other.getName()) { setInterceptsMouseClicks (false, false); setPaintingIsUnclipped (true); setComponentID (other.getComponentID()); setTransform (other.getTransform()); if (auto* clipPath = other.drawableClipPath.get()) setClipPath (clipPath->createCopy()); }
void WPainter::drawText(const WRectF& rectangle, WFlags<AlignmentFlag> alignmentFlags, TextFlag textFlag, const WString& text) { if (textFlag == TextSingleLine) drawText(rectangle, alignmentFlags, text); else { if (!(alignmentFlags & AlignVerticalMask)) alignmentFlags |= AlignTop; if (!(alignmentFlags & AlignHorizontalMask)) alignmentFlags |= AlignLeft; if (device_->features() & WPaintDevice::CanWordWrap) device_->drawText(rectangle.normalized(), alignmentFlags, textFlag, text); else if (device_->features() & WPaintDevice::HasFontMetrics) { #ifndef WT_TARGET_JAVA MultiLineTextRenderer renderer(*this, rectangle); AlignmentFlag horizontalAlign = alignmentFlags & AlignHorizontalMask; AlignmentFlag verticalAlign = alignmentFlags & AlignVerticalMask; /* * Oh irony: after all these years of hating CSS, we now * implemented an XHTML renderer for which we need to use the * same silly workarounds to render the text with all possible * alignment options */ WStringStream s; s << "<table style=\"width:" << (int)rectangle.width() << "px;\"" "cellspacing=\"0\"><tr>" "<td style=\"padding:0px;height:" << (int)rectangle.height() << "px;color:" << pen().color().cssText() << ";text-align:"; switch (horizontalAlign) { case AlignLeft: s << "left"; break; case AlignRight: s << "right"; break; case AlignCenter: s << "center"; break; default: break; } s << ";vertical-align:"; switch (verticalAlign) { case AlignTop: s << "top"; break; case AlignBottom: s << "bottom"; break; case AlignMiddle: s << "middle"; break; default: break; } s << ";" << font().cssText(false); s << "\">" << WWebWidget::escapeText(text, true).toUTF8() << "</td></tr></table>"; save(); /* * FIXME: what if there was already a clip path? We need to combine * them ... */ WPainterPath p; p.addRect(rectangle.x() + 1, rectangle.y() + 1, rectangle.width() - 2, rectangle.height() - 2); setClipPath(p); setClipping(true); renderer.render(WString::fromUTF8(s.str())); restore(); #endif // WT_TARGET_JAVA } else throw WException("WPainter::drawText(): device does not support " "TextWordWrap or FontMetrics"); } }
/// /// iconPie(): Draws a pie with a given color on the individual icon. Each pie corresponds to one /// categorical variable. The color corresponds to the level in that variable. /// void DrawingCanvas::iconPie( double x, double y, Individual *pIndividual ){ // // Handle virtual individuals: // if(pIndividual->isVirtual()){ //_body << "<circle cx=\"" << x << "\" cy=\"" << y << "\" r=\"" << 0.5*DrawingMetrics::getScalingFactor() << "\""; //_body << " class=\"thinLine\""; //_body << "/>\n"; return; } // // Get here if non-virtual: // // // How many sections are there? // unsigned sections = pIndividual->getDataTable()->getIconColumnCount(); if(!sections){ // // No affected fields : Draw a small circle in the middle: // (Nice for debugging -- otherwise comment out ) // //_body << "<circle cx=\"" << x << "\" cy=\"" << y << "\" r=\"" << 0.5*DrawingMetrics::getScalingFactor() << "\""; //_body << " class=\"thinLine\""; //_body << "/>\n"; return; } // // Setup clipping Id, if needed: // std::string clipId = pIndividual->getId().get() + "_clipPath"; double radius=DrawingMetrics::getIconRadius(); bool isMale = false; if( pIndividual->getGender().getEnum()==Gender::MALE ){ setClipPath(x,y,clipId); // // Increase radius for clipping: // radius*=Number::SQRT_TWO; isMale = true; _body << "<g clip-path=\"url(#" << clipId << ")\">\n"; }else{ // // Empty g with no clipping for female: // _body << "<g>\n"; } // // Iterate over _iconColumns: // const DataTable *pDT = pIndividual->getDataTable(); double arcAngle = 2.0*Number::PI/sections; double startAngle = 0.5*Number::PI - arcAngle; double endAngle = startAngle+arcAngle; for(unsigned i=0;i<sections;i++){ // // Get the data column of the ith icon column: // DataColumn * pDC = pDT->getColumn( pDT->getIconColumnIndex(i) ); // // Get the UniqueList for this column: // const UniqueList * pUL = pDC->getUniqueList(); // // Only process if there are some non-missing levels // present: // if(!pUL->getLevels()) continue; // // What is the level and label in the UniqueList corresponding to the data value // for this individual? // unsigned level; std::string label; pUL->getOrdinalAndLabelForKey( pDC->getDataAtIndex( pIndividual->getRowIndex() ),label,level ); // // UniqueList ordinals are 1-offset, so we should only get zero back // if the key was not found, which should never happen: // if(level==0) throw Exception("DrawingCanvas::iconPie()","UniqueList returned ordinal 0."); // // Level is 1-offset, so subtract: // level--; // // Get the color series corresponding to this icon column: // // NOTA BENE: (1) IF there is only one section, we use the // blackAndWhite series // UNLESS there is a color override. // // (2) OTHERWISE if there are more than one section, // we use the color series // UNLESS there is a black-and-white override. // ColorSeries *pCS; if(sections==1){ if(DrawingMetrics::getColor()) pCS = pDT->getColorSeriesFromStack(i); else pCS = pDT->getBlackAndWhiteSeriesFromStack(i); }else{ if(DrawingMetrics::getBlackAndWhite()) pCS = pDT->getBlackAndWhiteSeriesFromStack(i); else pCS = pDT->getColorSeriesFromStack(i); } // // Assume reversed for now: // bool reversed=true; std::string arcClass="blackInkLetter"; if(!pCS->reversedSeriesUseBlackInkAtLevel(level)) arcClass="whiteInkLetter"; // // If the number of sections is small, use a larger font size // if(sections == 1) arcClass += "_1"; else if(sections == 2) arcClass += "_2"; else if(sections == 3) arcClass += "_3"; arc(x,y,radius,startAngle,endAngle,(reversed?pCS->reversedSeriesGetColorAtLevel(level):pCS->getColorAtLevel(level)),label,arcClass,isMale); startAngle+=arcAngle; endAngle+=arcAngle; // For the first time draw the icon legend too: if(!_iconLegendFlag){ _iconLegendFlag = true; _iconLegend.setPedigreeTable(pDT); } } _body << "</g>\n"; return; }
/// /// iconQuadrantFill(): Shade quadrants of the icon to indicate specific levels. /// This method has a limit of 16 distinct levels. /// void DrawingCanvas::iconQuadrantFill( double x, double y, Individual *pIndividual ){ // // Handle virtual individuals: // if(pIndividual->isVirtual()){ return; } // // Get here if non-virtual: // // // How many sections are there? // unsigned sections = pIndividual->getDataTable()->getIconColumnCount(); if(!sections){ // // No affected fields : Draw a small circle in the middle: // (Nice for debugging -- otherwise comment out ) // //_body << "<circle cx=\"" << x << "\" cy=\"" << y << "\" r=\"" << 0.5*DrawingMetrics::getScalingFactor() << "\""; //_body << " class=\"thinLine\""; //_body << "/>\n"; return; } // // Setup clipping Id, if needed: // double radius=DrawingMetrics::getIconRadius(); bool isMale = false; if( pIndividual->getGender().getEnum()==Gender::MALE ){ std::string clipId = pIndividual->getId().get() + "_clipPath"; setClipPath(x,y,clipId); // // Increase radius for clipping: // radius*=Number::SQRT_TWO; isMale = true; _body << "<g clip-path=\"url(#" << clipId << ")\">\n"; }else{ // // Empty g with no clipping for female: // _body << "<g>\n"; } // // NOTA BENE: The quadrant fill method looks only at the // FIRST iconColumn and ignores any additional icon columns: // const DataTable *pDT = pIndividual->getDataTable(); // // Get the data column of the first icon column: // DataColumn * pDC = pDT->getColumn( pDT->getIconColumnIndex(0) ); // // Get the UniqueList for this column: // const UniqueList * pUL = pDC->getUniqueList(); // // Only process if there are some non-missing levels // present: // if(!pUL->getLevels()){ _body << "</g>\n"; return; } // // What is the level and label in the UniqueList corresponding to the data value // for this individual? // //unsigned level; //std::string label; //pUL->getOrdinalAndLabelForKey( pDC->getDataAtIndex( pIndividual->getRowIndex() ),label,level ); // Data* data = pDC->getDataAtIndex( pIndividual->getRowIndex()); // // Draw a dot for missing values: // if(data->isMissing()){ drawText(x,y,".","blackInkLetter_1"); _body << "</g>\n"; return; } std::string label; std::string svalue = data->get(); std::istringstream i(svalue); int level; bool converted = (i>>level); if(!converted || level<0 || level>15 ){ // // (1) Unable to convert data string to integer, or // (2) integer value is out of range // double lineSpacing,xAdvance,yMinimum,yMaximum; _lasiWrapper.getDimensions(svalue,&lineSpacing,&xAdvance,&yMinimum,&yMaximum); y+= 0.5*(yMaximum-yMinimum); drawIconText(x,y,svalue,"blackInkLetter_1"); _body << "</g>\n"; return; } // // Fill arc for each quadrant based on level: // Note: Madeline's "arc()" method doesn't use the standard counter-clockwise quadrants // starting at 0, so the order of the 4 "if" statements below may appear mixed up but // this actually results in shading of the quadrants according to standard notation: // if(level & 0x02 ){ arc(x,y,radius,0,0.5*Number::PI,"#000",label,"whiteInkLetter",isMale); } if(level & 0x01 ){ arc(x,y,radius,0.5*Number::PI,Number::PI,"#000",label,"whiteInkLetter",isMale); } if(level & 0x08 ){ arc(x,y,radius,Number::PI,1.5*Number::PI,"#000",label,"whiteInkLetter",isMale); } if(level & 0x04 ){ arc(x,y,radius,1.5*Number::PI,2.0*Number::PI,"#000",label,"whiteInkLetter",isMale); } // // For the first time draw the icon legend too: // //if(!_iconLegendFlag){ // _iconLegendFlag = true; // _iconLegend.setPedigreeTable(pDT); //} _body << "</g>\n"; return; }