Accueil > Développement > Applications de Gestion de Documents : Pratique

Applications de Gestion de Documents : Pratique

Par Apple Computer, Inc. © 2002,

Traduit par Pascal Gouhier, 20/06/2003

Sommaire

  1. Créer une sous-classe de NSDocument
  2. Implémenter une application basée sur des documents
  3. Foire aux questions
  4. Créer une sous-classe de NSDocumentController
  5. Sauvegarder les codes HFS de type et de créateur

Créer une sous-classe de NSDocument

Toute application qui utilise l’architecture des applications basées sur des documents de l’Application Kit doit créer une sous-classe de NSDocument. Cette architecture nécessite que vous réécriviez quelques méthodes de NSDocument au choix, et recommande d’en réécrire d’autres dans certains cas.

  • Primitives concernant les donnéesLa méthode dataRepresentationOfType: doit être implémentée pour créer et présenter les données (emballées dans un objet NSData) d’un document d’un type supporté, habituellement en vue de l’écriture de ces données dans un fichier. la méthode loadDataRepresentation:ofType: doit être implémentée pour convertir un objet NSData contenant les données d’un document d’un certain type vers les structures de données internes et pour afficher ces données dans une fenêtre; l’objet NSData est en général le résultat de la lecture d’un fichier. Les sous-classes doivent réécrire ces méthodes.
  • Primitives concernant l’emplacementPar défaut, la méthode writeToFile:ofType: écrit les données dans un fichier après avoir obtenu les données de fileWrapperRepresentationOfType:, qui les reçoit de la méthode dataRepresentationOfType:. La méthode readFromFile:ofType: lit les données d’un fichier, crée un objet NSFileWrapper à partir de lui, et envoie cet objet à loadFileWrapperRepresentation:ofType:; Si cet objet représente un unique fichier, il est passé à la méthode loadDataRepresentation:ofType: pour traitement; sinon (c’est à dire s’il représente un répertoire), la méthode loadFileWrapperRepresentation:ofType: est réécrite pour gérer la situation. Les sous-classes peuvent réécrire n’importe laquelle de ces méthodes au lieu des primitives concernant les données si la façon dont NSDocument lit et écrit les données ne suffit pas; leurs implémentations doit cependant aussi gérer les fonctions de chargement des primitives concernant les données.
  • Création de contrôleurs de fenêtresLes sous-classes de NSDocument doivent aussi créer leur contrôleur de fenêtre. Elles peuvent le faire directement ou indirectement. Si un document n’a qu’un fichier nid (avec une fenêtre dedans), la sous-classe peut réécrire windowNibName: pour retourner le nom de la fenêtre du fichier nib; en conséquence de quoi, une instance par défaut de NSWindowController est créée pour le document, avec le document en tant que propriétaire du fichier nib (File’s Owner). Si un document a plusieurs fenêtres, ou si une instance d’une sous-classe personnalisée de NSWindowController est à utiliser, la sous-classe de NSDocument doit réécrire makeWindowControllers: pour créer ces objets.
  • Impression et mise-en-pageNormalement, une application basée sur les documents peut changer les information qu’elle utilise pour définir comment les données du document sont imprimées (un objet NSPrintInfo). Les sous-classes doivent réécrire shouldChangePrintInfo: pour interdire ce changement. Si une application est faite pour imprimer les données d’un document, les sous-classes de NSDocument doivent réécrire printShowingPrintPanel:.
  • Fichiers de sauvegardeQuand il sauve un document, NSDocument cré une copie de sauvegarde de l’ancien fichier avant d’écrire les données dans le nouveau (le fichier de sauvegarde a le même nom, mais avec un tilde juste avant l’extension). Normallement, si l’opération d’écriture se passe bien, il détruit le fichier de sauvegarde. Les sous-classes peuvent réécrire keepBackupFile pour qu’il retourne YES, et ainsi garder le plus récent fichier de sauvegarde.
  • Panneau de sauvegarde avec la vue accessoirePar défaut, quand NSDocument lance le panneau de sauvegarde, et si le document peut être écrit dans de nombreux formats, il insère une vue accessoire vers le bas du panneau. Cette vue contient une liste pop-up des formats. Si vous ne voulez pas de cette vue, réécrivez shouldRunSavePanelWithAccessoryView pour retourner NO.
  • Eléments de menuNSDocument implemente validateMenuItem: pour gérer l’état d’activation des éléments de menu “Revenir à la version enregistrée” (Reverts) et “Enregistrer sous…” (Save As). Si vous voulez valider d’autres éléments de menu, vous pouvez réécrire cette methode, mais soyez certains d’invoquer l’implémentation de super. Pour plus d’information sur les éléments de menu et leur validation, lisez la description du protocole informel NSMenuValidation.

Les initialisateurs de NSDocument sont un autre soucis des créateurs de sous-classes. La méthode init est l’initialisateur primaire et est invoqué par l’autre initialisateur initWithContentsOfFile:ofType:. La méthode init est invoquée directement quand un nouveau document est créé; la méthode initWithContentsOfFile:ofType: est invoquée directement quand un document est ouvert. Cependant, si vous n’avez aucun initialisateur qui ne s’applique qu’aux documents qui sont ouverts, vous pouvez réécrire initWithContentsOfFile:ofType:; si vous avez des initialisations générales, vous devez bien évidemment réécrire init. Dans les deux cas, soyez sûre d’invoquer l’implémentation de super en premier lieu.

Implémenter une application basée sur des documents

Il est possible de mettre en place une application basée sur des documents sans avoir à écrire beaucoup de code. Si vos exigences sont minimales, l’Application Kit vous fournit une instance par défaut de NSWindowController et une instance par défaut de NSDocumentController. Vous n’avez qu’à créer un projet, composer l’interface utilisateur, implémenter une sous-classe de NSDocument et ajouter toute autre classe ou fonction personalisées dont vous avez besoin pour votre application.

Les sections suivantes vous guident à travers les tâches successives que vous devez accomplir, et celles que vous pourriez vouloir accomplir, quand vous implémentez une application basée sur des documents. Quand quelquechose est décrit en détail ailleurs, comme par exemple la réécriture des méthodes des sous-classes de NSDocument, vous y êtes renvoyés.

Mis à part le nom des trois classes qui forment les fondations des applications basées sur des documants, deux choses sont aussi fondamentales, à savoir le nombre d’objet requis et la nécessité ou non de les sous-classer. Le tableau suivant résume cette information :

Classe Combien d’objets ? Sous-classe ?
NSDocumentController 1 par application Optionelle (mais non souhaitable)
NSDocument 1 par fichier sauvegardé Requis
NSWindowController 1 par fichier nib de document Optionelle (mais souhaitable)

Le modèle de projet “Document-Based Application” (Application basée sur des documents)

