How to provide multiple links in one cell of a QTableView - html

I have a QTableView in my project, in which several columns display data that includes a hyperlink. I use a delegate class for these to set it up so that when the cell in the column is clicked, it opens the linked page in the browser. This works great... when it's only one value being linked to one page. For example, I may have a list of search values for mysite.com where the columns have values A, B, C, etc.. If the user clicks on the cell in this column with A, it will open a hyperlink for mysite.com/A (again, this part works fine). However, I now need to add a column that may have something like "A,B", where it needs to support links to search for A AND B in the same cell depending on which they click. I've been searching around online for a while now and it seems like this probably can't be done with a delegate. I have a line in a QTextBrowser elsewhere in my code where I can do this via HTML, like this:
QString toShow;
for(int i = 0; i < searchValueList.size(); i++)
{
toShow.append("`<a href=\"www.mysite.com/" + searchValueList.at(i) + "\"`>" +
searchValueList.at(i) + "`</a`>";
}
However I can't find any way to set the cells in a QTableView to recognize HTML formatting or Rich Text, and alas I'm not even sure that's possible. Is there any way at all to do what I'm trying to accomplish?

You can create a custom QItemDelegate for the specific column in which you can display rich text. The delegate could be like :
class RichTextDelegate: public QItemDelegate
{
public:
RichTextDelegate(QObject *parent = 0);
void paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
};
RichTextDelegate::RichTextDelegate(QObject *parent):QItemDelegate(parent)
{
}
void RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if( option.state & QStyle::State_Selected )
painter->fillRect( option.rect, option.palette.highlight() );
painter->save();
QTextDocument document;
document.setTextWidth(option.rect.width());
QVariant value = index.data(Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml(value.toString());
painter->translate(option.rect.topLeft());
document.drawContents(painter);
}
painter->restore();
}
You should set the item delegate for the specific column :
ui->tableView->setItemDelegateForColumn(colIndex, new RichTextDelegate(ui->tableView));
Now if you set the model text for the specific column in a row to a rich text, it will be shown properly :
model->item(rowIndex, colIndex)->setText(someRichText);

Related

Copy table with inline images

I am trying to copy the contents of a GDoc into another GDoc. This works pretty well for all the different element types. Including a table (Enum DocumentApp.ElementType.TABLE). However, if the table contains an inline image (Enum DocumentApp.ElementType.INLINE_IMAGE) the image is not copied correctly.
Here is a link to the source GDoc example. https://docs.google.com/document/d/14kXjC0CTkEgmD7Ttv0YKL9ubfDRHbL1hCVOqOiyiEDU/edit#. Find the row in the table with the New Zealand flag. The flag is not copied correctly into the target GDoc's new table.
I am simply finding the table object in the source document (above) and using Body::insertTable(childIndex,table) to insert it into the target GDoc's Body. Most other elements in the table copy OK. Including an embedded Google Drawing. But not the inline image.
I had a similar problem and in my case, the GLYPH_TYPE of numbered lists got lost, too. But I detected that the images will be fine copied if the CellChildren are copied one by one.
So I solved my problem by copying the new table at whole and then replacing the contents of each dstTable cell one by one from the original table.
This works fine now even with nested tables, since the function calls itself, if another table within the table is detected.
And the problem with the lost ListItem attribute is solved, too by setting the attributes additionally after the insert.
Here is my well working code segment:
Firstly I detect the table and insert it in the dstBody ...
...
dstChild = srcChild.copy();
switch( dstChild.getType() ) {
case DocumentApp.ElementType.PARAGRAPH:
...
case DocumentApp.ElementType.LIST_ITEM:
...
case DocumentApp.ElementType.TABLE:
var newTable =
dstBody.insertTable( dstBody.getNumChildren()-1, dstChild );
copyTableCellByCell( dstChild, newTable );
break;
....
}
And this is the maybe recursive function that replaces each cell by first clearing it and copying the content from the original table:
function copyTableCellByCell( srcTable, dstTable ) {
var numRows = dstTable.getNumRows();
var dstRow, numCells, dstCell, actCellIndex;
for ( var actRowIndex = 0; actRowIndex < numRows; actRowIndex++ ) {
dstRow = dstTable.getRow( actRowIndex );
numCells = dstRow.getNumCells();
for ( actCellIndex = 0; actCellIndex < numCells; actCellIndex++ ) {
dstCell = dstRow.getCell( actCellIndex );
dstCell.clear();
var srcCell = srcTable.getCell( actRowIndex, actCellIndex );
var numCellChildren = srcCell.getNumChildren();
for ( var y = 0; y < numCellChildren; y++ ) {
var cellChild = srcCell.getChild( y );
var childCopy = cellChild.copy();
switch( childCopy.getType() ) {
case DocumentApp.ElementType.PARAGRAPH:
dstCell.insertParagraph( y, childCopy );
break;
case DocumentApp.ElementType.LIST_ITEM:
// that the GLYPH_TYPE doesn't get lost
var atts = childCopy.getAttributes();
var
newListItem = dstCell.insertListItem( y, childCopy );
newListItem.setAttributes( atts );
break;
case DocumentApp.ElementType.TABLE:
var newTable =
dstCell.insertTable( y, childCopy );
copyTableCellByCell( cellChild, newTable );
break;
}
}
// remove the very first empty paragraph in the cell
while ( (y = dstCell.getNumChildren()) > numCellChildren ) {
dstCell.getChild( y - 1 ).removeFromParent();
}
}
}
}
This could be fine tuned, of course.
You could just search and pick out the inline images, if you want the servers have to do less work.
Methods that you can use to copy an Inline Image can be found in Class InlineImage and there are even additional methods if an inline image was contained within a ListItem or Paragraph.
I found a none coding solution to this bug.
Insert the image as a "drawing" in the source document.
Click the table cell where images needs to be inserted.
Click "insert" on the document's menu.
Click "insert drawing".
In drawing pane add image to be inserted.
Save and close.
The result will be an image in the table that gets copied perfectly along with the type TABLE.

How to draw timelines in a tree table

I am writing an analyzer to visually see where my application is spending time. The interface that I am trying to achieve (see below) is something similar to a tree table with
lines or boxes to denote response time.
be a collapsible tree like graph
the ability to display metrics in the table columns (e.g., start time, cost, etc)
the ability to display the labels or description and metrics on the left and lines on the right
I create the following diagram (see below) in R -- unfortunately, although the graph production is automated, the approach is not interactive. I was wondering if you could suggest a better way -- maybe a tree table. I looked at many Swing, JavaFx tree table examples. I have not seen an example that has lines (time lines) in a tree table.
Any suggestions would be greatly appreciated. Thanks in advance.
You can show any node in a TreeTableCell using the grahic property in javaFX. This includes Rectangles.
This is a simple example of showing bars in a column using Rectangles:
// Arrays in TreeItems contain {startValue, endValue} (both positive)
TreeItem<int[]> root = new TreeItem<>(new int[]{0, 10});
root.getChildren().addAll(new TreeItem<>(new int[]{0, 5}), new TreeItem<>(new int[]{5, 10}));
TreeTableView<int[]> ttv = new TreeTableView<>(root);
// Column displaying bars based on data of TreeItem. Do not use this as
// the first column, otherwise the alignment be off depending on the
// distance to the root.
TreeTableColumn<int[], int[]> column = new TreeTableColumn<>();
column.setCellValueFactory(c -> c.getValue().valueProperty());
final double BAR_SIZE = 20;
column.setCellFactory((t) -> new TreeTableCell<int[], int[]>() {
// the bar
private final Rectangle rectangle = new Rectangle(0, 10);
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// bar invisible by default
rectangle.setVisible(false);
setGraphic(rectangle);
}
#Override
protected void updateItem(int[] item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
// resize and display bar, it item is present
rectangle.setWidth((item[1] - item[0]) * BAR_SIZE);
rectangle.setTranslateX(item[0] * BAR_SIZE);
rectangle.setVisible(true);
} else {
// no item -> hide bar
rectangle.setVisible(false);
}
}
});
// add a columns new column
// add a additional empty column at the start to prevent bars being
// aligned based on distance to the root
ttv.getColumns().addAll(new TreeTableColumn<>(), column);
Things you need to do
use a data type different to int[]; the cell value factory and TreeTableCell needs to be adjusted accordingly; an example of a more complex model can be found e.g. in the oracle tutorial: https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/tree-table-view.htm
Choose better colors; These colors could e.g. be stored in a Map and created if a new one is needed.
add additional colums

Customize html output for a field's droplist options

I have a field called icon, which is a droplist sourced from folder in the content tree. I would like the list to not just show the text value(shown in the screen shot) but also to utilize an icon font and display what the actual icon would look like. Basically customizing the content editor's droplist for this field from:
<option value="gears">gears</option>
to
<option value="gears">gears <span class="my-icon-font-gears"></span></option>
Is there any documentation on how to modify the outputted html for a droplist, and to modify the content editor page to load another link, in this case a font-file.
I created a module on the marketplace that does something similar. You can have a look here. There is some documentation on there explaining how to use it.
The code is also on Git if you want to have a look.
Suggest you use the Droplink field type instead of the Droplist, since the value is stored by GUID and this will lead to less longer term problems if the link item is renamed or moved. In any case you need a custom field, inherit from Sitecore.Shell.Applications.ContentEditor.LookupEx (which is the DropLink field type) and override the DoRender() method with the custom markup you require.
It's not possible to embed a span tag since the option tag cannot contain other tags as it is invalid HTML. Adding it will cause the browser to strip it out. You can however set the class on the option itself and style that.
`<option value="gears" style="my-icon-font-gears">gears</option>`
Here is some sample code to achieve the field.
using System;
using System.Web.UI;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
namespace MyProject.CMS.Custom.Controls
{
public class StyledLookupEx : Sitecore.Shell.Applications.ContentEditor.LookupEx
{
private string _styleClassField;
private string StyleClassField
{
get
{
if (String.IsNullOrEmpty(_styleClassField))
_styleClassField = StringUtil.ExtractParameter("StyleClassField", this.Source).Trim();
return _styleClassField;
}
}
// This method is copied pasted from the base class apart from thhe single lined marked below
protected override void DoRender(HtmlTextWriter output)
{
Assert.ArgumentNotNull(output, "output");
Item[] items = this.GetItems(Sitecore.Context.ContentDatabase.GetItem(this.ItemID, Language.Parse(this.ItemLanguage)));
output.Write("<select" + this.GetControlAttributes() + ">");
output.Write("<option value=\"\"></option>");
bool flag1 = false;
foreach (Item obj in items)
{
string itemHeader = this.GetItemHeader(obj);
bool flag2 = this.IsSelected(obj);
if (flag2)
flag1 = true;
/* Option markup modified with class added */
output.Write("<option value=\"" + this.GetItemValue(obj) + "\"" + (flag2 ? " selected=\"selected\"" : string.Empty) + " class=\"" + obj[StyleClassField] + "\" >" + itemHeader + "</option>");
}
bool flag3 = !string.IsNullOrEmpty(this.Value) && !flag1;
if (flag3)
{
output.Write("<optgroup label=\"" + Translate.Text("Value not in the selection list.") + "\">");
output.Write("<option value=\"" + this.Value + "\" selected=\"selected\">" + this.Value + "</option>");
output.Write("</optgroup>");
}
output.Write("</select>");
if (!flag3)
return;
output.Write("<div style=\"color:#999999;padding:2px 0px 0px 0px\">{0}</div>", Translate.Text("The field contains a value that is not in the selection list."));
}
}
}
This field adds a custom properties to allow you to specify the linked field to use for the style class. The assumption is that you have another single line text field on the linked item to specify the CSS class.
Usage: Set the source property of the field in the following format:
Datasource={path-or-guid-to-options}&StyleClassField={fieldname}
e.g. Datasource=/sitecore/content/lookup/iconfonts&StyleClassField=IconClassName
To use this new field compile the above code in to project, switch over to the core database and then create a new field type – you can duplicate the existing Droplink field located in /sitecore/system/Field types/Link Types/Droplink. Delete the existing Control field and instead set the ASSEMBLY and CLASS fields to point to your implementation.
You also need to load a custom CSS stylesheet with the style defintions into the Content Editor, which you can achieve that by following this blog post.

Qtableview, add widgets between rows

I have a QTableview (multiple columns, sorting) and would like to add a button that shows additional data below the current row. For the rendering of this additional data I would like to use another widget, that fills up a variable height and spans all the rows.
While I know that I can create delegates for cells, I was wondering if this is possible for rows or whether that would mean that I would have to inherit from a tableview and modify its paint method, which seems to be lot of work for a novice like me.
QVariant YourTableModel::data(const QModelIndex & index, int32_t role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
switch (index.column())
{
case YOUR_COL:
double theDouble = getDoubleFromModelSomewhere();
return QString::number(theDouble, 'f', 3); // Format shows 3 decimals
}
}
return QVariant();
}
If I have understood your question properly then I think this is the answer.
QTableView *view = new QTableView;
view->setItemDelegateForRow(int row, QAbstractItemDelegate *delegate);

Java: Is there any method in JTextPane that does the same thing as append() in JTextArea?

The program lets the user type in a command in a textfield then whatever they typed will show in the text area. If it is keywords such as yes it will turn green, however I cannot set just one line of text green in a text area so I need to use a text pane.
The problem is that if I use a text pane I can't use the append method anymore.
private final static String newline = "\n";
private void enterPressed(java.awt.event.KeyEvent evt) {
int key = evt.getKeyCode();
if (key == KeyEvent.VK_ENTER)
{
String textfieldEnterdValue = textfield1.getText().toString();
this.TextArea1.append("> "+tb1EnterdValue+newline);
this.tb1.setText("");
if((tb1EnterdValue.equals("yes")) )
{
TextArea1.setForeground(Color.green);
}
}
JTextPane uses Document as a model. This is necessary to support the use of multiple colors and fonts.
So, to append to a JTextPane, you need to modify the Document.
The following methods are available :
insertString(int pos, String value, AttributeSet att)
remove(int pos, int length)
For example, this will append value to the end of the document.
Document d = textPane.getDocument();
d.insertString(d.getLength(), value, null);
Additionally, you may want to call scrollRectToVisible(Rectangle) with the result of modelToView(int) to ensure the newly added line is on screen.
I think you'll need to do that directly on the underlying document.
Something like this:
String value = textfield1.getText(); // no need for toString() here!
textPane.getDocument().insertString(textPane.getCaretPosition(), value, null);