Příkazy a schránka - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Seriály

Více seriálů



Software

Příkazy a schránka

27. listopadu 2006, 00.00 | Minule jsme se naučili využít vyšší vrstvy input managementu pro pohodlné zpracování událostí a rozlišení běžných kláves a příkazů typu moveLeft: či deleteBackward:. Řekli jsme si ale také, že "ryze příkazové" kombinace kláves s přepínačem Command se zpracovávají jinou cestou – a dnes si právě ukážeme, jak na ně.

V seriálu o programování ve vývojovém prostředí Cocoa se v současnosti zabýváme tvorbou vlastních grafických objektů, views. Se základy jejich konstrukce a s kreslením jejich grafické podoby jsme se seznámili zpočátku; nyní se učíme ve views zpracovávat nejrůznější události, generované vstupními zařízeními: nejprve jsme se naučili reagovat na události vytvářené myší, a teď si ukazujeme, kterak ošetřit události související s klávesnicí.

V předminulém dílu jsme si popsali základy spojené s first responderem a také nejzákladnější zprávu keyDown:, jejímž prostřednictvím jsou tyto události předávány. Minule jsme se naučili využít vyšší vrstvy input managementu pro pohodlné zpracování událostí a rozlišení běžných kláves a příkazů typu moveLeft: či deleteBackward:. Řekli jsme si ale také, že "ryze příkazové" kombinace kláves s přepínačem Command se zpracovávají jinou cestou – a dnes si právě ukážeme jak na ně.

Zároveň, protože se to zrovna hodí, se naučíme základní operace pro práci se schránkou – podobně, jako jsme si minule ukázali, kterak v prostředí Cocoa korektně zobrazovat "alerty".

Standardní příkazy

Nejjednodušší to je se standardními příkazy jako copy, paste či delete: ty jsou totiž zpracovávány knihovním kódem aniž bychom se o to museli jakkoli starat, a naše view je dostane prostřednictvím odpovídajících standardních zpráv – pokud je samozřejmě implementuje.

Zkuste spustit aplikaci v její současné podobě; otevřete-li menu "Edit", uvidíte, že celá skupina příkazů "Cut", "Copy", ..., "Delete" je zobrazena šedě – to proto, že framework "vidí", že náš view (který je, jak víme od předminula, first responderem) odpovídající služby nedokáže nabídnout. Jakmile však některou z nich implementujeme, třeba takto:

-(void)delete:sender {
  [self deleteBackward:sender];
}

vše ihned automaticky začne fungovat: framework uvidí, že momentální first responder implementuje službu delete:, takže v menu "Edit" příkaz "Delete" bude k dispozici; vybere-li jej uživatel, naše view dostane zprávu delete: a smaže označený grafický objekt nebo všechny objekty – tuto službu jsme implementovali na konci minulého článku.

Schránka

Implementujeme-li již standardní příkazy, vyplatí se na malou chvilku odbočit, a ukázat si, jak se v prostředí Cocoa pracuje se schránkou prostřednictvím standardních služeb třídy NSPasteboard. Je to dosti jednoduché: při kopírování pouze specifikujeme jméno, jež určí typ dat, uložených do schránky – v našem případě jde o data specifická pro aplikaci, takže použijeme i specifické jméno – a pak do schránky zapíšeme vlastní data. My použijeme pole Bézierových křivek (takže v případě, že je některá z nich označena, budeme samozřejmě muset vytvořit pole obsahující pouze tuto jedinou), a abychom je mohli uložit do schránky, použijeme standardní archivační služby prostředí Cocoa, jež již známe z doby, kdy jsme si povídali o Foundation Kitu:

static NSString *
  const PBType=@"cz.ocs.TestViewApp.Pasteboard";
-(void)copy:sender {
  if ([lines count]==0) NSBeep();
  else {
    NSPasteboard *pboard=[NSPasteboard generalPasteboard];
    [pboard declareTypes:
      [NSArray arrayWithObject:PBType] owner:nil];
    NSArray *a=lines;
    if (selection<[lines count])
      a=[NSArray arrayWithObject:
        [lines objectAtIndex:selection]];
    [pboard setData:
      [NSArchiver archivedDataWithRootObject:a]
      forType:PBType];
  }
}

