IT-Academy Logo
Sign Up Login Help
Home - Programmieren - Windows - OWL Object Windows Library



OWL Object Windows Library

Die OWL ist eine Klassenbibliothek, die mit den Borland C++ Compilern mitgeliefert wird. Dieses Dokument sammelt die Erfahrungen in der Programmierung der OWL ohne Anspruch auf Vollständigkeit. Es geht in erster Linie um die OWL als Mittel zur Erzeugung von portablem Code zwischen Windows und OS/2.


Autor: Arnold Willerner (willemer)
Datum: 23-01-2002, 19:36:13
Referenzen: http://www.willemer.de/informatik/index.htm
Schwierigkeit: Profis
Ansichten: 6870x
Rating: Bisher keine Bewertung.

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Fenster


Erzeugen und Starten eines Standardfensters

Wie im normalen API unterscheidet auch die OWL zwischen Rahmen- und Clientfenster. Das Rahmenfenster kann dekoriert sein (Statusbalken) oder ein MDI-Rahmen sein. Der Inhalt ist ein von TWindow direkt abgeleitetes Fenster.

Das Elternfenster eines Rahmenfensters ist normalerweise 0 und damit der Desktop.

tFrameWindow *TextFenster;
    TextFenster = new tTextWindowFrame
(0, "Betrachter", new tTextWindow(...)); TextFenster->Create(); TextFenster->ShowWindow(SW_SHOWNORMAL);
Im Konstruktor ist der Aufruf von Init notwendig.

tTextWindow::tTextWindow(char *Name, long pvon, long pbis)
{
Init(0, 0, 0);
TextView = new TEdit(this, 100, "", 2,2,100,100, MAXTEXTLEN, true);
}
   

Größenänderungen

Zunächst wird eine Memberfunktion definiert, die Größenänderungen empfängt (EvSize).

class tTextWindow : public TWindow { 
public:
   ...
private:
   void EvSize(unsigned int, TSize&);
   ...
   DECLARE_RESPONSE_TABLE(tTextWindow);
};

DEFINE_RESPONSE_TABLE1(tTextWindow, TWindow)
EV_WM_SIZE,
END_RESPONSE_TABLE;

void tTextWindow::EvSize(unsigned int, TSize& newSize)
// Die Textfeld soll mit dem Fenster mitwachsen
{
#ifdef __OS2__
TRect Rect;

this->GetClientRect(Rect);
newSize.cx = Rect.right-Rect.left;
newSize.cy = Rect.bottom-Rect.top;
#endif

EditFenster->MoveWindow(0, 0, newSize.cx, newSize.cy);
// die restlichen Striche wegmachen und die Scrollbars wieder richten...
   Invalidate();
} 
Hier wird ein Multiline-Edit an die Größe des Rahmenfensters angepaßt. WM_SIZE, das auslösende Ereignis, erscheint einmal direkt nach Erzeugung des Fensters und dann bei jeder Größenänderung.

Unter OS/2 scheint die EvSize speziell bei nachträglichen Größenänderungen einige Probleme mit dem Parameter zu haben. Man kann das Problem umgehen, indem man, wie oben demonstriert, noch einmal explizit das Client-Rechteck einliest.


Dialog wird nicht erzeugt

Typischerweise erzeugt der Resource-Workshop statt einem Standard-Button ein Borland Control. Sollte eine Dialogbox nicht starten, sollte der RC-File auf Borland-Controls abgesucht werden.

Maus und Tastatur


Die Anmeldung von Accelerator-Keys

Während die OS/2-Version kein Problem damit hat, die Accelerators im Konstruktor des Hauptfensters zu aktivieren, funktioniert dies bei Windows nicht und es gibt Ärger beim Portieren. Windows möchte diese gern vorher angelegt bekommen. Ein guter Ort ist die InitMainWindow-Funktion von TApplication. Dazu muß TApplication abgeleitet werden.

// Definition der virtuellen Funktion InitMainWindow
void TMyApp::InitMainWindow(void)
{
SetMainWindow(new TFrameWindow(0, "MeinFensterName" ,new tHauptFenster(0)));
	MainWindow->AssignMenu(FRAMEMENU);
	MainWindow->Attr.W = 600;
	MainWindow->Attr.H = 250;
	MainWindow->Attr.AccelTable = MAINACCEL;
}
Bei der Gelegenheit: Attr.W und Attr.H funktionieren unter OS/2 scheinbar überhaupt nicht. (oder nicht an dieser Stelle? Lösung zu mir!)

Verhindern, daß ESC die Dialogbox schließt

Es wird einfach eine normale CmCancel-Funktion geschrieben, die aber NICHT TDialog::CmCancel aufruft.

Dialoge


Minimalcode zum Erzeugen einer Dialogbox

Die Dialogbox wird im Workshop definiert und habe die ID IDD_ABOUT. Sie kann aufgerufen werden durch

void TMyApp::CmAbout() {
  TDialog(GetMainWindow(), IDD_ABOUT).Execute();
}

Dabei ist CmAbout die Funktion, die dem WM_COMMAND hinterlegt wird. Ist die Klasse, die diese Nachricht bearbeitet eine von TWindow abgeleitete, kann statt GetMainWindow() auch this verwendet werden.


Dialog zum Öffnen und Sichern einer Datei


static TFileOpenDialog::TData OpenData(OFN_FILEMUSTEXIST,

"All Files (*.*)|*.*|Text Files (*.txt)|*.txt|CD-Inhalt (*.cd)|*.cd",
                   0, "", "*");
TFileOpenDialog *OpenDialog;

OpenDialog = new TFileOpenDialog(this, OpenData, 0, "Datei lesen");
if (OpenDialog->Execute()==IDOK) {
// Datei wurde ausgewählt
// Dateiname mit Pfad befindet sich in OpenData.FileName
}

Hält man die Variable OpenData global, merkt sich diese die zuletzt bearbeitete Datei und Position.

Analog gibt es auch einen Dialog zum Sichern der Datei.


TFileSaveDialog::TData SaveData;
TFileSaveDialog SaveDialog(this, SaveData, 0, "Exportieren");

	if (SaveDialog.Execute()==IDOK) {
		fp = fopen(SaveData.FileName, "w");
In diesem Fall wurde die TData-Variable nicht vorbelegt.

Schriftauswahl-Dialog

#include <owl\choosefo.h>
 ...
 ...
TChooseFontDialog::TData FontData;
TChooseFontDialog *FontDialog;
static int FontSize = 0;
static char FontName[80] = "";
static TFont *MyFont = 0;

FontData.Flags = CF_SCREENFONTS; // CF_PRINTERFONTS CF_BOTH
if (FontSize) {
strcpy(FontData.LogFont.lfFaceName, FontName);
FontData.PointSize = FontSize*10;
FontData.Flags |= CF_INITTOLOGFONTSTRUCT;
}
FontDialog = new TChooseFontDialog(this, FontData, 0, "Schrift-Auswahl");

if (FontDialog->Execute()==IDOK) {
strcpy(FontName, FontData.LogFont.lfFaceName);
FontSize = FontData.PointSize/10;
. . .
}
delete FontDialog;
}

Der Aufruf FontDialog->Execute bewirkt, daß aus der Benutzer einen Fontdialog bekommt. Die Daten werden in der FontData-Struktur abgelegt. Die wichtigen Daten sind der "Facename" und die Größe in Punkt (nicht in Pixeln!). Voraussetzung, daß der Fontdialog überhaupt startet, ist die Belegung des FontData.Flags mit einer FONT-Option. Fehlt dieser Eintrag, behauptet Windows, es wären keine Fonts installiert.

Um die Dialogbox vorzubesetzen, muß das Flag CF_INITTOLOGFONTSTRUCT enthalten. Dann werden die Daten aus LogFont verwendet. Unter OS/2 scheint es einen Fehler im OWL zu geben, der verhindert, daß die Punktgröße vorbelegt werden kann. Sollte ich mich irren oder jemand einen guten Workaround haben, bitte eine Mail an mich.

Mit den Informationen kann ein TFont gebildet werden und dann auf Grafiken oder Kontrollelementen angewandt werden.


Farben-Dialog

  TChooseColorDialog::TData choose;
  static TColor    custColors[16] = { 
    0x010101L, 0x101010L, 0x202020L, 0x303030L,
    0x404040L, 0x505050L, 0x606060L, 0x707070L,
    0x808080L, 0x909090L, 0xA0A0A0L, 0xB0B0B0L,
    0xC0C0C0L, 0xD0D0D0L, 0xE0E0E0L, 0xF0F0F0L 

};

choose.Flags = CC_RGBINIT;
choose.Color = Color;
choose.CustColors = custColors;
if (TChooseColorDialog(this, choose).Execute() == IDOK) {
Color = choose.Color;
}
   

Edit- und Staticfelder


Textcursor manipulieren

TextView->SetSelection(0, 0); // setze den Text-Cursor an den Anfang
TextView->SetSelection(-1, -1); // setze den Text-Cursor an das Ende
TextView->SetSelection(0, -1); // markiere den gesamten Text
   

Aktion bei Verlassen des Eingabefeldes

Um den Focus bei Verlassen eines TEdit-Controls zu fangen, wird eine von TEdit abgeleitete Klasse erzeugt, die das WM_KILLFOCUS-Ereignis verarbeitet.

class tGastValid : public TEdit {
public:
tGastValid(TWindow *Win, short ID) : TEdit(Win, ID) {}
void EvKillFocus(HWND);
DECLARE_RESPONSE_TABLE(tGastValid);
};

DEFINE_RESPONSE_TABLE1(tGastValid, TEdit)
EV_WM_KILLFOCUS, // ruft EvKillFocus(HWND GetFocus)
END_RESPONSE_TABLE;

void tGastValid::EvKillFocus(HWND hWnd)
{
// Sende an das Elternfenster eine Pushbutton-Nachricht
GetParentO()->SendMessage(WM_COMMAND, DB_BTGAST, 0);
TEdit::EvKillFocus(hWnd);
}
In der Eventfunktion habe ich mir damit geholfen, daß ich an den übergeordneten Dialog eine Pushbutton-Nachricht simuliere.

Fonts und Farben


Ändern des Fonts für Kontrollelemente

An der Stelle, wo der Font geändert werden soll.

MyFont = new TFont ("Times New Roman", 10);

    MyStatic->SetWindowFont (*MyFont, TRUE);

Es ist wichtig, daß die TFont-Variable nicht eliminiert wird, bevor eine neues SetWindowFont aufgerufen wird. Damit entsteht die Gefahr von Speicherlöchern. Um dies zu vermeiden, kann man die alte TFont löschen, bevor sie neu besetzt wird.

static TFont *MyFont = 0;

      if (MyFont) delete MyFont;
      MyFont = new TFont(FontName, FontSize);
      MeinElement->SetWindowFont(*MyFont, TRUE);

Das Verfahren ist mit TStatic, TEdit und TListBox unter Windows getestet.

Um die korrekte Größe zu erreichen, muß der Device Context des Bildschirms oder eines der Fenster erzeugt und die logischen Pixel pro Zoll ermittelt werden.


LONG yLogPixelsPerInch;
LOGFONT LogFont = {0};

TClientDC dc(*Element);
yLogPixelsPerInch = dc.GetDeviceCaps(LOGPIXELSY);
LogFont.lfHeight = -MulDiv(FontSize, yLogPixelsPerInch, 72);
strcpy(LogFont.lfFaceName, FontName);

if (pTFont) delete pTFont;
pTFont = new TFont(&LogFont);
Element->SetWindowFont(*pTFont, TRUE);

Aus diesem Wert und der Punktgröße wird beim Teilen durch 72 (72 Punkt sind ein Zoll!) die Höhe des Bildschirmzeichens. Aus diesem LOGFONT wird dann per Konstruktor der TFont erstellt.


Ermitteln der Font-Metrics in einem Fenster

TEXTMETRIC tm;
TFont Schrift(GetWindowFont());

   // Berechne die Schriftgroesse
   Schrift.GetTextMetrics(tm);
   // in tm.tmHeight befindet sich z. B: die Höhe

Vorder- und Hintergrundfarben

Die einfachste Methode, die Farbe in einem von TWindow abgeleiteten Element zu ändern, ist SetColor für die Vordergrundfarbe und SetBkgndColor für die Hintergrundfarbe aufzurufen. In einer SetupWindow-Funktion einer Dialogbox könnte beispielsweise stehen:

#include <owl\color.h>

  // ...

static TBrush Farbe = TBrush(TColor::LtRed);

    EditName->SetBkgndColor(TColor::LtGray);
    EditName->SetColor(&Farbe);

Dadurch würde rote Schrift auf hellgrauem Grund erscheinen.

Das TBrush-Element muß statisch sein! Wird es bei Erstellen der Dialogbox erzeugt, sollte es im Destruktor wieder vernichtet werden.

Im OWL-FAQ finden sich mehrfach Hinweise wie dieser:

// Add this to your response table:
EV_WM_CTLCOLOR,
// define a TBrush
BkBrush = new TBrush(::GetSysColor(COLOR_BTNFACE));

HBRUSH PVdlistDlg::EvCtlColor(HDC hDC, HWND hWndChild, UINT ctlType)
{
POINT point;
UINT cid;

cid = ::GetDlgCtrlID(hWndChild);
if(cid == IDC_LISTBOX1)
{
SetTextColor(hDC, TColor(0,0,255));
SetBkColor(hDC, ::GetSysColor(COLOR_BTNFACE));
return *BkBrush;
}
return (TDialog::EvCtlColor(hDC,hWndChild,ctlType)); // No special action required
} 

Die Funktion EvCtlColor scheint aber unter OS/2 nicht angesprochen zu werden. (aw)


Menüs


Menüpunkte mit Haken versehen

Unter OWL basiert das "Enablen" und das "Checken" auf dem Command-Enabler. Es wird eine Variable gehalten, die den Zustand repräsentiert. Durch Anklicken des Menüpunkts wird der Inhalt der Variablen (hier FolgeTauschen) gewechselt.

Das Setzen des Hakens erfolgt am einfachsten in der Enable-Funktion. Die übergebene Enable-Variable kann mit der Methode SetCheck bearbeitet werden. Die Enable-Funktion (hier CeOption) wird jedesmal aufgerufen, wenn das Menü aufgeklappt wird.

DEFINE_RESPONSE_TABLE1(tHauptFenster, TWindow)
//Optional: more defines here...
EV_COMMAND(OPTION, Option),
EV_COMMAND_ENABLE(OPTION, CeOption),
//Optional: more defines here...
END_RESPONSE_TABLE;

void tHauptFenster::Option()
// Antwort auf Klicken
{
    FolgeTauschen = !FolgeTauschen;
}

void tHauptFenster::CeOption(TCommandEnabler &ce)
// Pruefung auf Enabled
{
	ce.SetCheck(FolgeTauschen?TRUE:FALSE);
}
FolgeTauschen ist eine boolesche Variable, deren Wert durch das Anklicken verändert wird.

Direkte Behandlung aller Menüereignisse

Soll das Programm alle Ereignisse, die aus dem Menü kommen selbst verwalten, muß zunächst eine Funktion EvCommand geschrieben werden. Damit die Menüs nicht alle disabled sind, muß eine Enable-Funktion EvCommandEnable definiert werden.

    // In der Fensterklasse...


virtual LRESULT EvCommand(unsigned int id, HWND hWndCtl, unsigned int notify);

void EvCommandEnable(TCommandEnabler& e) {e.Enable(1);}

// ...


LRESULT tMainWindow::EvCommand(unsigned int id, HWND, unsigned int)
{
if (id==M_ENDE) {
PostMessage(WM_QUIT);
} else {
switch(id) {
// klassische Ereignisbehandlung
case M_FILEOPEN:
break;
// ...
default:
}
}
}


INI-Dateien


Prinzip

In der INI-Datei werden systemweite Daten für das Programm abgelegt. Dabei ist die OS2.INI bzw. die WIN.INI die Datei, die Informationen zwischen den Applikationen austauschen soll. Will ein Programm nur seine eigenen Informationen ablegen, verwendet es eine INI-Datei mit eigenem Namen ohne Pfad.

Um die Datei festzulegen, wird ein Objekt der Klasse TProfile angelegt. Dessen Parameter enthält den Namen der Datei oder keinen, wenn mit der System-INI gearbeitet werden soll.

#include <owl\profile.h>

 . . .
TProfile Profile(DateiName);


Profile.GetString(Kategorie, Schluessel, Wert, 80, "default");
Profile.WriteString(Kategorie, Schluessel, Wert);

Analoge Aufrufe gibt es für Integer statt String-Werte.


Unterschiede in den Parametern zwischen OS/2 und Windows

Bedauerlicherweise sind die Parameter zwischen Windows und OS/2 nicht gleich. Dabei ist der Parameter für die Kategorie bei OS/2 vom TProfile in den WriteString gerutscht.


#ifdef __OS2__
TProfile Profile(DateiName);

Profile.WriteString(Kategorie, Schluessel, Wert);
#else
TProfile Profile(Kategorie, DateiName);

Profile.WriteString(Schluessel, Wert);
#endif

OWL for OS/2


Eine WinHelp()-Funktion für OS/2

In der OWL für OS/2 ist die Hilfe leider nicht mehr realisiert worden. Die Header liegen unvollständig vor und die Funktion TWindow::WinHelp() ist nicht realisiert.

Beides ist kein großer Aufwand.


PMWINDOW.H
#define HELP_CONTEXT 0x223 // HM_EXT_HELP (oder HM_KEYS_HELP?)
#define HELP_QUIT 0x231 // special handling for OS/2
#define HELP_INDEX 0x22a // HM_HELP_INDEX
#define HELP_CONTENTS 0x22b // HM_HELP_CONTENTS
#define HELP_HELPONHELP 0x22f
#define HELP_SETINDEX 0x
#define HELP_SETCONTENTS 0x
#define HELP_CONTEXTPOPUP 0x
#define HELP_FORCEFILE 0x
#define HELP_KEY 0x22c // HM_HELP_KEYS
#define HELP_COMMAND 0x
#define HELP_PARTIALKEY 0x230 // HM_HELP_INDEX
#define HELP_MULTIKEY 0x232
#define HELP_SETWINPOS 0x


Die Funktion TWindows::WinHelp muß geschrieben werden:
 
/* Ergaenzung der OS/2-OWL um die WinHelp-Funktion.
* wurde leider von Borland vollstaendig weggelassen.
*
* (C) Arnold Willemer 1996
* der Firma Tacoss zur freien Verfuegung ueberlassen.
*/

// Verbindung zur OS/2 Hilfe schlagen
#define INCL_WINHELP
#include

// Einbindung in die TWindow-Klasse von OWL
#include (owl\window.h)

// gibt es eine Hilfe? Wenn nicht, initialisieren. Dann ist HwndHilfe das Handle des Help-Kontexts
static HWND HwndHilfe = 0;

// Windows sendet HELP_QUIT und gut. OS/2 macht eine richtige Abmeldung des Help-Kontexts
static void CloseHelp(void)
{
	// Hilfe freigeben
	WinDestroyHelpInstance(HwndHilfe);
	HwndHilfe = 0;
}

// Ergaenze die TWindow-Klasse um WinHelp

BOOL TWindow ::WinHelp(const char * helpFile, unsigned int command, unsigned long data)
{
HELPINIT HelpInit;

if (HwndHilfe==0) {
// Initialierung der Hilfe
HelpInit.cb = sizeof(HELPINIT);
HelpInit.pszTutorialName=0;
HelpInit.phtHelpTable=(PHELPTABLE)MAKEULONG
(1,0xFFFF); // wer sollte das sein?
HelpInit.hmodHelpTableModule=0;
HelpInit.hmodAccelActionBarModule=0;
HelpInit.idAccelTable=0;
HelpInit.idActionBar=0;
HelpInit.pszHelpWindowTitle="Hilfe";
HelpInit.fShowPanelId=CMIC_HIDE_PANEL_ID;
HelpInit.pszHelpLibraryName=(char *)helpFile;
HwndHilfe = WinCreateHelpInstance(::WinQueryAnchorBlock(HWindow), &HelpInit);
if (HwndHilfe && (HelpInit.ulReturnCode==0)) {
WinAssociateHelpInstance(HwndHilfe, Parent->HWindow);
} else if (command!=HELP_QUIT) {
char message[80];
sprintf(message, "Hilfesystem nicht ok (%d)", HelpInit.ulReturnCode);
WinMessageBox(HWND_DESKTOP, HWindow, message, "Achtung", 0, MB_NOICON|MB_OK);
}
} // if (HwndHilfe==0)
// Absetzen der Nachricht
switch (command) {
case HELP_HELPONHELP:
WinSendMsg(HwndHilfe, HM_DISPLAY_HELP, 0, 0); // causes Using Help
break;
case HELP_KEY:
WinSendMsg(HwndHilfe, HM_DISPLAY_HELP, (char *)data, (void *)HM_PANELNAME);
break;
case HELP_PARTIALKEY: // fhrt unter Windows zum Suchen
case HELP_INDEX:
WinSendMsg(HwndHilfe, HM_HELP_INDEX, (void *)0, (void *)0);
break;
case HELP_CONTENTS:
WinSendMsg(HwndHilfe, HM_HELP_CONTENTS, (void *)0, (void *)0);
break;
case HELP_QUIT:
CloseHelp();
break;
case HELP_CONTEXT:
char Msg[80];
sprintf(Msg, "Kontext-Nr %d", data);
WinMessageBox(HWND_DESKTOP, HWindow, Msg, "HELP_CONTEXT", 0, MB_OK);
break;
default:
WinMessageBox(HWND_DESKTOP, HWindow, "no help type realized by owl", "Sorry", 0, MB_OK);
break;
}
}

 

OWL - Listview-Kontrollelemente unter Win32

Das ListView ist mit Win32 erschienen. In der OS/2 OWL ist ein solches Element nicht vorgesehen, obwohl es mit dem Container-Element vermutlich zu realisieren wäre.

Zunächst muß der Header eingebunden werden.

#include <owl\listwind.h>

Es wird die Klasse für das Fenster definiert, das das Listview-Element aufnehmen soll.

class tTaskListWin: public TMDIChild
{
public:
	// Konstruktor
	tTaskListWin(TMDIClient &AParent, char *title);

	...
	void ColClick(TLwNotify& nmHdr);
	TListWindow *Liste;
private:
	DECLARE_RESPONSE_TABLE(tTaskListWin);
};

DEFINE_RESPONSE_TABLE1(tTaskListWin, TMDIChild)
	EV_LVN_DELETEITEM(LIST_ID, DelItem),
	EV_LVN_COLUMNCLICK(LIST_ID, ColClick),
END_RESPONSE_TABLE;

EV_LVN_COLUMNCLICK ist das Ereignis, das auftritt, wenn in der Spalte geklickt wird. Es ruft die Funktion ColClick.

Das Erzeugen des Elements im Konstruktor erfolgen.


tTaskListWin::tTaskListWin(TMDIClient &AParent, char *title)
	 : TMDIChild(AParent, title)
{
	Liste = new TListWindow(this, LIST_ID, 10, 10, 100, 100);
	Liste->Attr.Style |= LVS_REPORT;

Mit LVS_REPORT wird festgelegt, daß es sich um eine Detailansicht handelt. Anschließend müssen die Spalten definiert werden, da man ohne Spalten nichts sieht. Die Spalten können erst angelegt werden, wenn das Kontrollelement erzeugt wurde (Create). Dieser Moment ist im OWL erst im SetupWindow erreicht.

void tTaskListWin::SetupWindow()
{

	TWindow::SetupWindow();
	TListWindColumn column0("ID", 100);
	Liste->InsertColumn(0, column0);
	TListWindColumn column1("Bezeichnung", 200, TListWindColumn::Left, 1);
	Liste->InsertColumn(1, column1);
	ShowAll(Liste);
}

ShowAll ist die Funktion, die die Liste mit Daten füllt. Sie ruft für jedes gefundene Element die Funktion ShowTask auf:

static ShowTask(TListWindow *Liste, tTask *pTask)
{
char str[256];
int index;

	tTask *newTask = new tTask;
	*newTask = *pTask;
	if (pTask==0) return 0;
	// zunaechst das Element der Hauptspalte
	TListWindItem item(pTask->GetID());
	item.SetItemData((LPARAM)newTask);
	index = Liste->InsertItem(item);
	// Dann die weitere Spalte
	TListWindItem sub(pTask->GetBez(), 1);
	Liste->SetItemText(index, sub);
}

SetItemData erfolgt genau einmal bei der Spalte 0. Damit wird der eigentliche Datenzeiger dem Listenelement zugeordnet.

Um zu erreichen, daß die hinterlegten Daten auch wieder frei gegeben werden, wird die Funktion DelItem definiert, die auf das Ereignis EV_LVN_DELETEITEM reagiert. Nach Doku die einzige Ereignisfunktion, in der das lParam-Element des TLwNotify gültig ist.

void tTaskListWin::DelItem(TLwNotify& nmHdr)
// wird aufgerufen, wenn ein Item geloescht wird
{
tTask *t = (tTask *)nmHdr.lParam;
	delete t;
}

Reaktion auf das Anklicken der Spalten

Wird auf die Spalte eines Reports geklickt, erwartet der Anwender meist die Sortierung der Daten nach dieser Spalte. Um dies zu erreichen, wird eine Klasse von TLwComparator abgeleitet, die in ihrem Member Compare die Strategie der Sortierung festlegt. Dabei gibt Compare nur das Ergebnis des Vergleichs zweier Elemente zurück. Die ersten beiden Parameter sind Zeiger auf die Daten, der dritte ist die Nummer der Spalte.

// ------ Comparator soll die Sortierung ueber die Knoepfe realisieren -------

class TMyLwComparator : public TLwComparator
{
public:
int Compare(uint32 item1, uint32 item2, uint32 lParam) const;
};

int TMyLwComparator::Compare(uint32 item1, uint32 item2, uint32 pSort)
const
{
tTask *t1, *t2;

t1 = (tTask *)item1; t2 = (tTask *)item2;
switch(pSort) {
case 0:
return strcmp(t1->GetID(), t2->GetID());
case 1:
return strcmp(t1->GetBez(), t2->GetBez());
default:
return 0;
}
}

const TMyLwComparator MyCompare;

Eingebunden wird der Comparator durch Aufruf von SortItem im Falle, dass der Headerbutton gedrückt wurde.

void tTaskListWin::ColClick(TLwNotify& nmHdr)
{
	Liste->SortItems(MyCompare, (uint32)nmHdr.iSubItem);
}

Diverse Themen


Timer benutzen

Unter OS/2 und Windows kann ein oder mehrere Timer benutzt werden. Sie sind an die Fenster gebunden. Das Fenster erhält also eine Nachricht (WM_TIMER), wenn ein Timer ein Signal gibt.

Dazu wird zunächst der Timer gestartet. Dies kann von jeder Memberfunktion von TWindow oder einer abgeleiteten Klasse (z. B. auch TDialog) durch Setzen des Timers erfolgen.

	SetTimer(1, 1000);
Der erste Parameter ist die Timer-ID, der zweite ist das Ereignisintervall in msek. Im Beispiel wird also jede Sekunde ein WM_TIMER-Ereignis bei der Fensterfunktion eintreffen, bis der Timer gestoppt wird.

Damit ein Timer-Ereignis behandelt wird, muß eine EvTimer-Funktion eingerichtet werden.

class tInitDialog : public TDialog {
public:
	tInitDialog(TWindow *parent, TResId resID);
	void SetupWindow();
protected:
	void EvTimer(uint);
	DECLARE_RESPONSE_TABLE(tInitDialog);
private:
	int Sekunden;
};

DEFINE_RESPONSE_TABLE1(tInitDialog, TDialog)
EV_WM_TIMER,
END_RESPONSE_TABLE;
Die EvTimer-Funktion erhält als Parameter die Timer-ID. Sie (oder eine andere Memberfunktion der Fensterklasse) kann den Timer durch Aufruf von KillTimer wieder abschalten.
void tInitDialog::EvTimer(uint TimerID)
{
	Sekunden--;
	if (Sekunden<=0) {
		KillTimer(TimerID);
	}
}
Leider definiert Windows den Parametertyp für die Timer-ID als uint, OS/2 dagegen als UINT. Ein kleiner #define am Anfang des Listings sorgt für Portabilität.
#ifdef __OS2__
#define uint UINT
#endif

Laden von Stringtabellen aus der Ressource

Texte sollten der leichteren Internationalisierung in den Ressourcen stehen. Dort liegen sie in einem STRINGTABLE. Man kann sie in einem beliebigen Fenster mit dem einfachen Aufruf laden:

GetApplication()->LoadString(ResID, buffer, len);

Ressource Syntaxunterschiede zwischen OS/2 und Windows

Obwohl sich die Ressource-Dateien eigentlich recht ähnlich sind, unterscheiden sich diese zwischen OS/2 und Windows. Beispiele:
OS/2
BITMAP BMP_LIVE live.bmp
POINTER CUR_MALET malet.cur
ICON ICO_CRITTER critter.ico
Windows
BMP_LIVE BITMAP live.bmp
CUR_MALET CURSOR malet.cur
ICO_CRITTER ICON critter.ico
 

PC/TCP für OS/2

Neben der IBM-Version von TCP/IP gibt es eine Implementierung der Firma ftp-Software namens PC/TCP. Die letzte mir bekannten Version ist die 1.31. Leider sind die IP-Programme zwischen den beiden TCP/IP-Varianten nicht austauschbar. Daher ist es nicht möglich, beispielsweise einen Webbrowser darauf zum Laufen zu bringen.

Installationshinweise

Start der Installation beginnt durch die Eingabe von
a:tcpinst
Das Programm fordert Ziel und Quellverzeichnis an und lädt nacheinander die Installationsdisketten. In den folgenden Dialogen werden Host und Domain angefragt. Des weiteren bis zu vier IP-Nummern, Netzmasken und für jede Schnittstelle der Defaultrouter. Die vier Schnittstellen sind zwei Netzkarten und zwei SLIP-Verbindungen. In den meisten Fällen wird aber nur ein Interface benötigt. Die Nameserver können festgelegt werden und welche Dämonen gestartet werden sollen.

Der Treiber für die Netzkarte muss in die CONFIG.SYS eingebunden werden. Dort hat die Installation einen Hinweis (Put the NDIS DEVICE= Statement here) hinterlassen.

DEVICE=C:\PCTCP\DRV\LE10NDS.OS2

Die PROTOCOL.INI muss editiert werden. Es muss eine neue Sektion für den Treiber gemacht werden. Der Name dieser Sektion wird unter [SOCKET] als bindings eingetragen.

[KARTE]
   DriverName = LE10NDS$

[SOCKET]
   DriverName = $SOCKET
   bindings = KARTE

Bei manchen Karten werden noch weitere Parameter benötigt. Dies ist vom Kartentyp abhängig. Beispielsweise:

[ULTRA]
   DRIVERNAME = SMC8000$
   IOBASE = 0x300
   RAMADDRESS = 0xCC00
   IRQ = 10

[SOCKET]
  DRIVERNAME = $SOCKET
  BINDINGS   = ULTRA

Die Schlüsselworte IOBASE, RAMADDRESS, IRQ und auch der DRIVERNAME können bei verschiedenen Karten anders lauten. Beim Treiber ist normalerweise eine ASCII-Datei mit der Endung NIF dabei, der man die Werte leicht entnehmen kann.

Druckdienst lpd

Der Druckdienst lpd wird bei PC/TCP nicht beim Booten des Rechners gestartet, auch wenn dies bei der Installation angegeben wird. Man kann dies leicht von Hand nachtragen, indem man in der \PCTCP\BIN\PCTCP.CMD die Zeile angibt:
minstart lpd
Wichtig ist, dass der lpd ein eigenes Fenster erhält. Ansonsten hat man Performance-Probleme. Diese hat man auch, wenn man in der PCTCP.INI hinter der Schnittstelle nicht das Flag /REDIRECTED angibt. Insbesondere, wenn der Rechner häufiger drucken soll, macht es Sinn, den Druck interruptgesteuert laufen zu lassen.

Ebenfalls in der PCTCP.INI sollte man kontrollieren, ob dort ein Init-String angegeben ist. Bei der Installation wird dort ein String vorgelegt, der explizit gelöscht werden muss, ansonsten versucht das System jedesmal nach dem Booten, diesen String an den Drucker zu senden. Das führt zu einer entsprechenden Fehlermeldung, wenn der Drucker ausgeschaltet ist oder gar keiner vorhanden ist.

Beispiel:

[pctcp lpd]
        printers=lp
[pctcp lp]
        Printer-Init=
        dev=LPT1/redirected
        break=off
        formfeed=off
[general]
        user=willemer

Der Parameter break schaltet das Ausdrucken einer Banner-Page aus. Der Parameter formfeed verhindert einen zusätzlichen Seitenvorschub am Ende des Ausdrucks durch das pc/tcp. Eine Leerseite am Ende eines Ausdrucks kann aber auch durch den normalen Druckertreiber von OS/2 verursacht sein.

Passwortdatei passwd

Im Gegensatz zum IBM-TCP/IP verwendet PC/TCP eine Passwortdatei, die sogar kompatibel zur /etc/passwd von Berkley-UNIX-Systemen ist. In der Datei \PCTCP\ETC\PASSWD steht ein Dummyeintrag, den man kopieren und anpassen kann. Das Passwortfeld kann man freilassen. Es wird durch den passwd-Befehl gefüllt.

Fehler

telnet-Service

Nach dem Einloggen per telnet auf einem OS/2-Rechner darf man keinesfalls wiederum mit ein telnet starten. In diesem Fall hängt sich die Sitzung komplett auf. Allgemein passiert es hin und wieder, insbesondere wenn man schnell tippt und viele Sondertasten verwendet, dass eine telnet-Sitzung hängt.

NFS

In der Version 1.31 gibt es einen Fehler, dass der NFS-Client nicht mit Dateien zurechtkommt, die zwar von der Gruppe zugreifbar sein müssten, aber keine Berechtigung des Users haben. Es ist an dieser Stelle sogar zum Löschen von Dateien gekommen.


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04508
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06246
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1154
Comments: 0