SWT: Integrate clickable link into StyledText - hyperlink

With the help of this question I was able to figure out how I can display a link inside a StyledText widget in SwT. The color is correct and even the cursor changes shape when hovering over the link.
So far so good, but the link is not actually clickable. Although the cursor changes its shape, nothing happens if clicking on the link. Therefore I am asking how I can make clicking the link to actually open it in the browser.
I thought of using a MouseListener, tracking the click-location back to the respective text the click has been performed on and then deciding whether to open the link or not. However that seems way too complicated given that there already is some routine going on for changing the cursor accordingly. I believe that there is some easy way to do this (and assuring that the clicking-behavior is actually consistent to when the cursor changes its shape).
Does anyone have any suggestions?
Here's an MWE demonstrating what I have done so far:
public static void main(String[] args) throws MalformedURLException {
final URL testURL = new URL("https://stackoverflow.com/questions/1494337/can-html-style-links-be-added-to-swt-styledtext");
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, true));
StyledText sTextWidget = new StyledText(shell, SWT.READ_ONLY);
final String firstPart = "Some text before ";
String msg = firstPart + testURL.toString() + " some text after";
sTextWidget.setText(msg);
sTextWidget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
StyleRange linkStyleRange = new StyleRange(firstPart.length(), testURL.toString().length(), null, null);
linkStyleRange.underline = true;
linkStyleRange.underlineStyle = SWT.UNDERLINE_LINK;
linkStyleRange.data = testURL.toString();
sTextWidget.setStyleRange(linkStyleRange);
shell.open();
while(!shell.isDisposed()) {
display.readAndDispatch();
}
}

Okay I was being a little too fast on posting this question... There's a snippet that deals with exactly this problem and it shows, that one indeed has to use an extra MouseListener in order to get things working.
The snippet can be found here and this is the relevant part setting up the listener:
styledText.addListener(SWT.MouseDown, event -> {
// It is up to the application to determine when and how a link should be activated.
// In this snippet links are activated on mouse down when the control key is held down
if ((event.stateMask & SWT.MOD1) != 0) {
int offset = styledText.getOffsetAtLocation(new Point (event.x, event.y));
if (offset != -1) {
StyleRange style1 = null;
try {
style1 = styledText.getStyleRangeAtOffset(offset);
} catch (IllegalArgumentException e) {
// no character under event.x, event.y
}
if (style1 != null && style1.underline && style1.underlineStyle == SWT.UNDERLINE_LINK) {
System.out.println("Click on a Link");
}
}
}
});

Related

Printing using ngx-extended-pdf-viewer on iOS and Mobile Safari or Chrome

I have an Angular 7 app that is using ngx-extended-pdf-viewer to render a PDF that I get as a byte array from the web.api. I have no issues rendering the PDF or even printing it from any desktop application. ngx-extended-pdf-viewer as a print button built right in. However, when trying to print from Safari on an iPhone (iOS 12) it only prints a blank page with the url at the bottom. The actual PDF does not print. With Chrome on iOS it doesn't do anything that I can see. I am pretty new to Angular and actually to mobile web development, so perhaps lack of knowledge is getting me. The PDF viewer is in a mat-tab, so not sure if maybe that is causing some issues??
I have tried other packages, but they all seem to be based on the same pdf.js code from Mozilla. Also this is the only one I've found so far that has a print. I was thinking about perhaps trying pdf.js outside of an npm package, but so far have not found solid directions on getting this to work in Angular. I'm sure it will, but all directions I have found seem to omit details. Such as, put this code in your app. They just fail to say where in the app.
From the web.api:
[HttpPost("GetPdfBytes/{PdfId}")]
public ActionResult<byte[]> GetPdfBytesId([FromBody]int id)
{
string exactPath = string.Empty;
if (id == 1)
{
exactPath = Path.GetFullPath("pdf-test.pdf");
}
else if (id == 2)
{
exactPath = Path.GetFullPath("DPP.pdf");
}
else if (id == 3)
{
exactPath = Path.GetFullPath("Request.pdf");
}
else
{
exactPath = Path.GetFullPath("Emergency Issue.pdf");
}
byte[] bytes = System.IO.File.ReadAllBytes(exactPath);
return Ok(bytes);
}
The HTML:
<mat-tab label="PDF">
<ng-template matTabContent>
<ngx-extended-pdf-viewer *ngIf="visible[2]" id="pdf3" [src]="pdfSrc3" useBrowserLocale="true" delayFirstView="1000" showSidebarButton="false"
showOpenFileButton="false" >
</ngx-extended-pdf-viewer>
</ng-template>
</mat-tab>
TypeScript:
getPDFBytesId(id: string) {
this.getPDFFromServicePdfBytesId(Number(id)).subscribe(
(data: any) => {
this.pdfSrc3 = this.convertDataURIToBinary(data);
},
error => {
console.log(error);
}
);
}
// hits the web.api
getPDFFromServicePdfBytesId(id: number): Observable<any> {
const body = id;
return this.http.post<any>('http://localhost:5000/api/values/GetPdfBytes/' + id, body);
}
// converts what we got back to a Uint8Array which is used by the viewer
convertDataURIToBinary(dataURI: string) {
const raw = window.atob(dataURI);
const rawLength = raw.length;
const array = new Uint8Array(new ArrayBuffer(rawLength));
for (let i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}

Display contact address in ios using xamarin

absolute beginner with xamarin.
Followed the following tutorial to try and simply click a button to display the contact list, select a contact, and then to display firstname, surname, and address on the screen.
https://github.com/xamarin/recipes/tree/master/Recipes/ios/shared_resources/contacts/choose_a_contact
Managed to get the firstname and surname to be displayed, but cannot get the address. Constantly getting the error
Foundation.MonoTouchException: Objective-C exception thrown. Name: CNPropertyNotFetchedException Reason: A property was not requested when contact was fetched.
On the
contanct.PostalAddresses
This is the snippet of code:-
partial void UIButton197_TouchUpInside(UIButton sender)
{
// Create a new picker
var picker = new CNContactPickerViewController();
// Select property to pick
picker.DisplayedPropertyKeys = new NSString[] { CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.PostalAddresses };
// Respond to selection
var pickerDelegate = new ContactPickerDelegate();
picker.Delegate = pickerDelegate;
pickerDelegate.SelectionCanceled += () => {
SelectedContact1.Text = "";
};
pickerDelegate.ContactSelected += (contact) => {
SelectedContact1.Text = contact.GivenName;
SelectedContact2.Text = contact.FamilyName;
SelectedContact3.Text = contact.PostalAddresses
};
pickerDelegate.ContactPropertySelected += (property) => {
SelectedContact1.Text = property.Value.ToString();
};
// Display picker
PresentViewController(picker, true, null);
}
Am i missing something?
Seem to have resolved this if anyone else is having a similar issue.
The solution was to completely close down visual studio on the mac and re-open it.
Originally, i was stopping the project, and re-building. Possibly a bug, but non of my changes where being picked up.
A simple re-start kicked it back in

pack figure doesn't work with computeFigure

I seem to have a problem with the pack-figure and using computeFigure.
Below is some code to reproduce the behaviour I have.
public void main() {
bool redraw = false;
str boxWidthProp = "";
Figure topBar = hcat([text("Width"), combo(["1", "2"], void(str s){ boxWidthProp = s; }, hshrink(0.1))
, button("Redraw", void() {redraw = true; }, resizable(false))], vshrink(0.05), hgap(5));
Figure getTreemap() {
return computeFigure(bool () { bool temp = redraw; redraw = false; return temp; }, Figure() {
int sz = 20;
if (boxWidthProp == "2")
sz = 100;
b = box(size(sz, sz), fillColor("Red"), resizable(false));
t = text(str() {return "w: <sz>; prop: <boxWidthProp>"; });
Figures boxes = [];
boxes += b;
boxes += t;
//return pack(boxes, std(gap(5)));
return vcat([t,b]);
});
}
vc = vcat([topBar, getTreemap()]);
render(vc);
}
When running this code, you get a new screen with a combo, button, label and a box.
When you change the combo you'll see the value of the label change.
When you click the button the box will change (it's size).
When you change the combo again, you'll see the label change and clicking - The button will do it's job again.
This can continue.
Now uncomment the line "return pack ..." and comment "return vcat".
Run the code again.
You will see the same screen.
Changing the combo works.
Clicking the button will change the box.
But from now on everything stops.
Changing the combo, nothing.
Clicking the button, nothing.
My guess here is that there is a problem with the pack-figure. But I'm a total newbie with rascal (and eclipse), so it might be that I'm missing something.

Difficulty updating InkPresenter visual after removing strokes?

I am creating an inkcanvas (CustomInkCanvas) that receives Gestures. At different times during its use, I am placing additional panels over different parts of the inkcanvas. All is well, and the part of the CustomInkCanvas that is not covered by another panel responds appropriately to ink and gestures.
However, occasionally a Gesture is not recognized, so in the default code of the gesture handler, I am trying to remove the ink from the CustomInkCanvas--even when it is not the uppermost panel.
How is this done?
Note: I have tried everything I can think of, including:
Dispatcher with Background update as:
cink.InkPresenter.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
Clearing the strokes with:
Strokes.Clear();
cink.InkPresenter.Strokes.Clear();
Invalidating the visual with:
cink.InkPresenter.InvalidateVisual();
cink.InavlidateVisual();
And even
foreach (Stroke s in Strokes)
{
cink.InkPresenter.Strokes.Remove(s);
}
Here is the full code...
void inkCanvas_Gesture(object sender, InkCanvasGestureEventArgs e)
{
CustomInkCanvas cink = sender as CustomInkCanvas;
ReadOnlyCollection<GestureRecognitionResult> gestureResults = e.GetGestureRecognitionResults();
StylusPointCollection styluspoints = e.Strokes[0].StylusPoints;
TextBlock tb; // instance of the textBlock being used by the InkCanvas.
Point editpoint; // user point to use for the start of editing.
TextPointer at; // textpointer that corresponds to the lowestpoint of the gesture.
Run parentrun; // the selected run containing the lowest point.
// return if there is no textBlock.
tb = GetVisualChild<TextBlock>(cink);
if (tb == null) return;
// Check the first recognition result for a gesture.
isWriting = false;
if (gestureResults[0].RecognitionConfidence == RecognitionConfidence.Strong)
{
switch (gestureResults[0].ApplicationGesture)
{
#region [Writing]
default:
bool AllowInking;
editpoint = GetEditorPoint(styluspoints, EditorPoints.Writing);
at = tb.GetPositionFromPoint(editpoint, true);
parentrun = tb.InputHitTest(editpoint) as Run;
if (parentrun == null)
{
AllowInking = true;
TextPointer At = tb.ContentEnd;
Here = (Run)At.GetAdjacentElement(LogicalDirection.Backward);
}
else
{
Here = parentrun;
AllowInking = String.IsNullOrWhiteSpace(parentrun.Text);
}
*** THIS FAILS TO REMOVE THE INK FROM THE DISPLAY ???? *********
if (AllowInking == false)
{
foreach (Stroke s in Strokes)
{
cink.InkPresenter.Strokes.Remove(s);
}
// remove ink from display
// Strokes.Clear();
// cink.InkPresenter.Strokes.Clear();
cink.InkPresenter.InvalidateVisual();
cink.InkPresenter.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
return;
}
// stop the InkCanvas from recognizing gestures
EditingMode = InkCanvasEditingMode.Ink;
isWriting = true;
break;
#endregion
}
}
}
private static Action EmptyDelegate = delegate() { };
Thanks in advance for any help.
It would be nice to get a guru response to this, but for anybody else getting here, apparently the strokes that go into creating the gesture have not yet been added to the InkCanvas, so there is nothing to remove or clear from the inkcanvas from within the gesture handler. Strokes are only added to the InkCanvas AFTER the gesture handler. The solution this newbie ended up with was to set a flag when ink was not allowed, and then act on it in the StrokesChanged handler like:
if (AllowInking == false)
{
ClearStrokes = true;
return;
}
void Strokes_StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
{
if (ClearStrokes == true)
{
ClearStrokes = false;
Strokes.Clear();
return;
}
All works now. Is there a better way?

how to reduce process speed of Entity FrameWork code?

I used this code in order to Read & Update a value in database which indicate Pageviews of a website.
void Session_Start(object sender, EventArgs e)
{
intPageView++;
Session.Add("Online", intPageView);
DataLayer.MainFunction.UpdateOnlineUser();
}
public static void UpdateOnlineUser()
{
try
{
int intCount = 0;
TaffyPetEntities db = new TaffyPetEntities();
T_Setting t_s = db.T_Setting.SingleOrDefault(i => i.ID == 1);
intCount = Convert.ToInt32(t_s.Page_counter);
intCount++;
t_s = new T_Setting();
t_s = db.T_Setting.First(i => i.ID == 1);
t_s.Page_counter = intCount;
db.SaveChanges();
}
catch (Exception err)
{
DataLayer.Error.RegisterError("MainFunction.cs", err.Message);
}
}
in local system every thing was good also running speed was good, but when website published and uploaded on the server every thing is changed. The problem is, at the first we haven't any data outout and when you clicked on each link on the website it takes too long to show another page.
to find out problem, i used Performance Analysis of VIsual Studio. at the result page of this analysis i found that this part of above code use 70% of total process:
T_Setting t_s = db.T_Setting.SingleOrDefault(i => i.ID == 1);
The questions is:
1. Is it correct syntax for fetching data in Entity FrameWork at the Data Layer (DAL) of a website?
2. Can i change this code with another method in order to reduce process speed?
Thanks.

Resources