URL avec accents dans un NSTextView

regattaregatta Membre
13:27 modifié dans API AppKit #1
Bonjour,

j'ai un texte affiché dans un NSTextView (type RTF) et je cherche à  transformer en URL un mot sélectionné : le mot "toto" doit devenir "http://toto.htm".

J'ai créé un menu contextuel qui appelle une routine dans laquelle le mot sélectionné est convertit et la conversion vient remplacer le mot sélectionné dans la NSTextView.

J'ai utilisé une méthode "artisanale" dans laquelle je code en RTF le lien. Cela fonctionne si le mot n'a pas d'accent ; sinon le caractère accentué est mal codé  :(
J'ai pourtant testé avec divers encodages dont NSMacOSRomanStringEncoding et bien d'autres.

En cherchant dans les programme Apple, TextEdit semble traiter ce cas, mais en regardant le source, je n'arrive pas à  trouver le code dans le programme  :fouf):

une idée pour résoudre ce problème !


Réponses

  • AliGatorAliGator Membre, Modérateur
    13:27 modifié #2
    Heu pour commencer, tu utilises bien [tt] stringByAddingPercentEscapesUsingEncoding:[/tt] au moins ?
    (Tu parles de "la méthode artisanale" passant par du RTF... J'ai pas tout compris ce que tu voulais dire, ce que le format RTF vient faire dans la création de ton URL à  partir de ton texte, et ce que tu appelles méthode artisanale...)
  • regattaregatta Membre
    13:27 modifié #3
    Non, pas du tout !

    J'ai utilisé Textedit dans lequel j'ai saisi un URL, puis Smultron pour regarder le codage ; enfin j'ai remplacé mon texte sélectionné par son équivalent en URL en construisanrt un NSData et en insérant mon mot par dans le NSData :
    [data appendData:[texte dataUsingEncoding:NSMacOSRomanStringEncoding]];

    Enfin [self replaceCharactersInRange:[[ranges objectAtIndex: 0] rangeValue] withRTF:data];
    remplace mon mot sélectionné par son équivalent url dans l'affichage.

    De l'artisanal quoi  ???

    Mais ta méthode me semble parfaitement répondre à  la question 

    Je teste.
    Merci
  • AliGatorAliGator Membre, Modérateur
    13:27 modifié #4
    Wooow.... de l'artisanal c'est rien de le dire, du grand cafouillage j'ai l'impression, même, si je puis me permettre :P
    Pourquoi passer par un NSData, d'où te vient cette idée ? Parce que tu voulais utiliser [tt]replaceCharactersInRange: withRTF:[/tt] et que tu avais besoin d'un NSData c'est ça ? Mais pourquoi uiliser cette méthode et donc passer par du RTF alors que ton texte d'origine n'est manifestement pas du RTF (et s'il en est, il faut absolument récupérer le texte non-RTF, le texte seul sans les balises de mise en forme RTF, quoi, car s'il y en a il ne faudrait pas qu'elles se retrouvent dans ton URL...)

    Bref, la bidouille, c'est parfois utile, mais c'est "dangereux" aussi, faut pas faire un peu n'importe quoi juste parce que ça a l'air de coller ! Là  je vois mal le but de construire ton NSData avec des appendData qui n'ont pas lieu d'être ici (si tu as besoin de construire ton texte par morceaux, fais-le avec des NSString (méthodes stringWithFormat ou appendString) avant la conversion en NSData... et encore si cette dernière s'avère nécessaire... bref, rester logique quoi :P
  • regattaregatta Membre
    13:27 modifié #5
    En fait, mon texte est bien en RTF : gras, souligné, couleur, etc...

    Il est stocké dans une base de donnée, relu ou sauvegardé si je fais des modifications.
    De là  l'utilisation de NSData !

    Par contre, je ne vois pas comment utiliser stringByAddingPercentEscapesUsingEncoding  pour résoudre mon problème !

    [texte stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [self replaceCharactersInRange:[[ranges objectAtIndex: 0] rangeValue] withString:texte];

    ne remplace pas le texte sélectionné (texte) par http://contenu_de_texte.htm  :(
  • AliGatorAliGator Membre, Modérateur
    13:27 modifié #6
    Heu... c'est normal... humhum...

    1) Comme sa signature et la doc l'indiquent, et comme le laissent sous-entendre les conventions de nommage, [tt]- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding[/tt] ne transforment pas ta chaà®ne, mais en retourne une nouvelle.

    Donc si "texte" est ton texte que tu veux échapper avec des %xx pour l'utiliser ensuite pour construire ton URL, il faut récupérer le résultat de la méthode ci-dessous, appliquée à  la variable "texte", dans une autre variable !

    2) C'est qui ce "self" ? Dans quoi mets-tu la méthode ? C'est pas plutôt à  la chaà®ne contenue dans ton NSTextField ou ta base ou autre plutôt que tu veux appliquer [tt]replaceCharactersnRange:withString:[/tt] ? En effet, à  ma connaissance, cette méthode est une méthode de NSMutableString... donc est-ce voulu que tu l'appliques à  "self", dont je doute qu'il soit une NSMutableString ? Ou alors tu as créé une méthode ayant la même signature dans la classe dans laquelle tu mets ce code, pour faciliter les choses, qui se charger de récupérer le texte, sous forme de MutableString, la modifier, et remettre le nouveau texte ?

    Enfin un peu plus de détails aideraient à  comprendre si c'est voulu ou une erreur de ta part, en fait à  quel endroit mets-tu ce code (qu'on sache à  quoi self fait référence) ?

    Sinon pour le coup du RTF, tu utilises les classes faites pour, de manière à  convertir proprement tes données RTF en NSData et vice-versa ? J'ai l'impression que tu te contentes de transformer ton NSString en NSData sans autre forme de procès ? Tu passes au moins par NSAttributedString ou qqch du genre ? Un peu plus de détails là  aussi pour vérifier que tu ne fais pas fausse route serait utile.
  • regattaregatta Membre
    13:27 modifié #7
    Mon code se trouve dans une classe dérivée de NSTextView.

    Après sélection du mot à  transformer en URL, et un clic droit sur le menu contextuel, je récupère le contenu de la sélection, tente de transformer le texte sélectionné en une adresse url, qui remplace le mot sélectionné et je sauvegarde le nouveau contenu du texte.

    Après, lorsque je clic sur l'url, j'utilise la méthode de NSTextView - (void)clickedOnLink:(id)link atIndex:(NSUInteger)charIndex pour lire le mot clé et afficher le texte correspondant au mot clé.

    Voici l'écran avant ma méthode artisanale
    Après la transformation
    Après le clic sur le lien créé

    (si les images s'affichent correctement !)


  • regattaregatta Membre
    octobre 2008 modifié #8
    Pour ce qui suit, je ne comprend pas ce que tu veux dire :

    Donc si "texte" est ton texte que tu veux échapper avec des %xx pour l'utiliser ensuite pour construire ton URL, il faut récupérer le résultat de la méthode ci-dessous, appliquée à  la variable "texte", dans une autre variable !


    Je souhaite remplacer "Wollastonite" par "http://Wollastonite"; pour que NSTextView le considère comme un URL et que son delegate puisse utiliser "clickedOnLink" pour afficher le texte de la base correspondant au minéral Wollastonite".

    Cela fonctionne correctement avec mon horreur de code à  part si un accent est dans le mot sélectionné !

    Si on prend le mot Trémolite, Textedit sauvegarde le lien sous la forme
    {\field{\*\fldinst{HYPERLINK "http://Trémolite"}}{\fldrslt
    \f0\fs24 \cf0 Tr\'e9molite}}}
    Le codage dans le lien est différent du codage du mot !

    Ajout : j'avais pas vu la boulette   :-\\
    NSString * texte2 = [texte stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  • AliGatorAliGator Membre, Modérateur
    octobre 2008 modifié #9
    Bon c'est bien ce qu'il me semblait, tu as l'air de vouloir persister dans les méthodes artisanales...
    Il faut que tu évites, autant que faire se peut*, d'utiliser des méthodes artisanales alors que Cocoa fournit tout ce qu'il faut... En effet il est préférable d'utiliser les méthodes qui te sont mises à  disposition car en général quand c'est Apple qui les a codées, ils pensent alors à  gérer tous les cas de figure et toutes les petites subtilités...

    Dans ton cas donc, c'est une très mauvaise idée de chercher à  créer les données RTF par toi même. Tu as en effet déjà  pu remarquer qu'il faut que tu penses à  gérer le cas des accents... et il y a sans doute d'autres subtilités. De plus, si tu veux pouvoir sélectionner un bout de texte mis en forme dans ton texte, dont une partie est en gras et l'autre pas par exemple, et mettre ceci en lien... si tu fais le code de génération des données RTF toi-même, ça commence à  faire un sacré paquet de cas à  se préoccuper...

    Alors pourquoi se prendre la tête à  réinventer la roue quand Cocoa prévoit tout ce qu'il faut pour te faciliter la vie ?
    En particulier, NSAttributedString et ses Additions te donnent tout ce qu'il faut pour créer une NSAttributedString qui intègre les liens, de façon propre.
    Comme tu peux facilement récupérer la NSAttributedString sélectionnée lrs de ton clic droit (et donc avoir ainsi une chaà®ne qui garde les informations de formattage, tant qu'à  faire autant éviter de s'en priver), il suffit de rajouter à  cette NSAttributedString l'attribut [tt]NSLinkAttributeName[/tt] avec le lien que tu veux.

    Donc les étapes à  réaliser :
    • 1) Récupérer la NSAttributedString correspondant au texte sélectionné (selAttrText)
    • 2) Récupérer le texte brut (sans attributs) de ce texte (selPlainText)
    • 3) Utiliser ce texte brut pour générer ton lien (url)
    • 4) Ajouter à  ton NSAttributedString (selAttrText) l'attribut de type NSLinkAttributeName avec comme valeur de cet attribut le lien généré
    • 5) Remplacer enfin le texte sélectionné dans ta NSTextView par le résultat obtenu


    En faisant ainsi, tu n'as ainsi pas à  descendre bas niveau (et c'est là  ton erreur à  mon sens) ni à  comprendre comment fonctionne le RTF : on se fiche de savoir comment le RTF se démerde pour coder les liens et tout ce que tu veux, on laisse Cocoa se débrouiller avec ce qu'on lui demande, c'est à  dire juste ce que tu veux sans chercher plus loin, c'est à  dire ajouter un attribut de lien au texte sélectionné. Faut pas chercher plus loin et manipuler le RTF toi-même, tu risques plus de fouttre le boxon dans ton texte et/ou oublier des cas qu'autre chose !!

    Note de plus qu'en faisant ainsi, l'étape 3 peut se contenter de convertir ton texte avec [tt]stringByAddingPercentEscapesUsingEncoding[/tt] : je ne suis même pas sûr que tu aies besoin d'ajouter le "http://"; devant (que tu ajoutais pour qu'il réalise que c'est un lien, si j'ai tout suivi), puisque tu demandes explicitement à  ton NSAttributedString d'ajouter l'attribut comme étant de type lien ! (D'ailleurs à  se demander si même le percentEscape est utile). Du moment que c'est toi ensuite qui intercepte l'évènement de clic sur un lien dans ta NSTextView, et donc intercepter le résultat pour en récupérer le mot clé cliqué...


    ----

    En suivant cette procédure haut niveau, non seulement tu n'as pas à  te demander comment le format RTF est foutu sous le capot, tu n'as pas à  te préoccuper des petites subtilités et cas auxquels tu aurais pu ne pas penser... et en plus si le format sous-jacent change ou est géré autrement, tu t'en fiches : tu laisses Cocoa tout faire, tu te contentes de lui dire exactement ce que tu veux (rajouter un lien à  ton mot) et pas bidouiller par toi-même pour y arriver...
    Et ça c'est un conseil général, évite de descendre trop bas niveau quand ce n'est pas nécessaire, vérifie avant que Cocoa ne te permet pas de faire directement ce que tu veux ;)


    *j'adore cette expression :)
  • AliGatorAliGator Membre, Modérateur
    octobre 2008 modifié #10
    Allez, j'ai fait le test dans un petit projet pour l'occasion, et voilà  comment j'ai codé ça (en plus c'est pas long y'a presque plus de commentaires que de code actif :P) :
    - (void)makeSelectionAsLink<br />{<br />	// récupérer le texte sélectionné (sous forme &quot;formattée&quot; tant qu&#39;à  faire)<br />	NSAttributedString* selAttrText = [textView attributedSubstringFromRange:[textView selectedRange]];<br /><br />	// encoder le mot à  définir (et sans ses attributs de formattage cette fois), pour pouvoir créer une URL valide (RFC 2396)<br />	NSString* encodedWordQ = [[selAttrText string] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];<br />	// traiter le cas particulier du &quot;?&quot; sinon ce qui est derrière le premier &quot;?&quot; sera vu comme queryString (parameterString)<br />	NSString* encodedWord = [encodedWordQ stringByReplacingOccurrencesOfString:@&quot;?&quot; withString:@&quot;%3F&quot;];<br />	// On peut ainsi créer une URL avec le mot à  définir dedans (encodé pour l&#39;URL juste avant donc)<br />	NSURL* linkURL = [NSURL URLWithString:[NSString stringWithFormat:@&quot;def://com.definitions.main/%@&quot;,encodedWord]]; <br />	<br />	// Ajouter l&#39;attribut NSLinkAttribute avec cette URL à  la sélection<br />	NSMutableAttributedString* newAttrText = [selAttrText mutableCopy];<br />	[newAttrText addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0,[selAttrText length])];<br />	// pour remplacer le texte en effet faut passer par le RTF car il n&#39;existe pas de méthode replaceCharactersInRange:withAttributedString:<br />	// mais bon on laisse quand même Cocoa faire la conversion du AttributedString en RTF donc ça reste haut niveau...<br />	NSData* rtfData = [newAttrText RTFFromRange:NSMakeRange(0,[newAttrText length]) documentAttributes:nil];<br />	[textView replaceCharactersInRange:[textView selectedRange] withRTF:rtfData];<br />	[newAttrText release];<br />}<br /><br /><br /><br />// Implémentation de la méthode de delegate quand on clique sur le mot/lien à  définir<br />- (BOOL)textView:(NSTextView *)aTextView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex<br />{<br />	if (![[link scheme] isEqualToString:@&quot;def&quot;]) return NO; // si c&#39;est pas une URL de type &quot;def://&quot;, laisser la gestion normale des URLs faire son boulot<br /><br />	// (1) dans host j&#39;ai mis ici par exemple &quot;com.definitions.main&quot; (cf plus haut)<br />	// mais ça peut te permettre de spécifier une catégorie pour classer tes mots à  définir par exemple...<br />	NSString* defSource = [link host]; // à  toi de voir si ça t&#39;est utile<br /><br />	// (2) dans wordToDef on récupère le mot à  définir qu&#39;on a cliqué<br />	NSString* wordToDef = [[link path] substringFromIndex:1]; // supprimer le premier caractère qui se trouve être le &quot;/&quot; entre le &quot;host&quot; et le &quot;path&quot; de l&#39;URL<br /><br /><br />	NSLog(@&quot;We want to find in %@ the definition of the word %@&quot;,defSource,wordToDef);<br />	<br />	return YES; // on a intercepté le clic, donc ne pas laisser la gestion par défaut faire son boulot<br />}
    
    Note que :
    • J'ai utilisé un scheme "def://" dans l'URL comme ça c'est facile d'identifier que le lien est un lien "à  toi" customisé et pour lequel tu dois le gérer comme une définition --> En plus comme ça tu laisses les URLs "normales" de type "http://..."; fonctionner !
    • J'ai mis le mot à  définir dans la partie "path" de l'URL et non dans la partie host, car la RFC définissant les URL valides n'autorise pas tous les caractères pour le host, et je crois que c'est beaucoup moins permissif que pour le path
    • J'en ai profité pour utiliser la partie "host" (partie entre le "def://" et le premier "/") pour autre chose, ça peut te permettre par exemple de trier tes mots par catégorie, ou encore si tu as plusieurs dictionnaires, pour plusieurs langues ou je ne sais quoi, de dire dans lequel aller chercher la définition... enfin bon ça te laisse d'autres possibilités
    • De par la subtilité que tu passes par une URL, cela est limité par les specs des URL. En particulier la longueur de l'URL est limité (bon, à  256 caractères je crois de mémoire, t'as le temps d'y arriver, mais bon c'est à  savoir que cette limitation existe). Déjà  tu vois que j'ai traité le cas particulier du caractère "?" qui est un caractère spécial dans les URLs... donc bon je pense que ça traite tous les cas mais bon faut faire gaffe
  • regattaregatta Membre
    octobre 2008 modifié #11
    Merci pour ton aide.

    Si j'ai utilisé une méthode artisanale, c'est que je n'ai pas trouvé d'autre solution  ;)
    D'où ma question dans le forum !

    Je développe dans un tout autre domaine et je ne réinvente pas la roue si j'ai une solution standard, et maintenant j'ai cette solution.

    Ajout : cela marche nickel (après passage en SDK 10.5). Effectivement j'avais vu que les attributs n'étaient pas repris, mais je voulais traiter un problème avant l'autre ; maintenant tous les problèmes sont réglés pour le lien !

    Merci et A+
    Eric

Connectez-vous ou Inscrivez-vous pour répondre.