L’environnement de développement Cocoa fourni un modèle de projet nommé “Cocoa Document-Based Application” (dans Project Builder) pour faciliter le développement d’applications basées sur des documents. Ce modèle fournis les éléments suivants :

  • Un fichier nib pour le document de l’applicationUne sous-classe de NSDocument nommée “Document” est désignée “File’s Owner” du fichier nib. Elle a une sortie vers sa fenêtre. La fenêtre est vide de tout objet.
  • le fichier nib principal de l’applicationCe fichier nib contient un menu avec un menu File (avec toutes les commandes associées aux documents) et les éléments Undo et Redo dans le menu Edit. Ces éléments de menu, ainsi que tous les éléments du menu File, sont connectés aux méthodes d’action “first-reponder” appropriées. L’élément de menu About MyApp est connecté à la méthode d’action chargeant le fichier nib contenant le panneau About.
  • Un fichier nib pour le panneau “A propos de ” (About panel)Ce fichier nib contient un message conventionnel avec de la place pour du texte pour le nom de l’application, la version, etc…. L’objet NSWindowController personnalisé (voyez plus loin) est le “File’s Owner” de ce fichier nib.
  • Un squelette d’implémentation de sous-classe de NSDocumentLe projet inclu un Document.h et un Document.m, qui dérivent de la définition de la sous-classe de NSDocument dans le fichier nib. Le dernier fichier inclu des bloques vides mais commentés pour les méthodes dataRepresentationOfType:, loadDataRepresentation:ofType:, et windowControllerDidLoadNib:. Il contient aussi une version pleinement fonctionnelle de la méthode windowNibName.
  • Une sous-classe personnalisée de NSWindowController pour le panneau “A propos de”
  • Une liste d’information de propriétésDans l’éditeur Target, vous pouvez modifier le fichier Info.plist, lequel contient les valeurs des clefs globales de l’application, ainsi que la clef CFBundleDocumentTypes (qui spécifie les informations concernant les documents avec lesquels l’application fonctionne).

Les étapes décrites dans les sections suivantes montrent comment créer une application basée sur des documents en utilisant le modèle de projet de Project Builder. Si ce modèle n’est pas disponible sur votre système, vous devrez compléter les travaux listés ci-après par vous même. Si c’est le cas, le tableau suivant liste les connections aux action du first-responder appropriées (c’est à dire, dans Interface Builder, connecter la commande de menu à l’icone First Responder, celle qui ressemble à un grand “1″, dans la fenêtre du fichier nib) :

Commandes du menu Fichier Action du First-Responder
New (Nouveau document) newDocument:
Open (Ouvrir …) openDocument:
Save (Enregistrer) saveDocument:
Save As (Enregistrer sous …) saveAsDocument:
Save To (Exporter …) saveToDocument:
Save All (Enregistrer tous) saveAllDocuments:
Close (Fermer) closeDocument:
Revert (Revenir au document enregistrer) revertDocument:
Print (Imprimer) printDocument:
Page Layout (Mise en page) runPageLayout:

De même, si vous avez ajouté les éléments Undo et Redo dans le menu Edit, connectez les aux méthodes undo: et redo: du first-responder.

Créer le projet et composer l’interface

  1. Lancez Project Builder et choisissez New Project du menu File.Dans le dialogue de New Project, choisissez “Cocoa Document Based Application”. Si ce modèle n’est pas disponible, vous pouvez dupliquer celui situé dans  /Developer/ProjectBuilder Extras/Project Templates/Application/Cocoa Document-based Application.
  2. Donnez un nom et une place pour le projet.
  3. Double-cliquez le fichier Document.nib dans le groupe Resources dans l’onglet Group & Files de Project Builder. Ceci ouvrira le fichier dans Interface Builder.Si vous voulez changer le nom du fichier nib, vous pouvez le sauvegarder sous un autre nom dans Interface Builder et l’ajouter au projet. Si vous le faites, vous devez aussi modifier le texte retourné par windowNibName dans l’implémentation de la sous-classe NSDocument.
  4. Créez l’interface visible de la fenêtre de document dans Interface Builder.
  5. Si les objets de la fenêtre de document nécessitent d’autres outlets ou actions, ajoutez les à la sous-classe MyDocument de NSDocument. Connectez ces actions et outlets via l’icone File’s Owner aux instances affichées du fichier nib.

    Important : Ne pas générer une instance de MyDocument pour faire ces connections.

    Si vous voulez nommer votre sous-classe de NSDocument autrement que “MyDocument” (le nom par défaut), changez le nom dans Interface Builder et à chaque endroit où il apparait dans les fichiers header (.h) et implémentation (.m).

  6. Si vos objets documents interagissent avec d’autres objets personnalisés, comme des objets modèles qui accomplissent des calculs spécialisés, définissez ces objets dans Interface Builder et faites toutes les connections nécessaires avec eux.

Compléter la liste des informations de propriété ( Information Property List)

  1. Dans Project Builder, cliquez l’onglet Targets et sélectionnez “application target” dans cet onglet.
  2. Remplacez les identifiants ou les valeurs par défaut de la liste de propriétés (Info.plist) par ceux spécifiques à votre application. Vous pouvez modifier ces informations soit dans Simple View (où vous n’avez qu’à taper les valeurs dans les champs) soit dans Expert View (où vous modifiez directement les entrées de propriétés).Voyez “Stockage des informations sur les types de document dans un fichier .plist” pour des informations sur les paires clé/valeur spécifiques aux documents (et pour des illustrations de l’éditeur Target de Project Builder).
  3. Pour les propriétés globales de l’application, entrez le nom de votre application, sa version et les information de copyright.

Implementer la sous-classe de NSDocument

La procédure suivante ne présente que des orientations générales. Pour des détails voyez la spécification complète de NSDocument, ainsi que “Création d’une sous-classe de NSDocument”. Vous devriez aussi lire les exposés de programmation concernant undo (annuler), copy/paste (copier/coller), et printing (imprimer).

  1. Dans Project Builder, ouvrir le fichier header de la sous-classe de NSDocument (dans le groupe Classes de l’onglet Groups & Files de Project Builder).
  2. Si vous ajoutez des outlets ou des actions à votre sous-classe de NSDocument dans Interface Builder, ajoutez les au fichier header de la sous-classe. Ajoutez aussi toutes les variables d’instances requises et incluez les déclarations des nouvelles méthodes que vous souhaitez rendre publiques, comme les méthodes d’accès (accessor).Bien sûr, vous pouvez spécifier des outlets et actions suplémentaires dans le fichier header existant et les importer ensuite dans le fichier nib en utilisant Interface Builder (Commande Classes>Read File).
  3. Ouvrez le fichier d’implémentation de la sous-classe (.m) dans le group Class de Project Builder.
  4. Bien que cela ne soit pas habituellement nécessaire, vous pouvez réécrire l’initialisateur primaire (init) et peut-être l’initialisateur d’ouverture de document initWithContentsOfFile:ofType: pour accomplir des initialisations spécifiques à votre sous-classe; assurez vous d’invoquer les implémentation de super. Vous pouvez aussi implémenter awakeFromNib pour initialiser les objets désarchivés depuis les fichiers nib.
  5. Réécrire les primitives basées sur les données (aucune implémentation de ces méthodes n’est fournie dans le modèle de projet).Dans presque tous les cas, vous devez implémenter dataRepresentationOfType: (pour gérer les données de documents d’un certain type) et  loadDataRepresentation:ofType: (pour charger les données de document d’un certain type).Un exemple en Objective-C :
    //------------------------------------------------------------------
    
    - (NSData *)dataRepresentationOfType:(NSString *)aType {
        NSAssert([aType isEqualToString:@"rtf"], @"Unknown type");
        return [textView RTFFromRange:NSMakeRange(0, [[textView textStorage] length])];
    }
    
    // --------------------------------------------------------------------
    
    - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType {
        NSAssert([aType isEqualToString:@"rtf"], @"Unknown type");
        fileContents = [data copyWithZone:[self zone]];
        return YES;
    }
  6. Si vous avez besoin de lire ou écrire les données d’une manière spéciale (car, par exemple, les données sont stockées dans un paquet de fichier), vous pouver réécrire readFromFile:ofType: et writeToFile:ofType: pour ne pas seulement lire et écrire les données, mais les charger et fournir des données dans un type donné.Un exemple en Objective-C :
    // ----------------------------------------------------------------------
    
    - (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)type {
        return [[textView string] writeToFile:fileName atomically:YES];
    }
    
    // ----------------------------------------------------------------------
    
    - (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)type {
        fileContents = [[NSString alloc] initWithContentsOfFile:fileName];
        return fileContents != nil;
    }
  7. Créer les contrôleurs de fenêtre des objets NSDocument.Si votre document n’a qu’une fenêtre, le modèle de projet fourni une implémentation par défaut :
    - (NSString *)windowNibName {
        return @"Document";
    }

    Si votre document a plus d’une fenêtre, ou si vous avez une sous-classe personnalisée de NSWindowController, réécrivez makeWindowControllers. Assurez vous d’avoir ajouter chaque contrôleur de fenêtre créé à la liste de ces objets gérés par le document (addWindowController:).

  8. Vous pouvez implémenter windowControllerWillLoadNib: et windowControllerDidLoadNib: pour exécuter toutes les tâches nécessaires relatives aux fenêtres avant et après qu’elles soient chargé depuis le fichier nib.Un exemple en Objective-C :
    - (void)windowControllerDidLoadWindowNib:(NSWindowController *)windowController {
        [super windowControllerDidLoadWindowNib:windowController];
        [textView setAllowsUndo:YES];
        if (fileContents != nil) {
            [textView setString:fileContents];
            [fileContents release];// Don't need it anymore
            fileContents = nil;
        }
    }
  9. Marquer le drapeau “dirty” du document s’il est modifié.Le drapeau retourné par isDocumentEdited indique si le document contient des changement non sauvegardé. Bien que NSDocument efface ce drapeau quand il sauvegarde ou revient au document sauvegardé, vous devez initialiser ce drapeau dans votre code, à moins que vous utilisiez le mécanisme annuler/rétablir par défaut de NSDocument. Normalement, vous répondez aux messages de délégation ou notification appropriés envoyés quand les utilisateurs modifient un document, et ensuite invoquez updateChangeCount: avec un argument NSChangeDone pour initialiser le drapeau “dirty”.
  10. Ecrire le code qui imprime les données du document.Si vous voulez que les utilisateurs soient capables d’imprimer un document, vous devez réécrire printShowingPrintPanel:, peut-être en fournissant un objet NSPrintInfo modifié.
  11. Enregistrer les groupes annuler et refaire dans votre code. Voyez la description de la classe NSUndoManager pour plus de détails.

Et bien sûr, vous implémentez toutes les méthodes qui sont spécifiques à votre sous-classe NSDocument.

Implementer des classes de contrôleurs supplémentaires

Si l’instance par défaut de NSWindowController fournie par l’Application Kit ne correspond pas aux besoin de votre application, vous pouvez créer une sous-classe personnalisée. Si vous le faites, vous devez réécrire La méthode makeWindowControllers de NSDocument pour créer des instances de cette classe personnalisée et ajouter l’objet créé à la liste de contrôleurs de fenêtre du document.

Si l’objet NSDocumentController par défaut ne répond pas à toutes vos attentes pour un contrôleur d’application, comme manipuler les préférences utilisateurs ou répondre à des messages de délégués d’application peu usuels, vous devez créer un objet contrôleur séparé (au lieu de sous-classer NSDocument Controller). Pour des informations sur l’implémentation de NSDocumentController et des sous-classes de NSWindowController, référez-vous aux spécifications de ces classes.

Foire Aux Questions

Ce document répond aux questions communément posées au sujet des classes permettant la manipulation de documents dans l’Application Kit. Cela comprend la classe NSDocument de même que NSDocumentController et NSWindowController.

Les fondations

Par où commencer?

Pour commencer l’écriture d’une application basée sur NSDocument, utilisez le modèle de projet Cocoa Document-based Application à la création d’un nouveau projet avec Project Builder.Quand vous faites cela, vous obtenez un nouveau projet d’application qui contient déjà une sous-classe de NSDocument et un fichier  nid pour un document.

La sous-classe NSDocument est pré-configurée pour charger le fichier nib. Des méthodes vides sont fournies pour ouvrir et sauvegarder, à vous de les compléter, et une méthode est fournie pour vous permettre d’ajouter le code qui sera exécuté au chargement du fichier nib.

Sans écrire une seule ligne de code, vous devriez pouvoir compiler et exécuter l’application. Vous verrez un document sans nom avec une fenêtre vide créée quand vous lancez l’application et les commandes du menu File feront toutes quelque chose, comme faire apparaitre un paneau de sauvegarde ou d’ouverture. Comme vous n’avez encore défini aucun type ni implémenté l’ouverture et la sauvegarde de fichiers, vous ne réussirez pas à ouvrir ou sauvegarder quoi que ce soit.

A partir de là, vous devez commencer par définir un type de document (voyez  “Comment définir les types?”) et par implémenter les méthodes d’ouverture et de sauvegarde (voyez “Comment implémenter la sauvegarde et l’ouverture de fichiers simples?”).

Au delà de la définition des types et de l’implémentation de l’ouverture et de la sauvegarde, vous aurez certainement besoin d’autres fonctionnalités dans votre sous-classe de NSDocument. La sous-classe devrait contenir et posséder le contenu du document. Cela veut dire que votre sous-classe de NSDocument doit fournir des méthodes pour conserver et gérer le contenu du document (c’est à dire les objets modèle du document).

Pour plus d’informations sur l’architecture document, voyez Application Architecture (traduit : Architecture des applications) et les différentes sections “concepts” de Document-Based Applications (traduit : Application de Gestion de Documents). Pour plus d’informations sur la céation de sous-classes de NSDocument, se reporter à “Créer une sous-classe de NSDocument”. Pour un exemple d’application qui suit les suggestions de conception des applications, voyez Sketch dans le dossier  /Developer/Examples/AppKit.

Comment définir les types ?

Les types sont définis dans un fichier nommé Info.plist, qui est gérer pour vous par Project Builder. Pour des détails sur les listes d’information de propriétés, voyez “Stockage des informations sur les types de documents dans un fichier .plist”, ainsi que le chapitre “Software Configuration” et l’appendice “Information Property List Keys” dans Inside Mac OS X: System Overview.

Vous définissez les types de votre application dans l’éditeur “Target” dans Project Builder. l’éditeur “Target” fourni une liste modifiable des types de document, montrée dans “Stockage des informations sur les types de documents dans un fichier .plist”. Voyez l’aide de Project Builder, section “Setting Document Types and Icons” pour plus de détails.

Pour une nouvelle application, vous devez créer un type avec un nom et une extension qui ont un sens pour votre application. Vous pouvez ajouter aussi plus de types. Voyez la section “Types, Chargement, et Sauvegarde” pour plus de détails.

Le principal ou plus important type de fichier de votre application doit être placé en tête de la liste de types. Ce sera le type que NSDocumentController utilisera par défaut quand l’utilisateur créera un nouveau document.

Comment implémenter la sauvegarde et l’ouverture de fichiers simples ?

Un nouveau projet d’application de gestion de documents contient déjà des methodes vides pour implémenter dataRepresentationOfType: et loadDataRepresentation:ofType: dans la sous-classe personnalisée qui est créée pour vous. Vous devez implémenter ces méthodes pour gérer la lecture et l’écriture de fichiers simples.

dataRepresentationOfType: fournit le contenu d’un document sous forme d’un objet NSData, formatté dans le type demandé.

loadDataRepresentation:ofType: est capable de lire le contenu du document depuis un NSData donné, en interprétant les données dans le type donné.

Etudiez l’application Sketch pour avoir un exemple d’implémentation de ces méthodes.

Si votre document sauvegarde les documents commes des fichiers empaquetés ou a des besoins plus sophistiqués, voyez “Comment implémenter les documents-paquets (documents qui sont en réalité des dossiers, mais qui apparaissent comme des documents opaques) ?” et/ou “Comment implémenter le chargement et la sauvegarde quand les API de données simples (NSData) ou d’empaquetage des données (NSFileWrapper) ne fonctionnent pas ?” pour plus d’informations.

Dois-je sous-classer NSWindowController ?

Le modèle par défaut de projet d’application de gestion de documents ne sous-classe pas NSWindowController. Vous n’avez pas à sous-classer NSWindowController si vous écrivez une application simple. Cependant, si vous écrivez une application avec des fonctionnalités avancées, vous voudrez sûrement le faire. Sous-classer NSWindowController est valable dans tous les cas, sauf dans les cas les plus simples. Voici quelques situations courantes qui peuvent rendre nécessaire la création d’une sous-classe :

  • Vous avez besoin de plusieurs fenêtre pour afficher votre document. Si vous écrivez une application dont les documents sont assez complexes pour nécessiter plusieurs fenêtres (comme les programmes de DAO qui veulent présenter des vues de face/du dessus/de côté et aussi des vues en 3D), alors vous devrez probablement avoir une ou plusieurs sous-classes de NSWindowController pour gérer les différents types de fenêtres dont votre document a besoin.
  • Vous voulez plusieurs vues du même document. Si vous écrivez une application dans laquelle vous voulez que l’utilisateur puisse créer plusieurs vues d’un document (comme dans un programme de dessin dans lequel l’utilisateur peut ouvrir plusieurs vues de son document afin de voir différentes parties du même document ou voir les mêmes parties mais avec des réglages différents), alors vous devez sous-classer NSWindowController.
  • Si la couche contrôleur de votre application est assez complexe pour rendre utile sa séparation en un contrôleur principal (votre sous-classe NSDocument) et un contrôleur d’entrée (une sous-classe de NSWindowController). Notamment pour les grosses applications, séparer les fonctions du contrôleur entre deux classes est très sensé et vous permet d’avoir des documents ouverts, mais non affichés pour éviter d’avoir à allouer de la mémoire et d’autres ressources d’une interface qui n’est pas utilisée dans certains cas.

Pour plus d’informations sur les rôles de NSDocument et NSWindowController dans l’architecture document, voyez “Les rôles des objets clés dans une application basée sur les documents”.

Comment sous-classer NSWindowController ?

Une fois que vous avez décidé de sous-classer NSWindowController, vous avez besoin de faire quelques changements dans le paramètrage par défaut d’une application de gestion de documents. Premièrement, tous les outlets et actions de Interface Builder dans votre interface de document doivent être ajoutés à la sous-classe deNSWindowController au lieu de la sous-classe de NSDocument. Ceci parce que maintenant, c’est la sous-classe de NSWindowController qui sera propriétaire du fichier nib (File’s Owner). Quelques actions de menu peuvent encore être implémentées par la sous-classe de NSDocument. Par exemple, les commandes d’enregistrement et de retour à l’original sont implémentées par NSDocument, et vous pouvez ajouter d’autres actions de menu de votre cru comme une action permettant de créer une nouvelle vue d’un document.

Deuxièmement, au lieu de surcharger windowNibName dans votre sous-classe de NSDocument, surchargez makeWindowControllers. Dans makeWindowControllers vous devez créer au moins une instance de votre sous-classe personnalisée de NSWindowController et utiliser addWindowController: pour l’ajouter au document. Si votre document a besoin en permanencede plusieurs contrôleurs, créez les tous ici. Si votre document devra gérer plusieurs vues, mais n’en a qu’une par défaut, créez le contrôleur de chaque vue ici et fournissez aux utilisateurs les actions leur permettant de les créer.

Vous ne devez pas forcer les fenêtres à être visible dans makeWindowControllers. NSDocument le fera pour vous si cela est nécessaire.

Voyez la section “NSWindowControllers” pour plus d’informations.

Comment faire des opérations ou préparations supplémentaires sur l’interface avant ou après le chargement de mon fichier nib ?

Dans beaucoup d’applications, vous avez besoin d’accomplir des opérations de paramètrage juste avant ou juste après le chargement de l’interface utilisateur du document.

Si vous ne sous-classez pas NSWindowController alors vous devez surcharger les méthodes windowControllerWillLoadNib: ou windowControllerDidLoadNib: de NSDocument pour faire tout paramètrage supplémentaire dont vous avez besoin.

Si vous sous-classez NSWindowController, vous devez surcharger à la place windowWillLoad et windowDidLoad de NSWindowController.

Types, Chargement, et Sauvegarde

Comment implémenter les documents-paquets (documents qui sont en réalité des dossiers, mais qui apparaissent comme des documents opaques) ?

Habituellement, une application qui veut que ses documents soient des documents-paquets utilisera la classe NSFileWrapper pour construire et accéder à ses documents.  Si c’est vrai pour quelques-uns ou pour tous les types que votre classe document gère, alors au lieu de surcharger dataRepresentationOfType: et loadDataRepresentation:ofType:, vous devriez surcharger fileWrapperRepresentationOfType: et loadFileWrapperRepresentation:ofType:.

Ces méthodes sont presque les mêmes que les méthodes sur les données, mais elles utilisent NSFileWrappers à la place.

Vous devriez aussi spécifier, dans la section sur les types de document de votre liste de propriétés, que les documents de ce type sont des paquets (comme décrit dans “Stockage des informations sur les types de documents dans un fichier .plist”).

Comment implémenter la sauvegarde et le chargement quand les API de données simples (NSData) ou d’empaquetage des données (NSFileWrapper) ne fonctionnent pas ?

Si, pour n’importe quelle raison, ni l’API NSData ni l’API NSFileWrapper ne fonctionnent pour vous, vous pouvez surcharger les quatre méthodes qui chargent et sauvegarde un document à partie de son chemin (path) ou de son URL. Ces méthodes sont :

writeToFile:ofType:
writeToURL:ofType:
readFromFile:ofType:
readFromURL:ofType:

Vous devrez surcharger les quatres méthodes si vous avez l’intention de les utiliser en tant que méthodes de base pour la lecture et la sauvegarde. Vous pouvez implémenter vos méthodes de fichier pour appeller vos méthodes d’URL et vice-versa.

Vous devez aussi surcharger ces méthodes non pas pour fournir les fonctionnalités de chargement et de sauvegarde, mais plutôt pour faire quelque chose juste avant ou juste après le chargement ou la sauvegarde. Dans ce cas, vous devrez ajouter votre code avant ou après un appel à l’implémentation de la superclasse. Ce type de surcharge est exposé dans “Comment gérer la lecture d’un type et sa conversion automatique (en interne) en un autre type ?”.

Comment gérer des types en lecture seule ?

Si votre application peut lire certains types de fichiers sans pouvoir les écrire, vous pouvez les déclarer en paramétrant leur rôle à “Viewer” au lieu  de “Editor” dans Project Builder, comme cela est expliqué ici : “Stockage des informations sur les types de documents dans un fichier .plist”.

Comment gérer des types en écriture seule ?

Si votre application peut enregistrer certains types de fichiers san pouvoir les lire, vous pouvez le déclarer en utilisant la clef NSExportableAs. Vous pouvez inclure  la clef NSExportableAs dans le dictionnaire des types pour un autre type que votre classe document gère.Habituellement, cette clef devrait être dans le dictionnaire de type au niveau du type le plus “natif” de votre classe document. Sa valeur est un tableau de noms de type que votre classe peut écrire mais pas lire.

L’exemple Sketch utilise cette clef pour permettre d’exporter les images en TIFF et EPS même si l’application ne peut pas lire ces types.

Les types en lecture seule ne peuvent être choisis que par “Enregistrer sous …”. Ils ne sont pas disponibles via “Enregistrer”.

Comment gérer la lecture d’un type et sa convertion automatique (en interne) en un autre type ?

Parfois, vous pouvez comprendre comment lire un type, mais ne pas savoir comment l’écrire, et donc, quand vous lisez des documents de ce type, vous voulez les convertir automatiquement en un autre type que vous pouvez écrire. Un exemple de ceci serait une application qui peut lire les documents d’une ancienne version (ou d’un produit concurrent). Elle pourrait lire les anciens documents et les convertirait automatiquement dans le nouveau format natif.

La première étape est  d’ajouter l’ancien type comme un type en lecture seule (voir “Comment gérer des types en lecture seule ?”).En faisant cela, vous serez capable d’ouvrir les ancien fichiers, mais ils s’ouvriront comme “untitled” ou “sans-titre”.

Si vous voulez les convertir automatiquement dans votre nouveau type, vous pouvez surcharger les méthodes readFrom… dans votre sous-classe de NSDocument pour appeler super et à ce moment là effacer le nom de fichier et son type. Vous devriez utiliser setFileType: et setFileName: pour donner un type approprié et un nom au nouveau document. Pour le nom de fichier, vous devrez vous assurer d’enlever l’ancienne extension du fichier original, si elle existe, et ajouter la nouvelle extension.

Comment personnaliser le panneau de sauvegarde ?

Vous pouvez controler si la vue d’accessoires par défaut (laquelle contient un popup permettant à l’utilisateur de choisir le format de sauvegarde) est utilisée en surchargeant shouldRunSavePanelWithAccessoryView.La vue par défaut est utilisée si cette méthode retourne TRUE et si le document permet l’écriture dans plusieurs types.

Vous pouvez plus personnaliser le panneau en surchargeant prepareSavePanel: et en modifiant le panneau avant d’appeler super. Vous pouvez aussi remplacer entièrement la vue accessoire.

Impression

Comment implémenter l’impression ?

Les sous-classes de NSDocument qui doivent gérer l’impression doivent surcharger printShowingPrintPanel:. Habituellement, c’est implémenté pour créer un NSPrintOperation avec les informations d’impression du document et le lancer.

Un document doit être préparé pour s’imprimer lui même, même s’il n’a pas de controleur de fenêtre sur le moment.

Dois-je faire quelque chose au sujet des informations d’impression ?

Idéalement, vous devez traiter les informations d’impression du document comme une part du document, à sauvegarder et charger en même temps que le reste du document. Ce n’est pas toujours possible si votre format de document est déjà défini et qu’il n’est pas assez souple pour autoriser la sauvegarde de ces informations d’impression.

A part les utiliser pour créer vos opérations d’impression et éventuellement les sauvegarder et les charger, vous n’avez rien à faire de plus au sujet des informations d’impression.

NSWindowControllers

Comment construire une sous-classe de NSWindowController qui utilise automatiquement un fichier nib en particulier ?

NSWindowController attends que quelqu’un lui dise quel fichier nib charger (avec sa méthode initWithNib… ), car c’est l’implémentation générique du comportement par défaut de tout les contrôleurs de fenêtres. Toutefois, quand vous écrivez une sous-classe de NSWindowController c’est presque toujours pour contrôler l’interface utilisateur contenue dans un fichier nib en particulier, et votre sous-classe ne fonctionnera pas avec un autre fichier nib. Il est donc incommode et source d’erreur pour le client de la sous-classe d’avoir à lui dire quel fichier nib charger.

C’est facilement résolu en surchargeant la méthode init pour simplement appeler la méthode initWithNibName: de super avec le bon nom de fichier nib. Maintenant les clients n’ont qu’à utiliser init et le controleur a le nib correct. Vous pouvez aussi surcharger les méthodes initWithNib… pour enregistrer une erreur, car aucun client ne doit jamais essayer de dire à votre sous-classe quel fichier nib utiliser. C’est une bonne idée pour toute sous-classe de NSWindowController conçue pour travailler avec un fichier nib spécific. Vous ne devez pas le faire seulement quand vous étendez les fonctionnalités de base de NSWindowController dans votre sous-classe, et que vous n’avez lié ces fonctionnalités à aucun fichier nib en particulier.

Comment utiliser NSWindowController pour des panneaux partagés (inspecteurs, panneaux de recherche, etc …) ?

Un NSWindowController sans NSDocument associé est utile par lui même. NSWindowController peut être utilisé comme classe de base pour des controleurs de paneau auxiliaires pour pouvoir utiliser ses capacités de gestion des fichiers nib.

Une utilisation indépendante habituelle est pour les controleurs de panneaux partagés comme les panneaux de recherche, les inspecteurs ou les panneaux de préférences. Dans ce cas, vous pouvez créer une sous-classe de NSWindowController qui implémente une méthode d’instance partagée. Par exemple, vous pourriez créer une sous-classe PreferencesController avec une méthode de classe sharedPreferenceController qui crée une seule instance la première fois qu’elle est appelée et retourne cette instance à chaque nouvel appel.

Comme votre sous-classe est un NSWindowController, vous pouvez simplement lui donner le nom de votre fichier nib de Preferences et elle se chargera de le charger et de gérer la fenêtre automatiquement. Vous ajoutez vos propres outlets et actions, comme d’habitude, pour connecter l’interface utilisateur spécifique à votre panneau, et ajoutez l’API pour contrôler le comportement du panneau.

L’application Sketch utilise des sous-classes de NSWindowController pour ses nombreux panneaux secondaires.

Comment utiliser plusieurs NSWindowControllers dans un document simple ?

Si vous utilisez makeWindowControllers pour créer votre contrôleur de fenêtre (voir “Comment sous-classer NSWindowController?”), vous pouvez en créer plus d’un, probablement de différentes sou-classes de NSWindowController, depuis le début. Une autre possibilité est de permettre à l’utilisateur de créer de nouveau contrôleurs plus tard quand il en a besoin. Dans tous les cas, plusieurs contrôleurs peuvent être ajoutés à un document avec addWindowController:.

Par défaut, un document fermera quand son dernier contrôleur ferme. Des contrôleurs spécifiques peuvent aussi être configurés pour fermer le document quand ils ferment même si d’autres contrôleurs sont encore ouverts. Un exemple où cela peut arriver est Interface Builder. Il y a une fenêtre principale pour le fichier nib (celle avec les onglets et les instances de haut niveau dedans), et il y a probablement un ensemble d’autre fenêtres (les éditeurs de fenêtre pour les fenêtres réelles du fichier nib). Si l’utilisateur ferme la fenêtre principale, toutes les autres fenêtres se ferment en même temps et le document est lui-même fermé. Interface Builder appelle setShouldCloseDocument:TRUE dans le contrôleur de la fenêtre principale pour implémenter ce comportement.

Comment personnaliser le titre de la fenêtre du NSWindowController d’un document ?

Si vous le voulez, vous pouvez surcharger la méthode windowTitleForDocumentDisplayName: de NSWindowController pour modifier le titre de chaque vue. Par exemple, une programme de CAD peut avoir les titres “Airplane - Top”, “Airplane -Side”, etc pour les différentes fenêtres qu’il utilise pour le document “Airplane”.

Annulation et comptage des changements

Comment implémenter l’annulation ?

L’annulation n’est pas toujours facile à implémenter, mais au moins le mécanisme pour le mettre en application est pratique. Par défaut, un NSDocument a son propre NSUndoManager. NSUndoManager fait en sorte de vous permettre de créer facilement des invocations qui seront le contraire de ce que vous faites pour changer le document.

La clef est d’avoir des primitives bien définies pour changer votre document. Chaque objet modèle, plus la sous classe document elle-même, doit définir le groupe de méthodes de base qui peuvent le changer. Chaque méthode de base est alors responsable de l’utilisation du gestionnaire d’annulation pour mettre en file d’attente les invocations qui annulent l’action de la méthode primitive. Par exemple, si vous décidez que setColor: est une méthode primitive pour un de vos objets modèles, alors dans setColor: votre objet devrait faire quelque chose comme :

 [[[myDocument undoManager] prepareInvocationWithTarget:self] setColor:oldColor]

Cet appel fera en sorte que le gestionnaire d’annulation construise une invocation et l’enregistre. Si l’utilisateur choisi plus tard “Annuler”, l’invocation enregistrée sera appelée et votre modèle recevra un autre message setColor:, cette fois avec l’ancienne couleur.(Si vous vous demandez si vous devez garder une trace de l’annulation des choses et éviter ainsi au gestionnaire d’annulation de refaire le travail, et la réponse est non. En fait,”Rétablir” (Redo) travaille en regardant quelles invocations sont enregistrées quand “Annuler” est lancé et les enregistre dans la pile Redo (Rétablir)!).

Une autre partie d’une bonne implémentation de l’annulation est de fournir des noms aux actions afin d’avoir des éléments de menus Annuler/Rétablir avec des titres plus descriptifs. Les noms des actions d’annulation sont souvent mieux placés dans vos méthodes d’action que dans les primitives de changement de vos objets modèles car beaucoup de changements primaires peuvent aller dans une action d’utilisateur ou différentes actions d’utilisateur peuvent appeler les mêmes primitives de différentes façon. L’exemple Sketch implemente l’annulation dans ce sens si vous voulez voir un example concret.

Pour plus d’information sur le support de l’annulation, voir Annuler et rétablir.

Comment implémenter l’annulation partielle ?

Comme NSUndoManager gère l’annulation multiple, ce n’est pas une bonne idée d’implémenter l’annulation pour une partie seulement des changements possibles. Le gestionnaire d’annulation s’appuie sur la possibilité certaine de faire revenir en arrière le document à travers l’histoire avec des annulations successives. Si certains changement sont évités, la pile d’annulation ne peut plus être synchronisée avec le contenu du document. En fonction de l’architecture de l’application, cela peut causer des problèmes allant du simplement génant à l’erreur fatale.

S’il y a certains changement qui ne peuvent être annulés, il y a deux possibilités pour gérer la situation quand un utilisateur fait ce type de changement. Si vous pouvez être absolument certain que les changements n’ont aucune relation avec les autres changements qui peuvent survenir sur le document (c’est à dire quelquechose de complètement indépendant de tout le reste du contenu a changé), alors vous pouvez sans risque simplement ne pas répertorier d’action d’annulation. Sinon, vous pouvez vider toutes les actions du gestionnaires d’annulation quand un tel changement arrive. Ce type de changement est donc un point de non retour dans l’expérience utilisateur. Quand vous concevez votre application et le format de document, vous devriez tenter d’éviter le recours à ce type d’opérations “point de non retour”.

Et si je ne veux pas gérer l’annulation ?

Si vous ne voulez pas supporter l’annulation du tout, la première chose à faire est d’appeler setHasUndoManager:FALSE dans vos documents. Ceci fera que vos documents ne pourront jamais avoir de gestionnaire d’annulation.

Sans gestionnaire d’annulation (et support de l’annulation dans vos objets modèles), le document ne peut pas vérifier automatiquement son état de modification. Donc vous devrez appeler updateChangeCount: à la main quand votre document est modifié.

Qu’est ce que ce compteur de changement ?

A cause du support de l’annulation, le document doit garder plus d’informations que simplement si le document est modifié ou non. Si un utilisateur ouvre un fichier, fait cinq changements et fait cinq fois “Annuler”, le document doit être à nouveau propre (non modifié). Mais si l’utilisateur n’annule que quatre opérations, le document est encore sal (modifié).

NSDocument tient un compteur de changement pour échanger avec. Le compteur peut être modifié par l’appel de updateChangeCount: avec un des types de changement supportés Les changement supportés sont NSChangeDone, NSChangeUndone, et NSChangeCleared. NSDocument lui-même efface le compteur quand quelqu’un sauvegarde le document ou revient à l’original. Si le document possède un gestionnaire d’annulation, il observe le gestionnaire et met automatiquement à jour le compteur quand les changements sont faits, défaits ou refaits.

Si votre sous-classe de document ne supporte pas l’annulation, alors vous avez besoin d’informer vous-même NSDocument au sujet des modifications avec updateChangeCount: (voir “Et si je ne veux pas gérer l’annulation ?”).

NSDocumentController

Dois-je sous-classer NSDocumentController ?

Habituellement, vous ne devriez pas avoir besoin de sous classer NSDocumentController. Presque tout ce qui peut être fait en sous-classant peut être fait aussi facilement par les délégués de l’application. Toutefois,il est possible de sous-classer NSDocumentController si vous pensez en avoir besoin.

Comment sous-classer NSDocumentController ?

Il y a deux façon de sous-classer NSDocumentController.

Vous pouvez créer une instance de votre sous-classe dans votre fichier nib principal. Cette instance deviendra l’instance partagée.

Ou vous pouvez créer une instance de votre sous-classe dans la méthode applicationWillFinishLaunching: du délégué de votre application.

Le premier NSDocumentController créé deviendra l’instance partagée. L’Application Kit lui-même créera l’instance partagée (en utilisant la classe NSDocument Controller) pendant la phase “finish launching” du lancement de l’application. Ainsi, si vous avez besoin d’une sous-classe, vous devez la créer avant que le kit le fasse.

Comment créer de nouveaux documents autrement que par des méthodes d’action des utilisateurs ?

Vous pouvez utiliser les méthodes open… de NSDocumentController, qui crée un document et, si shouldCreateUI est TRUE, crée aussi la (les) contrôleur(s) de fenêtre du document et ajoute le document à la liste des documents ouverts. Ces méthodes cherchent aussi les chemins des fichiers et retournent un document existant pour le chemin si il y en a un.

Vous pouvez aussi utiliser les méthodes make… de NSDocumentController, qui crée seulement le document. Habituellement, vous voudrez appeler addDocument: pour ajouter le nouveau document au NSDocumentController.

Finalement, vous pouvez simplement créer un document vous même avec n’importe quel initialisateur que la sous-classe supporte. Habituellement, vous voudrez ajouter le document au NSDocumentController avec la méthode addDocument: de NSDocumentController.

Comment autoriser l’utilisateur à créer différents types de documents ?

Certaines applications supportent plusieurs types de document réellement différent (et non un seul document qui peut être sauvegardé dans plusieurs formats). Par exemple, Appleworks vous autorise à créer des documents de texte, de tableur ou d’autres types de documents, tout cela dans une seule application. Dans ces applications, l’ouverture de documents existants n’est pas habituellement un problème, car le type de document est déterminé par le fichier que l’on ouvre. Mais la création de nouveaux documents dans ce type d’application est plus difficile.

La méthode action newDocument: de NSDocumentController crée un nouveau document du premier type listé dans le tableau de type de document de l’application (configuré dans project Builder). Mais ce n’est pas réellement suffisant pour les applications qui veulent supporter plusieurs types de document distincts.

Au lieu de cela, vous pouvez créer votre (vos) propres nouvelle(s) action(s), aussi bien dans le délégué de votre application que dans une sous-classe de NSDocumentController. Vous pourriez créer plusieurs méthodes d’action et avoir plusieurs éléments de menu “New” (”Nouveau”) différents, ou vous pourriez avoir une action qui demande à l’utilisateur de choisir un type de document avant d’en créer un nouveau.

Une fois que l’utilisateur a sélectionné un type, votre méthode action peut simplement utiliser la méthode openUntitledDocumentOfType: dans NSDocumentController pour créer réellement un document du type correct.

Comment personnaliser le panneau d’ouverture ?

Si vous avez besoin de personnaliser le panneau d’ouverture des fichiers, vous rencontrez un des moment où vous avez clairement besoin d’une sous-classe de NSDocumentController. Vous pouvez surcharger la méthode runModalOpenPanel:forTypes: de NSDocumentController pour personnaliser le panneau ou ajouter une vue accessoire.

Créer une sous-classe de NSDocumentController

La classe NSDocumentController garde une trace de la première instance de NSDocumentController (ou une sous-classe personnalisée) qui est créée, et retourne cette instance avec sa méthode sharedDocumentController. Pour permettre à votre application d’utiliser votre sous-classe personnalisée de NSDocumentController, vous devez faire en sorte que votre sous-classe soit la première instance de NSDocumentController créée quand l’application démarre. Il y a deux façon de le faire.

  1. Créer votre sous-classe dans le fichier nib principal.Le fichier nib principal est situé par l’application à son démarrage. Si vous créez une instance de votre sous-classe dans le fichier nib principal, l’application la chargera à son lancement et l’utilisera comme contrôleur de document partagé. Vous devez connecter l’outlet délégué de l’application à l’instance de votre sous-classe afin que votre application puisse communiquer avec votre contrôleur de document.
  2. Créer une instance de votre sous-classe dans applicationWillFinishLaunching:L’application ne cherchera pas son contrôleur de document partagé avant que le message applicationWillFinishLaunching: soit envoyé à son délégué. Donc, vous pouvez créer une instance de votre sous-classe de NSDocumentController dans la méthode applicationWillFinishLaunching: du délégué de votre application, et cette instance sera identifiée comme le contrôleur de document partagé.

Sauvegarder le type HFS et le code Créateur

Par défaut, les applications basées sur NSDocument ne sauvegarde ni le type HFS ni le code créateur dans les documents. Pour placer les codes type et créateur, votre sous-classe de NSDocument peut réécrire fileAttributesToWriteToFile:ofType:saveOperation: pour ajouter les codes type et créateur aux attributs NSFileHFSTypeCode et NSFileHFSCreatorCode , respectivement.

Si vous voulez placer les codes type et créateur d’un fichier, indépendamment de NSDocument, utilisez la méthode changeFileAttributes:atPath: de NSFileManager en Objective-C, ou setFileAttributes de NSPathUtilities en Java.

Le listing 1 montre la méthode fileAttributesToWriteToFile:ofType:saveOperation: d’une sous-classe NSDocument, implémentée en Objective-C. Cette implémentation utilise la signature du bundle d’application pour le code créateur, et le premier type HFS disponible associé avec le type du document, les deux trouvant l’information dans la liste d’information de propriété de l’application. Si l’un des deux manque, la méthode ne place pas la clé correspondante dans le “dictionary” retourné. Vous pouvez modifier ce fragment pour générer le comportement que vous voulez dans votre application.

Listing 1 Sauvegarder les informations de type HFS et de créateur (Objective-C)

- (NSDictionary *)fileAttributesToWriteToFile:(NSString *)fullDocumentPath
ofType:(NSString *)documentTypeName
saveOperation:(NSSaveOperationType)saveOperationType
{
 NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
 NSString *creatorCodeString;
 NSArray *documentTypes;
 NSNumber *typeCode, *creatorCode;
 NSMutableDictionary *newAttributes;
 typeCode = creatorCode = nil;
// First, set creatorCode to the HFS creator code for the application,
// if it exists.
 creatorCodeString = [infoPlist objectForKey:@"CFBundleSignature"];
 if(creatorCodeString)
 {
  creatorCode = [NSNumber
  numberWithUnsignedLong:NSHFSTypeCodeFromFileType([NSString
  stringWithFormat:@"'%@'",creatorCodeString])];
 }
// Then, find the matching Info.plist dictionary entry for this type.
// Use the first associated HFS type code, if any exist.
 documentTypes = [infoPlist objectForKey:@"CFBundleDocumentTypes"];
 if(documentTypes)
 {
  int i, count = [documentTypes count];
  for(i = 0; i < count; i++)
  {
   NSString *type = [[documentTypes objectAtIndex:i]
   objectForKey:@"CFBundleTypeName"];
   if(type && [type isEqualToString:documentTypeName])
   {
    NSArray *typeCodeStrings = [[documentTypes objectAtIndex:i]
    objectForKey:@"CFBundleTypeOSTypes"];
    if(typeCodeStrings)
    {
     NSString *firstTypeCodeString = [typeCodeStrings
     objectAtIndex:0];
     if (firstTypeCodeString)
     {
      typeCode = [NSNumber numberWithUnsignedLong:
                  NSHFSTypeCodeFromFileType([NSString
                  stringWithFormat:@"'%@'",firstTypeCodeString])];
     }
    }
    break;
   }
  }
 }
// If neither type nor creator code exist, use the default implementation.
 if(!(typeCode || creatorCode))
 {
  return [super fileAttributesToWriteToFile:fullDocumentPath
  ofType:documentTypeName saveOperation:saveOperationType];
 }
// Otherwise, add the type and/or creator to the dictionary.
 newAttributes = [NSMutableDictionary dictionaryWithDictionary:[super
 fileAttributesToWriteToFile:fullDocumentPath ofType:documentTypeName
 saveOperation:saveOperationType]];
 if(typeCode) [newAttributes setObject:typeCode forKey:NSFileHFSTypeCode];
 if(creatorCode)[newAttributes setObject:creatorCode forKey:NSFileHFSCreatorCode];
 return newAttributes;
}

Le listing 2 est l’implémentation équivalente de fileAttributesToWriteToFile en Java. Comme fileAttributesToWriteTofile a été introduite pour Java dans Mac OS X version 10.2, ce code ne fonctionnera pas avec les versions antérieures du système d’exploitation.

Listing 2 Sauvegarder les informations de type HFS et de créateur (Java)

public NSDictionary fileAttributesToWriteToFile(String fullDocumentPath,
String documentTypeName, int saveOperationType)
{
 NSDictionary infoPlist = NSBundle.mainBundle().infoDictionary();
 Integer typeCode, creatorCode;
 typeCode = creatorCode = null;
// First, set creatorCode to the HFS creator code for the application,
// if it exists.
 String creatorCodeString;
 creatorCodeString = (String)infoPlist.objectForKey("CFBundleSignature");
 if(creatorCodeString != null)
 {
  creatorCode = new Integer(
  NSHFSFileTypes.hfsTypeCodeFromFileType("'" + creatorCodeString + "'"));
  }
// Then, find the matching Info.plist dictionary entry for this type.
// Use the first associated HFS type code, if any exist.
 NSArray documentTypes =
 (NSArray)infoPlist.objectForKey("CFBundleDocumentTypes");
 if(documentTypes != null)
 {
  int count = documentTypes.count();
  for(int i = 0; i < count; i++)
  {
   String type = (String)
   ((NSDictionary)
   (documentTypes.objectAtIndex(i))).objectForKey("CFBundleTypeName");
   if(type != null && (type.equals(documentTypeName)))
   {
    NSArray typeCodeStrings =
    (NSArray)((NSDictionary)
    (documentTypes.objectAtIndex(i))).objectForKey("CFBundleTypeOSTypes");
    if(typeCodeStrings != null)
    {
     String firstTypeCodeString =
     (String)typeCodeStrings.objectAtIndex(0);
     if (firstTypeCodeString != null)
     {
      typeCode = new Integer(
      NSHFSFileTypes.hfsTypeCodeFromFileType("'" + firstTypeCodeString + "'"));
     }
    }
    break;
   }
  }
 }
// If neither type nor creator code exist, use the default implementation.
 if(typeCode == null && creatorCode == null)
 {
  return super.fileAttributesToWriteToFile(fullDocumentPath,
  documentTypeName, saveOperationType);
 }
// Otherwise, add the type and/or creator to the dictionary.
 NSMutableDictionary newAttributes = new NSMutableDictionary(
 super.fileAttributesToWriteToFile(fullDocumentPath,
 documentTypeName, saveOperationType));
 if(typeCode != null)
  newAttributes.setObjectForKey(typeCode,
  NSPathUtilities.FileHFSTypeCode);
 if(creatorCode != null)
  newAttributes.setObjectForKey(creatorCode,
  NSPathUtilities.FileHFSCreatorCode);
 return (NSDictionary)newAttributes;
}

Texte original en anglais sur developer.apple.com : Overview of Programming Topic: Document-Based Applications

Pascal Développement ,

  1. Pas encore de commentaire
  1. Pas encore de trackbacks
Vous devez être identifié pour poster un commentaire