My se tím v této triviální aplikaci nebudeme obtěžovat, avšak je vhodné se zmínit, že takto lze do schránky "najednou" uložit data libovolně mnoha typů; každá aplikace si pak může vybrat ten formát, který se jí hodí. Mohli bychom kupříkladu vygenerovat z naší grafiky obrázek ve formátu PDF, a ten do schránky uložit – také jako "data" – s typem NSPDFPboardType. Také bychom mohli vytvořit textovou podobu (třeba seznam výchozích a koncových bodů našich čar), a uložit ji do schránky jako typ NSStringPboardType – ten bychom pak neukládali zprávou setData:forType:, nýbež alternativní zprávou setString:forType:, a "přečíst" ze schránky by si jej pak dokázala libovolná aplikace, jež dokáže pracovat s texty...

Povšimněme si také volání standardní služby NSBeep v případě, že není co kopírovat: to proto, abychom dali uživateli na vědomí, že se jeho akce tak docela nepodařila. Samozřejmě, že lepší by bylo v případě, kdy nemáme co kopírovat, zakázat vůbec položku "Copy" v menu; ani to není příliš těžké, ale přece jen si to již necháme na jindy.

Pro implementaci služby paste:pak nejprve pomocí standardní metody availableTypesFromArray: ověříme, zda schránka obsahuje data požadovaného typu; v kladném případě je načteme, "rozbalíme" pomocí standardního "unarchiveru", a vložíme do pole lines:

-(void)paste:sender {
  NSPasteboard *pboard=[NSPasteboard generalPasteboard];
  if ([pboard availableTypeFromArray:
    [NSArray arrayWithObject:PBType]]) {
    NSArray *a=[NSUnarchiver
      unarchiveObjectWithData:[pboard dataForType:PBType]];
    [lines addObjectsFromArray:a];
    selection=1e6;
    [self setNeedsDisplay:YES];
  } else NSBeep();
}

Nezapomeneme samozřejmě "vynulovat" (tedy v našem případě nastavit na hodně velkou hodnotu) proměnnou selection, ani pípnout, pokud schránka neobsahuje data požadovaného typu.

Implementace příkazu "Cut" již je jednoduchá, a ponecháme ji jako triviální cvičení laskavému čtenáři :)

Samozřejmě, jako vždy, ukazujeme si jen základy – schránka v Cocoa nabízí mnohem více; alespoň za stručnou zmínku kupříkladu stojí možnost do schránky ve skutečnosti nic neukládat a počkat teprve zda si nějaký jiný (nebo týž) proces data ze schránky vyžádá: v našem případě je to nevýznamné a nemělo by to smysl, avšak u rozsáhlých dat je to vhodné, neboť to významně zrychlí provedení příkazu "Copy".

Nestandardní příkazy

Ačkoli systém Cocoa umožňuje na úrovni views přímo zpracovávat libovolnou kombinaci s přepínačem "Command" pomocí standardní metody performKeyEquivalent:, nebudeme si ukazovat jak na to – vyjma zcela výjimečných případů to totiž není dobře. Příkazy by všechny měly být v menu, a jejich klávesové kombinace nemají být nikde v aplikaci naprogramovány natvrdo – to proto, aby je uživatel mohl kdykoli podle libosti předefinovat pomocí odpovídajících služeb v "System Preferences".

Co tedy dělat, chceme-li přidat nějaký nestandardní příkaz – třeba "Mirror", který by obsah našeho "view" zrcadlově převrátil? Vlastně nic, co bychom již neznali: implementujeme přímo metodu mirror: – její konkrétní obsah:

-(void)mirror:sender {
    NSAffineTransform *at=[NSAffineTransform transform];
    [at translateXBy:NSWidth([self bounds]) yBy:0];
    [at scaleXBy:-1 yBy:1];
    for (id o,en=[lines objectEnumerator];o=[en nextObject];)
        [o transformUsingAffineTransform:at];
    [self setNeedsDisplay:YES];
}

prozatím ponecháme bez vysvětlení, neboť afinní transformace jsou "vyšší dívčí", jíž se budeme zabývat až mnohem později.

Pak v aplikaci Interface Builder doplníme mezi akce First Responderu akci "mirror:"

abychom se dostali do inspektoru "třídy" First Responder, stačí v okně Interface Builderu poklepat na stejnojmennou ikonu – a přidání položky menu i její "nadrátování" na nově vytvořenou akci již pro nás samozřejmě jsou zcela stará vesta:

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Tipy a Triky  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

 

 

 

Nejčtenější články
Nejlépe hodnocené články
Apple kurzy

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: