~mhall119/ubuntu/precise/geany/add_keywords

« back to all changes in this revision

Viewing changes to scintilla/gtk/PlatGTK.cxx

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2011-12-10 07:43:26 UTC
  • mfrom: (3.3.7 sid)
  • Revision ID: package-import@ubuntu.com-20111210074326-s8yqbew5i20h33tf
Tags: 0.21-1ubuntu1
* Merge from Debian Unstable, remaining changes:
  - debian/patches/20_use_evince_viewer.patch:
     + use evince as viewer for pdf and dvi files
  - debian/patches/20_use_x_terminal_emulator.patch:
     + use x-terminal-emulator as terminal
  - debian/control
     + Add breaks on geany-plugins-common << 0.20
* Also fixes bugs:
  - Filter for MATLAB/Octave files filters everythign (LP: 885505)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Scintilla source code edit control
 
2
// PlatGTK.cxx - implementation of platform facilities on GTK+/Linux
 
3
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
 
4
// The License.txt file describes the conditions under which this software may be distributed.
 
5
 
 
6
#include <string.h>
 
7
#include <stdio.h>
 
8
#include <stdlib.h>
 
9
#include <stddef.h>
 
10
#include <math.h>
 
11
 
 
12
#include <glib.h>
 
13
#include <gmodule.h>
 
14
#include <gdk/gdk.h>
 
15
#include <gtk/gtk.h>
 
16
#include <gdk/gdkkeysyms.h>
 
17
 
 
18
#include "Platform.h"
 
19
 
 
20
#include "Scintilla.h"
 
21
#include "ScintillaWidget.h"
 
22
#include "UniConversion.h"
 
23
#include "XPM.h"
 
24
 
 
25
/* GLIB must be compiled with thread support, otherwise we
 
26
   will bail on trying to use locks, and that could lead to
 
27
   problems for someone.  `glib-config --libs gthread` needs
 
28
   to be used to get the glib libraries for linking, otherwise
 
29
   g_thread_init will fail */
 
30
#define USE_LOCK defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
 
31
/* Use fast way of getting char data on win32 to work around problems
 
32
   with gdk_string_extents. */
 
33
#define FAST_WAY
 
34
 
 
35
#include "Converter.h"
 
36
 
 
37
#if GTK_CHECK_VERSION(2,20,0)
 
38
#define IS_WIDGET_FOCUSSED(w) (gtk_widget_has_focus(GTK_WIDGET(w)))
 
39
#else
 
40
#define IS_WIDGET_FOCUSSED(w) (GTK_WIDGET_HAS_FOCUS(w))
 
41
#endif
 
42
 
 
43
#if GTK_CHECK_VERSION(2,22,0)
 
44
#define USE_CAIRO 1
 
45
#endif
 
46
 
 
47
#ifdef USE_CAIRO
 
48
#define DISABLE_GDK_FONT 1
 
49
#endif
 
50
 
 
51
#ifdef _MSC_VER
 
52
// Ignore unreferenced local functions in GTK+ headers
 
53
#pragma warning(disable: 4505)
 
54
#endif
 
55
 
 
56
#ifdef SCI_NAMESPACE
 
57
using namespace Scintilla;
 
58
#endif
 
59
 
 
60
enum encodingType { singleByte, UTF8, dbcs};
 
61
 
 
62
struct LOGFONT {
 
63
        int size;
 
64
        bool bold;
 
65
        bool italic;
 
66
        int characterSet;
 
67
        char faceName[300];
 
68
};
 
69
 
 
70
#if USE_LOCK
 
71
static GMutex *fontMutex = NULL;
 
72
 
 
73
static void InitializeGLIBThreads() {
 
74
        if (!g_thread_supported()) {
 
75
                g_thread_init(NULL);
 
76
        }
 
77
}
 
78
#endif
 
79
 
 
80
static void FontMutexAllocate() {
 
81
#if USE_LOCK
 
82
        if (!fontMutex) {
 
83
                InitializeGLIBThreads();
 
84
                fontMutex = g_mutex_new();
 
85
        }
 
86
#endif
 
87
}
 
88
 
 
89
static void FontMutexFree() {
 
90
#if USE_LOCK
 
91
        if (fontMutex) {
 
92
                g_mutex_free(fontMutex);
 
93
                fontMutex = NULL;
 
94
        }
 
95
#endif
 
96
}
 
97
 
 
98
static void FontMutexLock() {
 
99
#if USE_LOCK
 
100
        g_mutex_lock(fontMutex);
 
101
#endif
 
102
}
 
103
 
 
104
static void FontMutexUnlock() {
 
105
#if USE_LOCK
 
106
        if (fontMutex) {
 
107
                g_mutex_unlock(fontMutex);
 
108
        }
 
109
#endif
 
110
}
 
111
 
 
112
// On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
 
113
// PangoFontDescription*.
 
114
class FontHandle {
 
115
        int width[128];
 
116
        encodingType et;
 
117
public:
 
118
        int ascent;
 
119
        GdkFont *pfont;
 
120
        PangoFontDescription *pfd;
 
121
        int characterSet;
 
122
        FontHandle(GdkFont *pfont_) {
 
123
                et = singleByte;
 
124
                ascent = 0;
 
125
                pfont = pfont_;
 
126
                pfd = 0;
 
127
                characterSet = -1;
 
128
                ResetWidths(et);
 
129
        }
 
130
        FontHandle(PangoFontDescription *pfd_, int characterSet_) {
 
131
                et = singleByte;
 
132
                ascent = 0;
 
133
                pfont = 0;
 
134
                pfd = pfd_;
 
135
                characterSet = characterSet_;
 
136
                ResetWidths(et);
 
137
        }
 
138
        ~FontHandle() {
 
139
#ifndef DISABLE_GDK_FONT
 
140
                if (pfont)
 
141
                        gdk_font_unref(pfont);
 
142
#endif
 
143
                pfont = 0;
 
144
                if (pfd)
 
145
                        pango_font_description_free(pfd);
 
146
                pfd = 0;
 
147
        }
 
148
        void ResetWidths(encodingType et_) {
 
149
                et = et_;
 
150
                for (int i=0; i<=127; i++) {
 
151
                        width[i] = 0;
 
152
                }
 
153
        }
 
154
        int CharWidth(unsigned char ch, encodingType et_) {
 
155
                int w = 0;
 
156
                FontMutexLock();
 
157
                if ((ch <= 127) && (et == et_)) {
 
158
                        w = width[ch];
 
159
                }
 
160
                FontMutexUnlock();
 
161
                return w;
 
162
        }
 
163
        void SetCharWidth(unsigned char ch, int w, encodingType et_) {
 
164
                if (ch <= 127) {
 
165
                        FontMutexLock();
 
166
                        if (et != et_) {
 
167
                                ResetWidths(et_);
 
168
                        }
 
169
                        width[ch] = w;
 
170
                        FontMutexUnlock();
 
171
                }
 
172
        }
 
173
};
 
174
 
 
175
// X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
 
176
static const int maxCoordinate = 32000;
 
177
 
 
178
static FontHandle *PFont(Font &f) {
 
179
        return reinterpret_cast<FontHandle *>(f.GetID());
 
180
}
 
181
 
 
182
static GtkWidget *PWidget(WindowID wid) {
 
183
        return reinterpret_cast<GtkWidget *>(wid);
 
184
}
 
185
 
 
186
static GtkWidget *PWidget(Window &w) {
 
187
        return PWidget(w.GetID());
 
188
}
 
189
 
 
190
Point Point::FromLong(long lpoint) {
 
191
        return Point(
 
192
                   Platform::LowShortFromLong(lpoint),
 
193
                   Platform::HighShortFromLong(lpoint));
 
194
}
 
195
 
 
196
Palette::Palette() {
 
197
        used = 0;
 
198
        allowRealization = false;
 
199
        allocatedPalette = 0;
 
200
        allocatedLen = 0;
 
201
        size = 100;
 
202
        entries = new ColourPair[size];
 
203
}
 
204
 
 
205
Palette::~Palette() {
 
206
        Release();
 
207
        delete []entries;
 
208
        entries = 0;
 
209
}
 
210
 
 
211
void Palette::Release() {
 
212
        used = 0;
 
213
        delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
 
214
        allocatedPalette = 0;
 
215
        allocatedLen = 0;
 
216
        delete []entries;
 
217
        size = 100;
 
218
        entries = new ColourPair[size];
 
219
}
 
220
 
 
221
// This method either adds a colour to the list of wanted colours (want==true)
 
222
// or retrieves the allocated colour back to the ColourPair.
 
223
// This is one method to make it easier to keep the code for wanting and retrieving in sync.
 
224
void Palette::WantFind(ColourPair &cp, bool want) {
 
225
        if (want) {
 
226
                for (int i=0; i < used; i++) {
 
227
                        if (entries[i].desired == cp.desired)
 
228
                                return;
 
229
                }
 
230
 
 
231
                if (used >= size) {
 
232
                        int sizeNew = size * 2;
 
233
                        ColourPair *entriesNew = new ColourPair[sizeNew];
 
234
                        for (int j=0; j<size; j++) {
 
235
                                entriesNew[j] = entries[j];
 
236
                        }
 
237
                        delete []entries;
 
238
                        entries = entriesNew;
 
239
                        size = sizeNew;
 
240
                }
 
241
 
 
242
                entries[used].desired = cp.desired;
 
243
                entries[used].allocated.Set(cp.desired.AsLong());
 
244
                used++;
 
245
        } else {
 
246
                for (int i=0; i < used; i++) {
 
247
                        if (entries[i].desired == cp.desired) {
 
248
                                cp.allocated = entries[i].allocated;
 
249
                                return;
 
250
                        }
 
251
                }
 
252
                cp.allocated.Set(cp.desired.AsLong());
 
253
        }
 
254
}
 
255
 
 
256
void Palette::Allocate(Window &w) {
 
257
        if (allocatedPalette) {
 
258
                gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)),
 
259
                                         reinterpret_cast<GdkColor *>(allocatedPalette),
 
260
                                         allocatedLen);
 
261
                delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
 
262
                allocatedPalette = 0;
 
263
                allocatedLen = 0;
 
264
        }
 
265
        GdkColor *paletteNew = new GdkColor[used];
 
266
        allocatedPalette = paletteNew;
 
267
        gboolean *successPalette = new gboolean[used];
 
268
        if (paletteNew) {
 
269
                allocatedLen = used;
 
270
                int iPal = 0;
 
271
                for (iPal = 0; iPal < used; iPal++) {
 
272
                        paletteNew[iPal].red = entries[iPal].desired.GetRed() * (65535 / 255);
 
273
                        paletteNew[iPal].green = entries[iPal].desired.GetGreen() * (65535 / 255);
 
274
                        paletteNew[iPal].blue = entries[iPal].desired.GetBlue() * (65535 / 255);
 
275
                        paletteNew[iPal].pixel = entries[iPal].desired.AsLong();
 
276
                }
 
277
                gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w)),
 
278
                                          paletteNew, allocatedLen, FALSE, TRUE,
 
279
                                          successPalette);
 
280
                for (iPal = 0; iPal < used; iPal++) {
 
281
                        entries[iPal].allocated.Set(paletteNew[iPal].pixel);
 
282
                }
 
283
        }
 
284
        delete []successPalette;
 
285
}
 
286
 
 
287
#ifndef DISABLE_GDK_FONT
 
288
 
 
289
static const char *CharacterSetName(int characterSet) {
 
290
        switch (characterSet) {
 
291
        case SC_CHARSET_ANSI:
 
292
                return "iso8859-*";
 
293
        case SC_CHARSET_DEFAULT:
 
294
                return "iso8859-*";
 
295
        case SC_CHARSET_BALTIC:
 
296
                return "iso8859-13";
 
297
        case SC_CHARSET_CHINESEBIG5:
 
298
                return "*-*";
 
299
        case SC_CHARSET_EASTEUROPE:
 
300
                return "*-2";
 
301
        case SC_CHARSET_GB2312:
 
302
                return "gb2312.1980-*";
 
303
        case SC_CHARSET_GREEK:
 
304
                return "*-7";
 
305
        case SC_CHARSET_HANGUL:
 
306
                return "ksc5601.1987-*";
 
307
        case SC_CHARSET_MAC:
 
308
                return "*-*";
 
309
        case SC_CHARSET_OEM:
 
310
                return "*-*";
 
311
        case SC_CHARSET_RUSSIAN:
 
312
                return "*-r";
 
313
        case SC_CHARSET_CYRILLIC:
 
314
                return "*-cp1251";
 
315
        case SC_CHARSET_SHIFTJIS:
 
316
                return "jisx0208.1983-*";
 
317
        case SC_CHARSET_SYMBOL:
 
318
                return "*-*";
 
319
        case SC_CHARSET_TURKISH:
 
320
                return "*-9";
 
321
        case SC_CHARSET_JOHAB:
 
322
                return "*-*";
 
323
        case SC_CHARSET_HEBREW:
 
324
                return "*-8";
 
325
        case SC_CHARSET_ARABIC:
 
326
                return "*-6";
 
327
        case SC_CHARSET_VIETNAMESE:
 
328
                return "*-*";
 
329
        case SC_CHARSET_THAI:
 
330
                return "iso8859-11";
 
331
        case SC_CHARSET_8859_15:
 
332
                return "iso8859-15";
 
333
        default:
 
334
                return "*-*";
 
335
        }
 
336
}
 
337
 
 
338
static bool IsDBCSCharacterSet(int characterSet) {
 
339
        switch (characterSet) {
 
340
        case SC_CHARSET_GB2312:
 
341
        case SC_CHARSET_HANGUL:
 
342
        case SC_CHARSET_SHIFTJIS:
 
343
        case SC_CHARSET_CHINESEBIG5:
 
344
                return true;
 
345
        default:
 
346
                return false;
 
347
        }
 
348
}
 
349
 
 
350
static void GenerateFontSpecStrings(const char *fontName, int characterSet,
 
351
                                    char *foundary, int foundary_len,
 
352
                                    char *faceName, int faceName_len,
 
353
                                    char *charset, int charset_len) {
 
354
        // supported font strings include:
 
355
        // foundary-fontface-isoxxx-x
 
356
        // fontface-isoxxx-x
 
357
        // foundary-fontface
 
358
        // fontface
 
359
        if (strchr(fontName, '-')) {
 
360
                char tmp[300];
 
361
                char *d1 = NULL, *d2 = NULL, *d3 = NULL;
 
362
                strncpy(tmp, fontName, sizeof(tmp) - 1);
 
363
                tmp[sizeof(tmp) - 1] = '\0';
 
364
                d1 = strchr(tmp, '-');
 
365
                // we know the first dash exists
 
366
                d2 = strchr(d1 + 1, '-');
 
367
                if (d2)
 
368
                        d3 = strchr(d2 + 1, '-');
 
369
                if (d3 && d2) {
 
370
                        // foundary-fontface-isoxxx-x
 
371
                        *d2 = '\0';
 
372
                        foundary[0] = '-';
 
373
                        foundary[1] = '\0';
 
374
                        strncpy(faceName, tmp, foundary_len - 1);
 
375
                        strncpy(charset, d2 + 1, charset_len - 1);
 
376
                } else if (d2) {
 
377
                        // fontface-isoxxx-x
 
378
                        *d1 = '\0';
 
379
                        strcpy(foundary, "-*-");
 
380
                        strncpy(faceName, tmp, faceName_len - 1);
 
381
                        strncpy(charset, d1 + 1, charset_len - 1);
 
382
                } else {
 
383
                        // foundary-fontface
 
384
                        foundary[0] = '-';
 
385
                        foundary[1] = '\0';
 
386
                        strncpy(faceName, tmp, faceName_len - 1);
 
387
                        strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
 
388
                }
 
389
        } else {
 
390
                strncpy(foundary, "-*-", foundary_len);
 
391
                strncpy(faceName, fontName, faceName_len - 1);
 
392
                strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
 
393
        }
 
394
}
 
395
 
 
396
#endif
 
397
 
 
398
static void SetLogFont(LOGFONT &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
 
399
        memset(&lf, 0, sizeof(lf));
 
400
        lf.size = size;
 
401
        lf.bold = bold;
 
402
        lf.italic = italic;
 
403
        lf.characterSet = characterSet;
 
404
        strncpy(lf.faceName, faceName, sizeof(lf.faceName) - 1);
 
405
}
 
406
 
 
407
/**
 
408
 * Create a hash from the parameters for a font to allow easy checking for identity.
 
409
 * If one font is the same as another, its hash will be the same, but if the hash is the
 
410
 * same then they may still be different.
 
411
 */
 
412
static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
 
413
        return
 
414
            size ^
 
415
            (characterSet << 10) ^
 
416
            (bold ? 0x10000000 : 0) ^
 
417
            (italic ? 0x20000000 : 0) ^
 
418
            faceName[0];
 
419
}
 
420
 
 
421
class FontCached : Font {
 
422
        FontCached *next;
 
423
        int usage;
 
424
        LOGFONT lf;
 
425
        int hash;
 
426
        FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
 
427
        ~FontCached() {}
 
428
        bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
 
429
        virtual void Release();
 
430
        static FontID CreateNewFont(const char *fontName, int characterSet,
 
431
                                    int size, bool bold, bool italic);
 
432
        static FontCached *first;
 
433
public:
 
434
        static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
 
435
        static void ReleaseId(FontID fid_);
 
436
};
 
437
 
 
438
FontCached *FontCached::first = 0;
 
439
 
 
440
FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
 
441
next(0), usage(0), hash(0) {
 
442
        ::SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
 
443
        hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
 
444
        fid = CreateNewFont(faceName_, characterSet_, size_, bold_, italic_);
 
445
        usage = 1;
 
446
}
 
447
 
 
448
bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
 
449
        return
 
450
            lf.size == size_ &&
 
451
            lf.bold == bold_ &&
 
452
            lf.italic == italic_ &&
 
453
            lf.characterSet == characterSet_ &&
 
454
            0 == strcmp(lf.faceName, faceName_);
 
455
}
 
456
 
 
457
void FontCached::Release() {
 
458
        if (fid)
 
459
                delete PFont(*this);
 
460
        fid = 0;
 
461
}
 
462
 
 
463
FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
 
464
        FontID ret = 0;
 
465
        FontMutexLock();
 
466
        int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
 
467
        for (FontCached *cur = first; cur; cur = cur->next) {
 
468
                if ((cur->hash == hashFind) &&
 
469
                        cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
 
470
                        cur->usage++;
 
471
                        ret = cur->fid;
 
472
                }
 
473
        }
 
474
        if (ret == 0) {
 
475
                FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
 
476
                if (fc) {
 
477
                        fc->next = first;
 
478
                        first = fc;
 
479
                        ret = fc->fid;
 
480
                }
 
481
        }
 
482
        FontMutexUnlock();
 
483
        return ret;
 
484
}
 
485
 
 
486
void FontCached::ReleaseId(FontID fid_) {
 
487
        FontMutexLock();
 
488
        FontCached **pcur = &first;
 
489
        for (FontCached *cur = first; cur; cur = cur->next) {
 
490
                if (cur->fid == fid_) {
 
491
                        cur->usage--;
 
492
                        if (cur->usage == 0) {
 
493
                                *pcur = cur->next;
 
494
                                cur->Release();
 
495
                                cur->next = 0;
 
496
                                delete cur;
 
497
                        }
 
498
                        break;
 
499
                }
 
500
                pcur = &cur->next;
 
501
        }
 
502
        FontMutexUnlock();
 
503
}
 
504
 
 
505
#ifndef DISABLE_GDK_FONT
 
506
static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
 
507
        if (IsDBCSCharacterSet(characterSet)) {
 
508
                return gdk_fontset_load(fontspec);
 
509
        } else {
 
510
                return gdk_font_load(fontspec);
 
511
        }
 
512
}
 
513
#endif
 
514
 
 
515
FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
 
516
                                 int size, bool bold, bool italic) {
 
517
        if (fontName[0] == '!') {
 
518
                PangoFontDescription *pfd = pango_font_description_new();
 
519
                if (pfd) {
 
520
                        pango_font_description_set_family(pfd, fontName+1);
 
521
                        pango_font_description_set_size(pfd, size * PANGO_SCALE);
 
522
                        pango_font_description_set_weight(pfd, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
 
523
                        pango_font_description_set_style(pfd, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
 
524
                        return new FontHandle(pfd, characterSet);
 
525
                }
 
526
        }
 
527
 
 
528
#ifndef DISABLE_GDK_FONT
 
529
        char fontset[1024];
 
530
        char fontspec[300];
 
531
        char foundary[50];
 
532
        char faceName[100];
 
533
        char charset[50];
 
534
        fontset[0] = '\0';
 
535
        fontspec[0] = '\0';
 
536
        foundary[0] = '\0';
 
537
        faceName[0] = '\0';
 
538
        charset[0] = '\0';
 
539
 
 
540
        GdkFont *newid = 0;
 
541
        // If name of the font begins with a '-', assume, that it is
 
542
        // a full fontspec.
 
543
        if (fontName[0] == '-') {
 
544
                if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
 
545
                        newid = gdk_fontset_load(fontName);
 
546
                } else {
 
547
                        newid = gdk_font_load(fontName);
 
548
                }
 
549
                if (!newid) {
 
550
                        // Font not available so substitute a reasonable code font
 
551
                        // iso8859 appears to only allow western characters.
 
552
                        newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
 
553
                                characterSet);
 
554
                }
 
555
                return new FontHandle(newid);
 
556
        }
 
557
 
 
558
        // it's not a full fontspec, build one.
 
559
 
 
560
        // This supports creating a FONT_SET
 
561
        // in a method that allows us to also set size, slant and
 
562
        // weight for the fontset.  The expected input is multiple
 
563
        // partial fontspecs seperated by comma
 
564
        // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
 
565
        if (strchr(fontName, ',')) {
 
566
                // build a fontspec and use gdk_fontset_load
 
567
                int remaining = sizeof(fontset);
 
568
                char fontNameCopy[1024];
 
569
                strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1);
 
570
                char *fn = fontNameCopy;
 
571
                char *fp = strchr(fn, ',');
 
572
                for (;;) {
 
573
                        const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
 
574
                        if (fontset[0] != '\0') {
 
575
                                // if this is not the first font in the list,
 
576
                                // append a comma seperator
 
577
                                spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
 
578
                        }
 
579
 
 
580
                        if (fp)
 
581
                                *fp = '\0'; // nullify the comma
 
582
                        GenerateFontSpecStrings(fn, characterSet,
 
583
                                                foundary, sizeof(foundary),
 
584
                                                faceName, sizeof(faceName),
 
585
                                                charset, sizeof(charset));
 
586
 
 
587
                        g_snprintf(fontspec,
 
588
                                 sizeof(fontspec) - 1,
 
589
                                 spec,
 
590
                                 foundary, faceName,
 
591
                                 bold ? "-bold" : "-medium",
 
592
                                 italic ? "-i" : "-r",
 
593
                                 size * 10,
 
594
                                 charset);
 
595
 
 
596
                        // if this is the first font in the list, and
 
597
                        // we are doing italic, add an oblique font
 
598
                        // to the list
 
599
                        if (italic && fontset[0] == '\0') {
 
600
                                strncat(fontset, fontspec, remaining - 1);
 
601
                                remaining -= strlen(fontset);
 
602
 
 
603
                                g_snprintf(fontspec,
 
604
                                         sizeof(fontspec) - 1,
 
605
                                         ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
 
606
                                         foundary, faceName,
 
607
                                         bold ? "-bold" : "-medium",
 
608
                                         size * 10,
 
609
                                         charset);
 
610
                        }
 
611
 
 
612
                        strncat(fontset, fontspec, remaining - 1);
 
613
                        remaining -= strlen(fontset);
 
614
 
 
615
                        if (!fp)
 
616
                                break;
 
617
 
 
618
                        fn = fp + 1;
 
619
                        fp = strchr(fn, ',');
 
620
                }
 
621
 
 
622
                newid = gdk_fontset_load(fontset);
 
623
                if (newid)
 
624
                        return new FontHandle(newid);
 
625
                // if fontset load failed, fall through, we'll use
 
626
                // the last font entry and continue to try and
 
627
                // get something that matches
 
628
        }
 
629
 
 
630
        // single fontspec support
 
631
 
 
632
        GenerateFontSpecStrings(fontName, characterSet,
 
633
                                foundary, sizeof(foundary),
 
634
                                faceName, sizeof(faceName),
 
635
                                charset, sizeof(charset));
 
636
 
 
637
        g_snprintf(fontspec,
 
638
                 sizeof(fontspec) - 1,
 
639
                 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
 
640
                 foundary, faceName,
 
641
                 bold ? "-bold" : "-medium",
 
642
                 italic ? "-i" : "-r",
 
643
                 size * 10,
 
644
                 charset);
 
645
        newid = LoadFontOrSet(fontspec, characterSet);
 
646
        if (!newid) {
 
647
                // some fonts have oblique, not italic
 
648
                g_snprintf(fontspec,
 
649
                         sizeof(fontspec) - 1,
 
650
                         "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
 
651
                         foundary, faceName,
 
652
                         bold ? "-bold" : "-medium",
 
653
                         italic ? "-o" : "-r",
 
654
                         size * 10,
 
655
                         charset);
 
656
                newid = LoadFontOrSet(fontspec, characterSet);
 
657
        }
 
658
        if (!newid) {
 
659
                g_snprintf(fontspec,
 
660
                         sizeof(fontspec) - 1,
 
661
                         "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
 
662
                         size * 10,
 
663
                         charset);
 
664
                newid = gdk_font_load(fontspec);
 
665
        }
 
666
        if (!newid) {
 
667
                // Font not available so substitute a reasonable code font
 
668
                // iso8859 appears to only allow western characters.
 
669
                newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
 
670
                        characterSet);
 
671
        }
 
672
        return new FontHandle(newid);
 
673
#else
 
674
        return new FontHandle(0);
 
675
#endif
 
676
}
 
677
 
 
678
Font::Font() : fid(0) {}
 
679
 
 
680
Font::~Font() {}
 
681
 
 
682
void Font::Create(const char *faceName, int characterSet, int size,
 
683
        bool bold, bool italic, int) {
 
684
        Release();
 
685
        fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
 
686
}
 
687
 
 
688
void Font::Release() {
 
689
        if (fid)
 
690
                FontCached::ReleaseId(fid);
 
691
        fid = 0;
 
692
}
 
693
 
 
694
// Required on OS X
 
695
#ifdef SCI_NAMESPACE
 
696
namespace Scintilla {
 
697
#endif
 
698
class SurfaceImpl : public Surface {
 
699
        encodingType et;
 
700
#ifdef USE_CAIRO
 
701
        cairo_t *context;
 
702
        cairo_surface_t *psurf;
 
703
#else
 
704
        GdkDrawable *drawable;
 
705
        GdkGC *gc;
 
706
        GdkPixmap *ppixmap;
 
707
#endif
 
708
        int x;
 
709
        int y;
 
710
        bool inited;
 
711
        bool createdGC;
 
712
        PangoContext *pcontext;
 
713
        PangoLayout *layout;
 
714
        Converter conv;
 
715
        int characterSet;
 
716
        void SetConverter(int characterSet_);
 
717
public:
 
718
        SurfaceImpl();
 
719
        virtual ~SurfaceImpl();
 
720
 
 
721
        void Init(WindowID wid);
 
722
        void Init(SurfaceID sid, WindowID wid);
 
723
        void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
 
724
 
 
725
        void Release();
 
726
        bool Initialised();
 
727
        void PenColour(ColourAllocated fore);
 
728
        int LogPixelsY();
 
729
        int DeviceHeightFont(int points);
 
730
        void MoveTo(int x_, int y_);
 
731
        void LineTo(int x_, int y_);
 
732
        void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
 
733
        void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
 
734
        void FillRectangle(PRectangle rc, ColourAllocated back);
 
735
        void FillRectangle(PRectangle rc, Surface &surfacePattern);
 
736
        void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
 
737
        void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
 
738
                ColourAllocated outline, int alphaOutline, int flags);
 
739
        void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
 
740
        void Copy(PRectangle rc, Point from, Surface &surfaceSource);
 
741
 
 
742
        void DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
 
743
        void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
 
744
        void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
 
745
        void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
 
746
        void MeasureWidths(Font &font_, const char *s, int len, int *positions);
 
747
        int WidthText(Font &font_, const char *s, int len);
 
748
        int WidthChar(Font &font_, char ch);
 
749
        int Ascent(Font &font_);
 
750
        int Descent(Font &font_);
 
751
        int InternalLeading(Font &font_);
 
752
        int ExternalLeading(Font &font_);
 
753
        int Height(Font &font_);
 
754
        int AverageCharWidth(Font &font_);
 
755
 
 
756
        int SetPalette(Palette *pal, bool inBackGround);
 
757
        void SetClip(PRectangle rc);
 
758
        void FlushCachedState();
 
759
 
 
760
        void SetUnicodeMode(bool unicodeMode_);
 
761
        void SetDBCSMode(int codePage);
 
762
};
 
763
#ifdef SCI_NAMESPACE
 
764
}
 
765
#endif
 
766
 
 
767
const char *CharacterSetID(int characterSet) {
 
768
        switch (characterSet) {
 
769
        case SC_CHARSET_ANSI:
 
770
                return "";
 
771
        case SC_CHARSET_DEFAULT:
 
772
                return "ISO-8859-1";
 
773
        case SC_CHARSET_BALTIC:
 
774
                return "ISO-8859-13";
 
775
        case SC_CHARSET_CHINESEBIG5:
 
776
                return "BIG-5";
 
777
        case SC_CHARSET_EASTEUROPE:
 
778
                return "ISO-8859-2";
 
779
        case SC_CHARSET_GB2312:
 
780
                return "CP936";
 
781
        case SC_CHARSET_GREEK:
 
782
                return "ISO-8859-7";
 
783
        case SC_CHARSET_HANGUL:
 
784
                return "CP949";
 
785
        case SC_CHARSET_MAC:
 
786
                return "MACINTOSH";
 
787
        case SC_CHARSET_OEM:
 
788
                return "ASCII";
 
789
        case SC_CHARSET_RUSSIAN:
 
790
                return "KOI8-R";
 
791
        case SC_CHARSET_CYRILLIC:
 
792
                return "CP1251";
 
793
        case SC_CHARSET_SHIFTJIS:
 
794
                return "SHIFT-JIS";
 
795
        case SC_CHARSET_SYMBOL:
 
796
                return "";
 
797
        case SC_CHARSET_TURKISH:
 
798
                return "ISO-8859-9";
 
799
        case SC_CHARSET_JOHAB:
 
800
                return "CP1361";
 
801
        case SC_CHARSET_HEBREW:
 
802
                return "ISO-8859-8";
 
803
        case SC_CHARSET_ARABIC:
 
804
                return "ISO-8859-6";
 
805
        case SC_CHARSET_VIETNAMESE:
 
806
                return "";
 
807
        case SC_CHARSET_THAI:
 
808
                return "ISO-8859-11";
 
809
        case SC_CHARSET_8859_15:
 
810
                return "ISO-8859-15";
 
811
        default:
 
812
                return "";
 
813
        }
 
814
}
 
815
 
 
816
void SurfaceImpl::SetConverter(int characterSet_) {
 
817
        if (characterSet != characterSet_) {
 
818
                characterSet = characterSet_;
 
819
                conv.Open("UTF-8", CharacterSetID(characterSet), false);
 
820
        }
 
821
}
 
822
 
 
823
SurfaceImpl::SurfaceImpl() : et(singleByte),
 
824
#ifdef USE_CAIRO
 
825
context(0),
 
826
psurf(0),
 
827
#else
 
828
drawable(0),
 
829
gc(0),
 
830
ppixmap(0),
 
831
#endif
 
832
x(0), y(0), inited(false), createdGC(false)
 
833
, pcontext(0), layout(0), characterSet(-1) {
 
834
}
 
835
 
 
836
SurfaceImpl::~SurfaceImpl() {
 
837
        Release();
 
838
}
 
839
 
 
840
void SurfaceImpl::Release() {
 
841
        et = singleByte;
 
842
#ifndef USE_CAIRO
 
843
        drawable = 0;
 
844
#endif
 
845
        if (createdGC) {
 
846
                createdGC = false;
 
847
#ifdef USE_CAIRO
 
848
                cairo_destroy(context);
 
849
#else
 
850
                g_object_unref(gc);
 
851
#endif
 
852
        }
 
853
#ifdef USE_CAIRO
 
854
        context = 0;
 
855
        if (psurf)
 
856
                cairo_surface_destroy(psurf);
 
857
        psurf = 0;
 
858
#else
 
859
        gc = 0;
 
860
        if (ppixmap)
 
861
                g_object_unref(ppixmap);
 
862
        ppixmap = 0;
 
863
#endif
 
864
        if (layout)
 
865
                g_object_unref(layout);
 
866
        layout = 0;
 
867
        if (pcontext)
 
868
                g_object_unref(pcontext);
 
869
        pcontext = 0;
 
870
        conv.Close();
 
871
        characterSet = -1;
 
872
        x = 0;
 
873
        y = 0;
 
874
        inited = false;
 
875
        createdGC = false;
 
876
}
 
877
 
 
878
bool SurfaceImpl::Initialised() {
 
879
        return inited;
 
880
}
 
881
 
 
882
void SurfaceImpl::Init(WindowID wid) {
 
883
        Release();
 
884
        PLATFORM_ASSERT(wid);
 
885
#ifdef USE_CAIRO
 
886
        GdkDrawable *drawable_ = GDK_DRAWABLE(PWidget(wid)->window);
 
887
        if (drawable_) {
 
888
                context = gdk_cairo_create(drawable_);
 
889
                PLATFORM_ASSERT(context);
 
890
        } else {
 
891
                // Shouldn't happen with valid window but may when calls made before
 
892
                // window completely allocated and mapped.
 
893
                psurf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 1, 1);
 
894
                context = cairo_create(psurf);
 
895
        }
 
896
        createdGC = true;
 
897
#endif
 
898
        pcontext = gtk_widget_create_pango_context(PWidget(wid));
 
899
        PLATFORM_ASSERT(pcontext);
 
900
        layout = pango_layout_new(pcontext);
 
901
        PLATFORM_ASSERT(layout);
 
902
        inited = true;
 
903
}
 
904
 
 
905
void SurfaceImpl::Init(SurfaceID sid, WindowID wid) {
 
906
        PLATFORM_ASSERT(sid);
 
907
        GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
 
908
        Release();
 
909
        PLATFORM_ASSERT(wid);
 
910
#ifdef USE_CAIRO
 
911
        context = gdk_cairo_create(drawable_);
 
912
#else
 
913
        gc = gdk_gc_new(drawable_);
 
914
        drawable = drawable_;
 
915
#endif
 
916
        pcontext = gtk_widget_create_pango_context(PWidget(wid));
 
917
        layout = pango_layout_new(pcontext);
 
918
#ifdef USE_CAIRO
 
919
        cairo_set_line_width(context, 1);
 
920
#else
 
921
        // Ask for lines that do not paint the last pixel so is like Win32
 
922
        gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
 
923
#endif
 
924
        createdGC = true;
 
925
        inited = true;
 
926
}
 
927
 
 
928
void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
 
929
        PLATFORM_ASSERT(surface_);
 
930
        Release();
 
931
        SurfaceImpl *surfImpl = static_cast<SurfaceImpl *>(surface_);
 
932
        PLATFORM_ASSERT(wid);
 
933
#ifdef USE_CAIRO
 
934
        context = cairo_reference(surfImpl->context);
 
935
#else
 
936
        PLATFORM_ASSERT(surfImpl->drawable);
 
937
        gc = gdk_gc_new(surfImpl->drawable);
 
938
#endif
 
939
        pcontext = gtk_widget_create_pango_context(PWidget(wid));
 
940
        PLATFORM_ASSERT(pcontext);
 
941
        layout = pango_layout_new(pcontext);
 
942
        PLATFORM_ASSERT(layout);
 
943
#ifdef USE_CAIRO
 
944
        if (height > 0 && width > 0)
 
945
                psurf = gdk_window_create_similar_surface(
 
946
                        gtk_widget_get_window(PWidget(wid)),
 
947
                        CAIRO_CONTENT_COLOR_ALPHA, width, height);
 
948
#else
 
949
        if (height > 0 && width > 0)
 
950
                ppixmap = gdk_pixmap_new(surfImpl->drawable, width, height, -1);
 
951
        drawable = ppixmap;
 
952
#endif
 
953
#ifdef USE_CAIRO
 
954
        cairo_destroy(context);
 
955
        context = cairo_create(psurf);
 
956
        cairo_rectangle(context, 0, 0, width, height);
 
957
        cairo_set_source_rgb(context, 1.0, 0, 0);
 
958
        cairo_fill(context);
 
959
        // This produces sharp drawing more similar to GDK:
 
960
        //cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
 
961
#endif
 
962
#ifdef USE_CAIRO
 
963
        cairo_set_line_width(context, 1);
 
964
#else
 
965
        // Ask for lines that do not paint the last pixel so is like Win32
 
966
        gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
 
967
#endif
 
968
        createdGC = true;
 
969
        inited = true;
 
970
}
 
971
 
 
972
void SurfaceImpl::PenColour(ColourAllocated fore) {
 
973
#ifdef USE_CAIRO
 
974
        if (context) {
 
975
                ColourDesired cdFore(fore.AsLong());
 
976
                cairo_set_source_rgb(context,
 
977
                        cdFore.GetBlue() / 255.0,
 
978
                        cdFore.GetGreen() / 255.0,
 
979
                        cdFore.GetRed() / 255.0);
 
980
        }
 
981
#else
 
982
        if (gc) {
 
983
                GdkColor co;
 
984
                co.pixel = fore.AsLong();
 
985
                gdk_gc_set_foreground(gc, &co);
 
986
        }
 
987
#endif
 
988
}
 
989
 
 
990
int SurfaceImpl::LogPixelsY() {
 
991
        return 72;
 
992
}
 
993
 
 
994
int SurfaceImpl::DeviceHeightFont(int points) {
 
995
        int logPix = LogPixelsY();
 
996
        return (points * logPix + logPix / 2) / 72;
 
997
}
 
998
 
 
999
void SurfaceImpl::MoveTo(int x_, int y_) {
 
1000
        x = x_;
 
1001
        y = y_;
 
1002
}
 
1003
 
 
1004
#ifdef USE_CAIRO
 
1005
static int Delta(int difference) {
 
1006
        if (difference < 0)
 
1007
                return -1;
 
1008
        else if (difference > 0)
 
1009
                return 1;
 
1010
        else
 
1011
                return 0;
 
1012
}
 
1013
#endif
 
1014
 
 
1015
void SurfaceImpl::LineTo(int x_, int y_) {
 
1016
#ifdef USE_CAIRO
 
1017
        // cairo_line_to draws the end position, unlike Win32 or GDK with GDK_CAP_NOT_LAST.
 
1018
        // For simple cases, move back one pixel from end.
 
1019
        if (context) {
 
1020
                int xDiff = x_ - x;
 
1021
                int xDelta = Delta(xDiff);
 
1022
                int yDiff = y_ - y;
 
1023
                int yDelta = Delta(yDiff);
 
1024
                if ((xDiff == 0) || (yDiff == 0)) {
 
1025
                        // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
 
1026
                        int xEnd = x_ - xDelta;
 
1027
                        int left = Platform::Minimum(x, xEnd);
 
1028
                        int width = abs(x - xEnd) + 1;
 
1029
                        int yEnd = y_ - yDelta;
 
1030
                        int top = Platform::Minimum(y, yEnd);
 
1031
                        int height = abs(y - yEnd) + 1;
 
1032
                        cairo_rectangle(context, left, top, width, height);
 
1033
                        cairo_fill(context);
 
1034
                } else if ((abs(xDiff) == abs(yDiff))) {
 
1035
                        // 45 degree slope
 
1036
                        cairo_move_to(context, x + 0.5, y + 0.5);
 
1037
                        cairo_line_to(context, x_ + 0.5 - xDelta, y_ + 0.5 - yDelta);
 
1038
                } else {
 
1039
                        // Line has a different slope so difficult to avoid last pixel
 
1040
                        cairo_move_to(context, x + 0.5, y + 0.5);
 
1041
                        cairo_line_to(context, x_ + 0.5, y_ + 0.5);
 
1042
                }
 
1043
                cairo_stroke(context);
 
1044
        }
 
1045
#else
 
1046
        if (drawable && gc) {
 
1047
                gdk_draw_line(drawable, gc,
 
1048
                              x, y,
 
1049
                              x_, y_);
 
1050
        }
 
1051
#endif
 
1052
        x = x_;
 
1053
        y = y_;
 
1054
}
 
1055
 
 
1056
void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
 
1057
                          ColourAllocated back) {
 
1058
#ifdef USE_CAIRO
 
1059
        PenColour(back);
 
1060
        cairo_move_to(context, pts[0].x + 0.5, pts[0].y + 0.5);
 
1061
        for (int i = 1;i < npts;i++) {
 
1062
                cairo_line_to(context, pts[i].x + 0.5, pts[i].y + 0.5);
 
1063
        }
 
1064
        cairo_close_path(context);
 
1065
        cairo_fill_preserve(context);
 
1066
        PenColour(fore);
 
1067
        cairo_stroke(context);
 
1068
#else
 
1069
        GdkPoint gpts[20];
 
1070
        if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) {
 
1071
                for (int i = 0;i < npts;i++) {
 
1072
                        gpts[i].x = pts[i].x;
 
1073
                        gpts[i].y = pts[i].y;
 
1074
                }
 
1075
                PenColour(back);
 
1076
                gdk_draw_polygon(drawable, gc, 1, gpts, npts);
 
1077
                PenColour(fore);
 
1078
                gdk_draw_polygon(drawable, gc, 0, gpts, npts);
 
1079
        }
 
1080
#endif
 
1081
}
 
1082
 
 
1083
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
 
1084
#ifdef USE_CAIRO
 
1085
        if (context) {
 
1086
#else
 
1087
        if (gc && drawable) {
 
1088
#endif
 
1089
#ifdef USE_CAIRO
 
1090
                cairo_rectangle(context, rc.left + 0.5, rc.top + 0.5,
 
1091
                             rc.right - rc.left - 1, rc.bottom - rc.top - 1);
 
1092
                PenColour(back);
 
1093
                cairo_fill_preserve(context);
 
1094
                PenColour(fore);
 
1095
                cairo_stroke(context);
 
1096
#else
 
1097
                PenColour(back);
 
1098
                gdk_draw_rectangle(drawable, gc, 1,
 
1099
                                   rc.left + 1, rc.top + 1,
 
1100
                                   rc.right - rc.left - 2, rc.bottom - rc.top - 2);
 
1101
                PenColour(fore);
 
1102
                // The subtraction of 1 off the width and height here shouldn't be needed but
 
1103
                // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
 
1104
                gdk_draw_rectangle(drawable, gc, 0,
 
1105
                                   rc.left, rc.top,
 
1106
                                   rc.right - rc.left - 1, rc.bottom - rc.top - 1);
 
1107
#endif
 
1108
        }
 
1109
}
 
1110
 
 
1111
void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
 
1112
        PenColour(back);
 
1113
#ifdef USE_CAIRO
 
1114
        if (context && (rc.left < maxCoordinate)) {     // Protect against out of range
 
1115
                cairo_rectangle(context, rc.left, rc.top,
 
1116
                             rc.right - rc.left, rc.bottom - rc.top);
 
1117
                cairo_fill(context);
 
1118
        }
 
1119
#else
 
1120
        if (drawable && (rc.left < maxCoordinate)) {    // Protect against out of range
 
1121
                gdk_draw_rectangle(drawable, gc, 1,
 
1122
                                   rc.left, rc.top,
 
1123
                                   rc.right - rc.left, rc.bottom - rc.top);
 
1124
        }
 
1125
#endif
 
1126
}
 
1127
 
 
1128
void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
 
1129
        SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfacePattern);
 
1130
#ifdef USE_CAIRO
 
1131
        bool canDraw = surfi.psurf;
 
1132
#else
 
1133
        bool canDraw = surfi.drawable;
 
1134
#endif
 
1135
        if (canDraw) {
 
1136
                // Tile pattern over rectangle
 
1137
                // Currently assumes 8x8 pattern
 
1138
                int widthPat = 8;
 
1139
                int heightPat = 8;
 
1140
                for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
 
1141
                        int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
 
1142
                        for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
 
1143
                                int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
 
1144
#ifdef USE_CAIRO
 
1145
                                cairo_set_source_surface(context, surfi.psurf, xTile, yTile);
 
1146
                                cairo_rectangle(context, xTile, yTile, widthx, heighty);
 
1147
                                cairo_fill(context);
 
1148
#else
 
1149
                                gdk_draw_drawable(drawable,
 
1150
                                                gc,
 
1151
                                                static_cast<SurfaceImpl &>(surfacePattern).drawable,
 
1152
                                                0, 0,
 
1153
                                                xTile, yTile,
 
1154
                                                widthx, heighty);
 
1155
#endif
 
1156
                        }
 
1157
                }
 
1158
        } else {
 
1159
                // Something is wrong so try to show anyway
 
1160
                // Shows up black because colour not allocated
 
1161
                FillRectangle(rc, ColourAllocated(0));
 
1162
        }
 
1163
}
 
1164
 
 
1165
void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
 
1166
        if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
 
1167
                // Approximate a round rect with some cut off corners
 
1168
                Point pts[] = {
 
1169
                                  Point(rc.left + 2, rc.top),
 
1170
                                  Point(rc.right - 2, rc.top),
 
1171
                                  Point(rc.right, rc.top + 2),
 
1172
                                  Point(rc.right, rc.bottom - 2),
 
1173
                                  Point(rc.right - 2, rc.bottom),
 
1174
                                  Point(rc.left + 2, rc.bottom),
 
1175
                                  Point(rc.left, rc.bottom - 2),
 
1176
                                  Point(rc.left, rc.top + 2),
 
1177
                              };
 
1178
                Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
 
1179
        } else {
 
1180
                RectangleDraw(rc, fore, back);
 
1181
        }
 
1182
}
 
1183
 
 
1184
#ifdef USE_CAIRO
 
1185
 
 
1186
static void PathRoundRectangle(cairo_t *context, double left, double top, double width, double height, int radius) {
 
1187
        double degrees = M_PI / 180.0;
 
1188
 
 
1189
        cairo_new_sub_path(context);
 
1190
        cairo_arc(context, left + width - radius, top + radius, radius, -90 * degrees, 0 * degrees);
 
1191
        cairo_arc(context, left + width - radius, top + height - radius, radius, 0 * degrees, 90 * degrees);
 
1192
        cairo_arc(context, left + radius, top + height - radius, radius, 90 * degrees, 180 * degrees);
 
1193
        cairo_arc(context, left + radius, top + radius, radius, 180 * degrees, 270 * degrees);
 
1194
        cairo_close_path(context);
 
1195
}
 
1196
 
 
1197
#else
 
1198
 
 
1199
// Plot a point into a guint32 buffer symetrically to all 4 qudrants
 
1200
static void AllFour(guint32 *pixels, int stride, int width, int height, int x, int y, guint32 val) {
 
1201
        pixels[y*stride+x] = val;
 
1202
        pixels[y*stride+width-1-x] = val;
 
1203
        pixels[(height-1-y)*stride+x] = val;
 
1204
        pixels[(height-1-y)*stride+width-1-x] = val;
 
1205
}
 
1206
 
 
1207
static guint32 u32FromRGBA(guint8 r, guint8 g, guint8 b, guint8 a) {
 
1208
        union {
 
1209
                guint8 pixVal[4];
 
1210
                guint32 val;
 
1211
        } converter;
 
1212
        converter.pixVal[0] = r;
 
1213
        converter.pixVal[1] = g;
 
1214
        converter.pixVal[2] = b;
 
1215
        converter.pixVal[3] = a;
 
1216
        return converter.val;
 
1217
}
 
1218
 
 
1219
#endif
 
1220
 
 
1221
static unsigned int GetRValue(unsigned int co) {
 
1222
        return (co >> 16) & 0xff;
 
1223
}
 
1224
 
 
1225
static unsigned int GetGValue(unsigned int co) {
 
1226
        return (co >> 8) & 0xff;
 
1227
}
 
1228
 
 
1229
static unsigned int GetBValue(unsigned int co) {
 
1230
        return co & 0xff;
 
1231
}
 
1232
 
 
1233
void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
 
1234
                ColourAllocated outline, int alphaOutline, int flags) {
 
1235
#ifdef USE_CAIRO
 
1236
        if (context && rc.Width() > 0) {
 
1237
                cairo_set_source_rgba(context,
 
1238
                        GetRValue(fill.AsLong()) / 255.0,
 
1239
                        GetGValue(fill.AsLong()) / 255.0,
 
1240
                        GetBValue(fill.AsLong()) / 255.0,
 
1241
                        alphaFill / 255.0);
 
1242
                PathRoundRectangle(context, rc.left + 1.0, rc.top+1.0, rc.right - rc.left - 2.0, rc.bottom - rc.top - 2.0, cornerSize);
 
1243
                cairo_fill(context);
 
1244
 
 
1245
                cairo_set_source_rgba(context,
 
1246
                        GetRValue(outline.AsLong()) / 255.0,
 
1247
                        GetGValue(outline.AsLong()) / 255.0,
 
1248
                        GetBValue(outline.AsLong()) / 255.0,
 
1249
                        alphaOutline / 255.0);
 
1250
                PathRoundRectangle(context, rc.left +0.5, rc.top+0.5, rc.right - rc.left - 1, rc.bottom - rc.top - 1, cornerSize);
 
1251
                cairo_stroke(context);
 
1252
        }
 
1253
#else
 
1254
        if (gc && drawable && rc.Width() > 0) {
 
1255
                int width = rc.Width();
 
1256
                int height = rc.Height();
 
1257
                // Ensure not distorted too much by corners when small
 
1258
                cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
 
1259
                // Make a 32 bit deep pixbuf with alpha
 
1260
                GdkPixbuf *pixalpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
 
1261
 
 
1262
                guint32 valEmpty = u32FromRGBA(0,0,0,0);
 
1263
                guint32 valFill = u32FromRGBA(GetRValue(fill.AsLong()),
 
1264
                        GetGValue(fill.AsLong()), GetBValue(fill.AsLong()), alphaFill);
 
1265
                guint32 valOutline = u32FromRGBA(GetRValue(outline.AsLong()),
 
1266
                        GetGValue(outline.AsLong()), GetBValue(outline.AsLong()), alphaOutline);
 
1267
                guint32 *pixels = reinterpret_cast<guint32 *>(gdk_pixbuf_get_pixels(pixalpha));
 
1268
                int stride = gdk_pixbuf_get_rowstride(pixalpha) / 4;
 
1269
                for (int yr=0; yr<height; yr++) {
 
1270
                        for (int xr=0; xr<width; xr++) {
 
1271
                                if ((xr==0) || (xr==width-1) || (yr == 0) || (yr == height-1)) {
 
1272
                                        pixels[yr*stride+xr] = valOutline;
 
1273
                                } else {
 
1274
                                        pixels[yr*stride+xr] = valFill;
 
1275
                                }
 
1276
                        }
 
1277
                }
 
1278
                for (int c=0;c<cornerSize; c++) {
 
1279
                        for (int xr=0;xr<c+1; xr++) {
 
1280
                                AllFour(pixels, stride, width, height, xr, c-xr, valEmpty);
 
1281
                        }
 
1282
                }
 
1283
                for (int xr=1;xr<cornerSize; xr++) {
 
1284
                        AllFour(pixels, stride, width, height, xr, cornerSize-xr, valOutline);
 
1285
                }
 
1286
 
 
1287
                // Draw with alpha
 
1288
                gdk_draw_pixbuf(drawable, gc, pixalpha,
 
1289
                        0,0, rc.left,rc.top, width,height, GDK_RGB_DITHER_NORMAL, 0, 0);
 
1290
 
 
1291
                g_object_unref(pixalpha);
 
1292
        }
 
1293
#endif
 
1294
}
 
1295
 
 
1296
void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
 
1297
        PenColour(back);
 
1298
#ifdef USE_CAIRO
 
1299
        cairo_arc(context, (rc.left + rc.right) / 2 + 0.5, (rc.top + rc.bottom) / 2 + 0.5,
 
1300
                Platform::Minimum(rc.Width(), rc.Height()) / 2, 0, 2*M_PI);
 
1301
        cairo_fill_preserve(context);
 
1302
        PenColour(fore);
 
1303
        cairo_stroke(context);
 
1304
#else
 
1305
        gdk_draw_arc(drawable, gc, 1,
 
1306
                     rc.left + 1, rc.top + 1,
 
1307
                     rc.right - rc.left - 2, rc.bottom - rc.top - 2,
 
1308
                     0, 32767);
 
1309
 
 
1310
        // The subtraction of 1 here is similar to the case for RectangleDraw
 
1311
        PenColour(fore);
 
1312
        gdk_draw_arc(drawable, gc, 0,
 
1313
                     rc.left, rc.top,
 
1314
                     rc.right - rc.left - 1, rc.bottom - rc.top - 1,
 
1315
                     0, 32767);
 
1316
#endif
 
1317
}
 
1318
 
 
1319
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
 
1320
        SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfaceSource);
 
1321
#ifdef USE_CAIRO
 
1322
        bool canDraw = surfi.psurf;
 
1323
#else
 
1324
        bool canDraw = surfi.drawable;
 
1325
#endif
 
1326
        if (canDraw) {
 
1327
#ifdef USE_CAIRO
 
1328
                cairo_set_source_surface(context, surfi.psurf,
 
1329
                        rc.left - from.x, rc.top - from.y);
 
1330
                cairo_rectangle(context, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
 
1331
                cairo_fill(context);
 
1332
#else
 
1333
                gdk_draw_drawable(drawable,
 
1334
                                gc,
 
1335
                                static_cast<SurfaceImpl &>(surfaceSource).drawable,
 
1336
                                from.x, from.y,
 
1337
                                rc.left, rc.top,
 
1338
                                rc.right - rc.left, rc.bottom - rc.top);
 
1339
#endif
 
1340
        }
 
1341
}
 
1342
 
 
1343
#ifndef DISABLE_GDK_FONT
 
1344
static size_t UTF8Len(char ch) {
 
1345
        unsigned char uch = static_cast<unsigned char>(ch);
 
1346
        if (uch < 0x80)
 
1347
                return 1;
 
1348
        else if (uch < (0x80 + 0x40 + 0x20))
 
1349
                return 2;
 
1350
        else
 
1351
                return 3;
 
1352
}
 
1353
#endif
 
1354
 
 
1355
char *UTF8FromLatin1(const char *s, int &len) {
 
1356
        char *utfForm = new char[len*2+1];
 
1357
        size_t lenU = 0;
 
1358
        for (int i=0;i<len;i++) {
 
1359
                unsigned int uch = static_cast<unsigned char>(s[i]);
 
1360
                if (uch < 0x80) {
 
1361
                        utfForm[lenU++] = uch;
 
1362
                } else {
 
1363
                        utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
 
1364
                        utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
 
1365
                }
 
1366
        }
 
1367
        utfForm[lenU] = '\0';
 
1368
        len = lenU;
 
1369
        return utfForm;
 
1370
}
 
1371
 
 
1372
static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
 
1373
        if (conv) {
 
1374
                char *utfForm = new char[len*3+1];
 
1375
                char *pin = const_cast<char *>(s);
 
1376
                size_t inLeft = len;
 
1377
                char *pout = utfForm;
 
1378
                size_t outLeft = len*3+1;
 
1379
                size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
 
1380
                if (conversions != ((size_t)(-1))) {
 
1381
                        *pout = '\0';
 
1382
                        len = pout - utfForm;
 
1383
                        return utfForm;
 
1384
                }
 
1385
                delete []utfForm;
 
1386
        }
 
1387
        return 0;
 
1388
}
 
1389
 
 
1390
// Work out how many bytes are in a character by trying to convert using iconv,
 
1391
// returning the first length that succeeds.
 
1392
static size_t MultiByteLenFromIconv(const Converter &conv, const char *s, size_t len) {
 
1393
        for (size_t lenMB=1; (lenMB<4) && (lenMB <= len); lenMB++) {
 
1394
                char wcForm[2];
 
1395
                char *pin = const_cast<char *>(s);
 
1396
                size_t inLeft = lenMB;
 
1397
                char *pout = wcForm;
 
1398
                size_t outLeft = 2;
 
1399
                size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
 
1400
                if (conversions != ((size_t)(-1))) {
 
1401
                        return lenMB;
 
1402
                }
 
1403
        }
 
1404
        return 1;
 
1405
}
 
1406
 
 
1407
#ifndef DISABLE_GDK_FONT
 
1408
static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
 
1409
        char *utfForm = new char[wclen*3+1];    // Maximum of 3 UTF-8 bytes per character
 
1410
        size_t lenU = 0;
 
1411
        for (int i = 0; i < wclen && wctext[i]; i++) {
 
1412
                unsigned int uch = wctext[i];
 
1413
                if (uch < 0x80) {
 
1414
                        utfForm[lenU++] = static_cast<char>(uch);
 
1415
                } else if (uch < 0x800) {
 
1416
                        utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
 
1417
                        utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
 
1418
                } else {
 
1419
                        utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
 
1420
                        utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
 
1421
                        utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
 
1422
                }
 
1423
        }
 
1424
        utfForm[lenU] = '\0';
 
1425
        return utfForm;
 
1426
}
 
1427
#endif
 
1428
 
 
1429
static char *UTF8FromDBCS(const char *s, int &len) {
 
1430
#ifndef DISABLE_GDK_FONT
 
1431
        GdkWChar *wctext = new GdkWChar[len + 1];
 
1432
        GdkWChar *wcp = wctext;
 
1433
        int wclen = gdk_mbstowcs(wcp, s, len);
 
1434
        if (wclen < 1) {
 
1435
                // In the annoying case when non-locale chars in the line.
 
1436
                // e.g. latin1 chars in Japanese locale.
 
1437
                delete []wctext;
 
1438
                return 0;
 
1439
        }
 
1440
 
 
1441
        char *utfForm = UTF8FromGdkWChar(wctext, wclen);
 
1442
        delete []wctext;
 
1443
        len = strlen(utfForm);
 
1444
        return utfForm;
 
1445
#else
 
1446
        return 0;
 
1447
#endif
 
1448
}
 
1449
 
 
1450
static size_t UTF8CharLength(const char *s) {
 
1451
        const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
 
1452
        unsigned char ch = *us;
 
1453
        if (ch < 0x80) {
 
1454
                return 1;
 
1455
        } else if (ch < 0x80 + 0x40 + 0x20) {
 
1456
                return 2;
 
1457
        } else {
 
1458
                return 3;
 
1459
        }
 
1460
}
 
1461
 
 
1462
// On GTK+, wchar_t is 4 bytes
 
1463
 
 
1464
const int maxLengthTextRun = 10000;
 
1465
 
 
1466
void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
 
1467
                                 ColourAllocated fore) {
 
1468
        PenColour(fore);
 
1469
#ifdef USE_CAIRO
 
1470
        if (context) {
 
1471
#else
 
1472
        if (gc && drawable) {
 
1473
#endif
 
1474
                int xText = rc.left;
 
1475
                if (PFont(font_)->pfd) {
 
1476
                        char *utfForm = 0;
 
1477
                        bool useGFree = false;
 
1478
                        if (et == UTF8) {
 
1479
                                pango_layout_set_text(layout, s, len);
 
1480
                        } else {
 
1481
                                if (!utfForm) {
 
1482
                                        SetConverter(PFont(font_)->characterSet);
 
1483
                                        utfForm = UTF8FromIconv(conv, s, len);
 
1484
                                }
 
1485
                                if (!utfForm) { // iconv failed so try DBCS if DBCS mode
 
1486
                                        if (et == dbcs) {
 
1487
                                                // Convert to utf8
 
1488
                                                utfForm = UTF8FromDBCS(s, len);
 
1489
                                        }
 
1490
                                }
 
1491
                                if (!utfForm) { // iconv and DBCS failed so treat as Latin1
 
1492
                                        utfForm = UTF8FromLatin1(s, len);
 
1493
                                }
 
1494
                                pango_layout_set_text(layout, utfForm, len);
 
1495
                        }
 
1496
                        pango_layout_set_font_description(layout, PFont(font_)->pfd);
 
1497
#ifdef USE_CAIRO
 
1498
                        pango_cairo_update_layout(context, layout);
 
1499
#endif
 
1500
#ifdef PANGO_VERSION
 
1501
                        PangoLayoutLine *pll = pango_layout_get_line_readonly(layout,0);
 
1502
#else
 
1503
                        PangoLayoutLine *pll = pango_layout_get_line(layout,0);
 
1504
#endif
 
1505
#ifdef USE_CAIRO
 
1506
                        cairo_move_to(context, xText, ybase);
 
1507
                        pango_cairo_show_layout_line(context, pll);
 
1508
#else
 
1509
                        gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
 
1510
#endif
 
1511
                        if (useGFree) {
 
1512
                                g_free(utfForm);
 
1513
                        } else {
 
1514
                                delete []utfForm;
 
1515
                        }
 
1516
                        return;
 
1517
                }
 
1518
#ifndef DISABLE_GDK_FONT
 
1519
                // Draw text as a series of segments to avoid limitations in X servers
 
1520
                const int segmentLength = 1000;
 
1521
                bool draw8bit = true;
 
1522
                if (et != singleByte) {
 
1523
                        GdkWChar wctext[maxLengthTextRun];
 
1524
                        if (len >= maxLengthTextRun)
 
1525
                                len = maxLengthTextRun-1;
 
1526
                        int wclen;
 
1527
                        if (et == UTF8) {
 
1528
                                wclen = UTF16FromUTF8(s, len,
 
1529
                                        static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
 
1530
                        } else {        // dbcs, so convert using current locale
 
1531
                                char sMeasure[maxLengthTextRun];
 
1532
                                memcpy(sMeasure, s, len);
 
1533
                                sMeasure[len] = '\0';
 
1534
                                wclen = gdk_mbstowcs(
 
1535
                                        wctext, sMeasure, maxLengthTextRun - 1);
 
1536
                        }
 
1537
                        if (wclen > 0) {
 
1538
                                draw8bit = false;
 
1539
                                wctext[wclen] = L'\0';
 
1540
                                GdkWChar *wcp = wctext;
 
1541
                                while ((wclen > 0) && (xText < maxCoordinate)) {
 
1542
                                        int lenDraw = Platform::Minimum(wclen, segmentLength);
 
1543
                                        gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
 
1544
                                                         xText, ybase, wcp, lenDraw);
 
1545
                                        wclen -= lenDraw;
 
1546
                                        if (wclen > 0) {        // Avoid next calculation if possible as may be expensive
 
1547
                                                xText += gdk_text_width_wc(PFont(font_)->pfont,
 
1548
                                                                       wcp, lenDraw);
 
1549
                                        }
 
1550
                                        wcp += lenDraw;
 
1551
                                }
 
1552
                        }
 
1553
                }
 
1554
                if (draw8bit) {
 
1555
                        while ((len > 0) && (xText < maxCoordinate)) {
 
1556
                                int lenDraw = Platform::Minimum(len, segmentLength);
 
1557
                                gdk_draw_text(drawable, PFont(font_)->pfont, gc,
 
1558
                                              xText, ybase, s, lenDraw);
 
1559
                                len -= lenDraw;
 
1560
                                if (len > 0) {  // Avoid next calculation if possible as may be expensive
 
1561
                                        xText += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
 
1562
                                }
 
1563
                                s += lenDraw;
 
1564
                        }
 
1565
                }
 
1566
#endif
 
1567
        }
 
1568
}
 
1569
 
 
1570
void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
 
1571
                                 ColourAllocated fore, ColourAllocated back) {
 
1572
        FillRectangle(rc, back);
 
1573
        DrawTextBase(rc, font_, ybase, s, len, fore);
 
1574
}
 
1575
 
 
1576
// On GTK+, exactly same as DrawTextNoClip
 
1577
void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
 
1578
                                  ColourAllocated fore, ColourAllocated back) {
 
1579
        FillRectangle(rc, back);
 
1580
        DrawTextBase(rc, font_, ybase, s, len, fore);
 
1581
}
 
1582
 
 
1583
void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
 
1584
                                  ColourAllocated fore) {
 
1585
        // Avoid drawing spaces in transparent mode
 
1586
        for (int i=0;i<len;i++) {
 
1587
                if (s[i] != ' ') {
 
1588
                        DrawTextBase(rc, font_, ybase, s, len, fore);
 
1589
                        return;
 
1590
                }
 
1591
        }
 
1592
}
 
1593
 
 
1594
class ClusterIterator {
 
1595
        PangoLayoutIter *iter;
 
1596
        PangoRectangle pos;
 
1597
        int lenPositions;
 
1598
public:
 
1599
        bool finished;
 
1600
        int positionStart;
 
1601
        int position;
 
1602
        int distance;
 
1603
        int curIndex;
 
1604
        ClusterIterator(PangoLayout *layout, int len) : lenPositions(len), finished(false),
 
1605
                positionStart(0), position(0), distance(0), curIndex(0) {
 
1606
                iter = pango_layout_get_iter(layout);
 
1607
                pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
 
1608
        }
 
1609
        ~ClusterIterator() {
 
1610
                pango_layout_iter_free(iter);
 
1611
        }
 
1612
 
 
1613
        void Next() {
 
1614
                positionStart = position;
 
1615
                if (pango_layout_iter_next_cluster(iter)) {
 
1616
                        pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
 
1617
                        position = PANGO_PIXELS(pos.x);
 
1618
                        curIndex = pango_layout_iter_get_index(iter);
 
1619
                } else {
 
1620
                        finished = true;
 
1621
                        position = PANGO_PIXELS(pos.x + pos.width);
 
1622
                        curIndex = lenPositions;
 
1623
                }
 
1624
                distance = position - positionStart;
 
1625
        }
 
1626
};
 
1627
 
 
1628
void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
 
1629
        if (font_.GetID()) {
 
1630
                const int lenPositions = len;
 
1631
                if (PFont(font_)->pfd) {
 
1632
                        if (len == 1) {
 
1633
                                int width = PFont(font_)->CharWidth(*s, et);
 
1634
                                if (width) {
 
1635
                                        positions[0] = width;
 
1636
                                        return;
 
1637
                                }
 
1638
                        }
 
1639
                        pango_layout_set_font_description(layout, PFont(font_)->pfd);
 
1640
                        if (et == UTF8) {
 
1641
                                // Simple and direct as UTF-8 is native Pango encoding
 
1642
                                int i = 0;
 
1643
                                pango_layout_set_text(layout, s, len);
 
1644
                                ClusterIterator iti(layout, lenPositions);
 
1645
                                while (!iti.finished) {
 
1646
                                        iti.Next();
 
1647
                                        int places = iti.curIndex - i;
 
1648
                                        while (i < iti.curIndex) {
 
1649
                                                // Evenly distribute space among bytes of this cluster.
 
1650
                                                // Would be better to find number of characters and then
 
1651
                                                // divide evenly between characters with each byte of a character
 
1652
                                                // being at the same position.
 
1653
                                                positions[i] = iti.position - (iti.curIndex - 1 - i) * iti.distance / places;
 
1654
                                                i++;
 
1655
                                        }
 
1656
                                }
 
1657
                                PLATFORM_ASSERT(i == lenPositions);
 
1658
                        } else {
 
1659
                                int positionsCalculated = 0;
 
1660
                                if (et == dbcs) {
 
1661
                                        SetConverter(PFont(font_)->characterSet);
 
1662
                                        char *utfForm = UTF8FromIconv(conv, s, len);
 
1663
                                        if (utfForm) {
 
1664
                                                // Convert to UTF-8 so can ask Pango for widths, then
 
1665
                                                // Loop through UTF-8 and DBCS forms, taking account of different
 
1666
                                                // character byte lengths.
 
1667
                                                Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
 
1668
                                                pango_layout_set_text(layout, utfForm, strlen(utfForm));
 
1669
                                                int i = 0;
 
1670
                                                int clusterStart = 0;
 
1671
                                                ClusterIterator iti(layout, strlen(utfForm));
 
1672
                                                while (!iti.finished) {
 
1673
                                                        iti.Next();
 
1674
                                                        int clusterEnd = iti.curIndex;
 
1675
                                                        int places = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
 
1676
                                                        int place = 1;
 
1677
                                                        while (clusterStart < clusterEnd) {
 
1678
                                                                size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
 
1679
                                                                while (lenChar--) {
 
1680
                                                                        positions[i++] = iti.position - (places - place) * iti.distance / places;
 
1681
                                                                        positionsCalculated++;
 
1682
                                                                }
 
1683
                                                                clusterStart += UTF8CharLength(utfForm+clusterStart);
 
1684
                                                                place++;
 
1685
                                                        }
 
1686
                                                }
 
1687
                                                delete []utfForm;
 
1688
                                                PLATFORM_ASSERT(i == lenPositions);
 
1689
                                        }
 
1690
                                }
 
1691
                                if (positionsCalculated < 1 ) {
 
1692
                                        // Either Latin1 or DBCS conversion failed so treat as Latin1.
 
1693
                                        bool useGFree = false;
 
1694
                                        SetConverter(PFont(font_)->characterSet);
 
1695
                                        char *utfForm = UTF8FromIconv(conv, s, len);
 
1696
                                        if (!utfForm) {
 
1697
                                                utfForm = UTF8FromLatin1(s, len);
 
1698
                                        }
 
1699
                                        pango_layout_set_text(layout, utfForm, len);
 
1700
                                        int i = 0;
 
1701
                                        int clusterStart = 0;
 
1702
                                        // Each Latin1 input character may take 1 or 2 bytes in UTF-8
 
1703
                                        // and groups of up to 3 may be represented as ligatures.
 
1704
                                        ClusterIterator iti(layout, strlen(utfForm));
 
1705
                                        while (!iti.finished) {
 
1706
                                                iti.Next();
 
1707
                                                int clusterEnd = iti.curIndex;
 
1708
                                                int ligatureLength = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
 
1709
                                                PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
 
1710
                                                for (int charInLig=0; charInLig<ligatureLength; charInLig++) {
 
1711
                                                        positions[i++] = iti.position - (ligatureLength - 1 - charInLig) * iti.distance / ligatureLength;
 
1712
                                                }
 
1713
                                                clusterStart = clusterEnd;
 
1714
                                        }
 
1715
                                        if (useGFree) {
 
1716
                                                g_free(utfForm);
 
1717
                                        } else {
 
1718
                                                delete []utfForm;
 
1719
                                        }
 
1720
                                        PLATFORM_ASSERT(i == lenPositions);
 
1721
                                }
 
1722
                        }
 
1723
                        if (len == 1) {
 
1724
                                PFont(font_)->SetCharWidth(*s, positions[0], et);
 
1725
                        }
 
1726
                        return;
 
1727
                }
 
1728
#ifndef DISABLE_GDK_FONT
 
1729
                int totalWidth = 0;
 
1730
                GdkFont *gf = PFont(font_)->pfont;
 
1731
                bool measure8bit = true;
 
1732
                if (et != singleByte) {
 
1733
                        GdkWChar wctext[maxLengthTextRun];
 
1734
                        if (len >= maxLengthTextRun)
 
1735
                                len = maxLengthTextRun-1;
 
1736
                        int wclen;
 
1737
                        if (et == UTF8) {
 
1738
                                wclen = UTF16FromUTF8(s, len,
 
1739
                                        static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
 
1740
                        } else {        // dbcsMode, so convert using current locale
 
1741
                                char sDraw[maxLengthTextRun];
 
1742
                                memcpy(sDraw, s, len);
 
1743
                                sDraw[len] = '\0';
 
1744
                                wclen = gdk_mbstowcs(
 
1745
                                        wctext, sDraw, maxLengthTextRun - 1);
 
1746
                        }
 
1747
                        if (wclen > 0) {
 
1748
                                measure8bit = false;
 
1749
                                wctext[wclen] = L'\0';
 
1750
                                // Map widths back to utf-8 or DBCS input string
 
1751
                                int i = 0;
 
1752
                                for (int iU = 0; iU < wclen; iU++) {
 
1753
                                        int width = gdk_char_width_wc(gf, wctext[iU]);
 
1754
                                        totalWidth += width;
 
1755
                                        int lenChar;
 
1756
                                        if (et == UTF8) {
 
1757
                                                lenChar = UTF8Len(s[i]);
 
1758
                                        } else {
 
1759
                                                lenChar = mblen(s+i, MB_CUR_MAX);
 
1760
                                                if (lenChar < 0)
 
1761
                                                        lenChar = 1;
 
1762
                                        }
 
1763
                                        while (lenChar--) {
 
1764
                                                positions[i++] = totalWidth;
 
1765
                                        }
 
1766
                                }
 
1767
                                while (i < len) {       // In case of problems with lengths
 
1768
                                        positions[i++] = totalWidth;
 
1769
                                }
 
1770
                        }
 
1771
                }
 
1772
                if (measure8bit) {
 
1773
                        // Either Latin1 or conversion failed so treat as Latin1.
 
1774
                        for (int i = 0; i < len; i++) {
 
1775
                                int width = gdk_char_width(gf, s[i]);
 
1776
                                totalWidth += width;
 
1777
                                positions[i] = totalWidth;
 
1778
                        }
 
1779
                }
 
1780
#endif
 
1781
        } else {
 
1782
                // No font so return an ascending range of values
 
1783
                for (int i = 0; i < len; i++) {
 
1784
                        positions[i] = i + 1;
 
1785
                }
 
1786
        }
 
1787
}
 
1788
 
 
1789
int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
 
1790
        if (font_.GetID()) {
 
1791
                if (PFont(font_)->pfd) {
 
1792
                        char *utfForm = 0;
 
1793
                        pango_layout_set_font_description(layout, PFont(font_)->pfd);
 
1794
                        PangoRectangle pos;
 
1795
                        bool useGFree = false;
 
1796
                        if (et == UTF8) {
 
1797
                                pango_layout_set_text(layout, s, len);
 
1798
                        } else {
 
1799
                                if (et == dbcs) {
 
1800
                                        // Convert to utf8
 
1801
                                        utfForm = UTF8FromDBCS(s, len);
 
1802
                                }
 
1803
                                if (!utfForm) { // DBCS failed so treat as iconv
 
1804
                                        SetConverter(PFont(font_)->characterSet);
 
1805
                                        utfForm = UTF8FromIconv(conv, s, len);
 
1806
                                }
 
1807
                                if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
 
1808
                                        utfForm = UTF8FromLatin1(s, len);
 
1809
                                }
 
1810
                                pango_layout_set_text(layout, utfForm, len);
 
1811
                        }
 
1812
#ifdef PANGO_VERSION
 
1813
                        PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout,0);
 
1814
#else
 
1815
                        PangoLayoutLine *pangoLine = pango_layout_get_line(layout,0);
 
1816
#endif
 
1817
                        pango_layout_line_get_extents(pangoLine, NULL, &pos);
 
1818
                        if (useGFree) {
 
1819
                                g_free(utfForm);
 
1820
                        } else {
 
1821
                                delete []utfForm;
 
1822
                        }
 
1823
                        return PANGO_PIXELS(pos.width);
 
1824
                }
 
1825
#ifndef DISABLE_GDK_FONT
 
1826
                if (et == UTF8) {
 
1827
                        GdkWChar wctext[maxLengthTextRun];
 
1828
                        size_t wclen = UTF16FromUTF8(s, len, static_cast<wchar_t *>(static_cast<void *>(wctext)),
 
1829
                                sizeof(wctext) / sizeof(GdkWChar) - 1);
 
1830
                        wctext[wclen] = L'\0';
 
1831
                        return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
 
1832
                } else {
 
1833
                        return gdk_text_width(PFont(font_)->pfont, s, len);
 
1834
                }
 
1835
#else
 
1836
                return 1;
 
1837
#endif
 
1838
        } else {
 
1839
                return 1;
 
1840
        }
 
1841
}
 
1842
 
 
1843
int SurfaceImpl::WidthChar(Font &font_, char ch) {
 
1844
        if (font_.GetID()) {
 
1845
                if (PFont(font_)->pfd) {
 
1846
                        return WidthText(font_, &ch, 1);
 
1847
                }
 
1848
#ifndef DISABLE_GDK_FONT
 
1849
                return gdk_char_width(PFont(font_)->pfont, ch);
 
1850
#else
 
1851
                return 1;
 
1852
#endif
 
1853
        } else {
 
1854
                return 1;
 
1855
        }
 
1856
}
 
1857
 
 
1858
// Three possible strategies for determining ascent and descent of font:
 
1859
// 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
 
1860
// 2) Use the ascent and descent fields of GdkFont.
 
1861
// 3) Call gdk_string_extents with string as 1 but also including accented capitals.
 
1862
// Smallest values given by 1 and largest by 3 with 2 in between.
 
1863
// Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
 
1864
// descenders but are mostly OK except for accented characters like � which are
 
1865
// rarely used in code.
 
1866
 
 
1867
// This string contains a good range of characters to test for size.
 
1868
//const char largeSizeString[] = "���� `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
 
1869
//                               "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
1870
#ifndef FAST_WAY
 
1871
const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
 
1872
                          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
1873
#endif
 
1874
 
 
1875
int SurfaceImpl::Ascent(Font &font_) {
 
1876
        if (!(font_.GetID()))
 
1877
                return 1;
 
1878
#ifdef FAST_WAY
 
1879
        FontMutexLock();
 
1880
        int ascent = PFont(font_)->ascent;
 
1881
        if ((ascent == 0) && (PFont(font_)->pfd)) {
 
1882
                PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
 
1883
                        PFont(font_)->pfd, pango_context_get_language(pcontext));
 
1884
                PFont(font_)->ascent =
 
1885
                        PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
 
1886
                pango_font_metrics_unref(metrics);
 
1887
                ascent = PFont(font_)->ascent;
 
1888
        }
 
1889
#ifndef DISABLE_GDK_FONT
 
1890
        if ((ascent == 0) && (PFont(font_)->pfont)) {
 
1891
                ascent = PFont(font_)->pfont->ascent;
 
1892
        }
 
1893
#endif
 
1894
        if (ascent == 0) {
 
1895
                ascent = 1;
 
1896
        }
 
1897
        FontMutexUnlock();
 
1898
        return ascent;
 
1899
#else
 
1900
 
 
1901
        gint lbearing;
 
1902
        gint rbearing;
 
1903
        gint width;
 
1904
        gint ascent;
 
1905
        gint descent;
 
1906
 
 
1907
        gdk_string_extents(PFont(font_)->pfont, sizeString,
 
1908
                                           &lbearing, &rbearing, &width, &ascent, &descent);
 
1909
        return ascent;
 
1910
#endif
 
1911
}
 
1912
 
 
1913
int SurfaceImpl::Descent(Font &font_) {
 
1914
        if (!(font_.GetID()))
 
1915
                return 1;
 
1916
#ifdef FAST_WAY
 
1917
 
 
1918
        if (PFont(font_)->pfd) {
 
1919
                PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
 
1920
                        PFont(font_)->pfd, pango_context_get_language(pcontext));
 
1921
                int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
 
1922
                pango_font_metrics_unref(metrics);
 
1923
                return descent;
 
1924
        }
 
1925
#ifndef DISABLE_GDK_FONT
 
1926
        return PFont(font_)->pfont->descent;
 
1927
#else
 
1928
        return 0;
 
1929
#endif
 
1930
#else
 
1931
 
 
1932
        gint lbearing;
 
1933
        gint rbearing;
 
1934
        gint width;
 
1935
        gint ascent;
 
1936
        gint descent;
 
1937
 
 
1938
        gdk_string_extents(PFont(font_)->pfont, sizeString,
 
1939
                                           &lbearing, &rbearing, &width, &ascent, &descent);
 
1940
        return descent;
 
1941
#endif
 
1942
}
 
1943
 
 
1944
int SurfaceImpl::InternalLeading(Font &) {
 
1945
        return 0;
 
1946
}
 
1947
 
 
1948
int SurfaceImpl::ExternalLeading(Font &) {
 
1949
        return 0;
 
1950
}
 
1951
 
 
1952
int SurfaceImpl::Height(Font &font_) {
 
1953
        return Ascent(font_) + Descent(font_);
 
1954
}
 
1955
 
 
1956
int SurfaceImpl::AverageCharWidth(Font &font_) {
 
1957
        return WidthChar(font_, 'n');
 
1958
}
 
1959
 
 
1960
int SurfaceImpl::SetPalette(Palette *, bool) {
 
1961
        // Handled in palette allocation for GTK so this does nothing
 
1962
        return 0;
 
1963
}
 
1964
 
 
1965
void SurfaceImpl::SetClip(PRectangle rc) {
 
1966
#ifdef USE_CAIRO
 
1967
        cairo_rectangle(context, rc.left, rc.top, rc.right, rc.bottom);
 
1968
        cairo_clip(context);
 
1969
#else
 
1970
        GdkRectangle area = {rc.left, rc.top,
 
1971
                             rc.right - rc.left, rc.bottom - rc.top};
 
1972
        gdk_gc_set_clip_rectangle(gc, &area);
 
1973
#endif
 
1974
}
 
1975
 
 
1976
void SurfaceImpl::FlushCachedState() {}
 
1977
 
 
1978
void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
 
1979
        if (unicodeMode_)
 
1980
                et = UTF8;
 
1981
}
 
1982
 
 
1983
void SurfaceImpl::SetDBCSMode(int codePage) {
 
1984
        if (codePage && (codePage != SC_CP_UTF8))
 
1985
                et = dbcs;
 
1986
}
 
1987
 
 
1988
Surface *Surface::Allocate() {
 
1989
        return new SurfaceImpl;
 
1990
}
 
1991
 
 
1992
Window::~Window() {}
 
1993
 
 
1994
void Window::Destroy() {
 
1995
        if (wid)
 
1996
                gtk_widget_destroy(GTK_WIDGET(wid));
 
1997
        wid = 0;
 
1998
}
 
1999
 
 
2000
bool Window::HasFocus() {
 
2001
        return IS_WIDGET_FOCUSSED(wid);
 
2002
}
 
2003
 
 
2004
PRectangle Window::GetPosition() {
 
2005
        // Before any size allocated pretend its 1000 wide so not scrolled
 
2006
        PRectangle rc(0, 0, 1000, 1000);
 
2007
        if (wid) {
 
2008
                rc.left = PWidget(wid)->allocation.x;
 
2009
                rc.top = PWidget(wid)->allocation.y;
 
2010
                if (PWidget(wid)->allocation.width > 20) {
 
2011
                        rc.right = rc.left + PWidget(wid)->allocation.width;
 
2012
                        rc.bottom = rc.top + PWidget(wid)->allocation.height;
 
2013
                }
 
2014
        }
 
2015
        return rc;
 
2016
}
 
2017
 
 
2018
void Window::SetPosition(PRectangle rc) {
 
2019
        GtkAllocation alloc;
 
2020
        alloc.x = rc.left;
 
2021
        alloc.y = rc.top;
 
2022
        alloc.width = rc.Width();
 
2023
        alloc.height = rc.Height();
 
2024
        gtk_widget_size_allocate(PWidget(wid), &alloc);
 
2025
}
 
2026
 
 
2027
void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
 
2028
        int ox = 0;
 
2029
        int oy = 0;
 
2030
        gdk_window_get_origin(PWidget(relativeTo.wid)->window, &ox, &oy);
 
2031
        ox += rc.left;
 
2032
        if (ox < 0)
 
2033
                ox = 0;
 
2034
        oy += rc.top;
 
2035
        if (oy < 0)
 
2036
                oy = 0;
 
2037
 
 
2038
        /* do some corrections to fit into screen */
 
2039
        int sizex = rc.right - rc.left;
 
2040
        int sizey = rc.bottom - rc.top;
 
2041
        int screenWidth = gdk_screen_width();
 
2042
        int screenHeight = gdk_screen_height();
 
2043
        if (sizex > screenWidth)
 
2044
                ox = 0; /* the best we can do */
 
2045
        else if (ox + sizex > screenWidth)
 
2046
                ox = screenWidth - sizex;
 
2047
        if (oy + sizey > screenHeight)
 
2048
                oy = screenHeight - sizey;
 
2049
 
 
2050
        gtk_window_move(GTK_WINDOW(PWidget(wid)), ox, oy);
 
2051
 
 
2052
        gtk_widget_set_size_request(PWidget(wid), sizex, sizey);
 
2053
}
 
2054
 
 
2055
PRectangle Window::GetClientPosition() {
 
2056
        // On GTK+, the client position is the window position
 
2057
        return GetPosition();
 
2058
}
 
2059
 
 
2060
void Window::Show(bool show) {
 
2061
        if (show)
 
2062
                gtk_widget_show(PWidget(wid));
 
2063
}
 
2064
 
 
2065
void Window::InvalidateAll() {
 
2066
        if (wid) {
 
2067
                gtk_widget_queue_draw(PWidget(wid));
 
2068
        }
 
2069
}
 
2070
 
 
2071
void Window::InvalidateRectangle(PRectangle rc) {
 
2072
        if (wid) {
 
2073
                gtk_widget_queue_draw_area(PWidget(wid),
 
2074
                                           rc.left, rc.top,
 
2075
                                           rc.right - rc.left, rc.bottom - rc.top);
 
2076
        }
 
2077
}
 
2078
 
 
2079
void Window::SetFont(Font &) {
 
2080
        // Can not be done generically but only needed for ListBox
 
2081
}
 
2082
 
 
2083
void Window::SetCursor(Cursor curs) {
 
2084
        // We don't set the cursor to same value numerous times under gtk because
 
2085
        // it stores the cursor in the window once it's set
 
2086
        if (curs == cursorLast)
 
2087
                return;
 
2088
 
 
2089
        cursorLast = curs;
 
2090
        GdkCursor *gdkCurs;
 
2091
        switch (curs) {
 
2092
        case cursorText:
 
2093
                gdkCurs = gdk_cursor_new(GDK_XTERM);
 
2094
                break;
 
2095
        case cursorArrow:
 
2096
                gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
 
2097
                break;
 
2098
        case cursorUp:
 
2099
                gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
 
2100
                break;
 
2101
        case cursorWait:
 
2102
                gdkCurs = gdk_cursor_new(GDK_WATCH);
 
2103
                break;
 
2104
        case cursorHand:
 
2105
                gdkCurs = gdk_cursor_new(GDK_HAND2);
 
2106
                break;
 
2107
        case cursorReverseArrow:
 
2108
                gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
 
2109
                break;
 
2110
        default:
 
2111
                gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
 
2112
                cursorLast = cursorArrow;
 
2113
                break;
 
2114
        }
 
2115
 
 
2116
        if (PWidget(wid)->window)
 
2117
                gdk_window_set_cursor(PWidget(wid)->window, gdkCurs);
 
2118
        gdk_cursor_unref(gdkCurs);
 
2119
}
 
2120
 
 
2121
void Window::SetTitle(const char *s) {
 
2122
        gtk_window_set_title(GTK_WINDOW(wid), s);
 
2123
}
 
2124
 
 
2125
/* Returns rectangle of monitor pt is on, both rect and pt are in Window's
 
2126
   gdk window coordinates */
 
2127
PRectangle Window::GetMonitorRect(Point pt) {
 
2128
        gint x_offset, y_offset;
 
2129
 
 
2130
        gdk_window_get_origin(PWidget(wid)->window, &x_offset, &y_offset);
 
2131
 
 
2132
#if GTK_CHECK_VERSION(2,2,0)
 
2133
        // GTK+ 2.2+
 
2134
        {
 
2135
                GdkScreen* screen;
 
2136
                gint monitor_num;
 
2137
                GdkRectangle rect;
 
2138
 
 
2139
                screen = gtk_widget_get_screen(PWidget(wid));
 
2140
                monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
 
2141
                gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
 
2142
                rect.x -= x_offset;
 
2143
                rect.y -= y_offset;
 
2144
                return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
 
2145
        }
 
2146
#else
 
2147
        pt = pt;
 
2148
        return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
 
2149
                          (-y_offset) + gdk_screen_height());
 
2150
#endif
 
2151
}
 
2152
 
 
2153
struct ListImage {
 
2154
        const char *xpm_data;
 
2155
        GdkPixbuf *pixbuf;
 
2156
};
 
2157
 
 
2158
static void list_image_free(gpointer, gpointer value, gpointer) {
 
2159
        ListImage *list_image = (ListImage *) value;
 
2160
        if (list_image->pixbuf)
 
2161
                g_object_unref (list_image->pixbuf);
 
2162
        g_free(list_image);
 
2163
}
 
2164
 
 
2165
ListBox::ListBox() {
 
2166
}
 
2167
 
 
2168
ListBox::~ListBox() {
 
2169
}
 
2170
 
 
2171
enum {
 
2172
        PIXBUF_COLUMN,
 
2173
        TEXT_COLUMN,
 
2174
        N_COLUMNS
 
2175
};
 
2176
 
 
2177
class ListBoxX : public ListBox {
 
2178
        WindowID list;
 
2179
        WindowID scroller;
 
2180
        void *pixhash;
 
2181
        GtkCellRenderer* pixbuf_renderer;
 
2182
        XPMSet xset;
 
2183
        int desiredVisibleRows;
 
2184
        unsigned int maxItemCharacters;
 
2185
        unsigned int aveCharWidth;
 
2186
public:
 
2187
        CallBackAction doubleClickAction;
 
2188
        void *doubleClickActionData;
 
2189
 
 
2190
        ListBoxX() : list(0), scroller(0), pixhash(NULL), pixbuf_renderer(0),
 
2191
                desiredVisibleRows(5), maxItemCharacters(0),
 
2192
                aveCharWidth(1), doubleClickAction(NULL), doubleClickActionData(NULL) {
 
2193
        }
 
2194
        virtual ~ListBoxX() {
 
2195
                if (pixhash) {
 
2196
                        g_hash_table_foreach((GHashTable *) pixhash, list_image_free, NULL);
 
2197
                        g_hash_table_destroy((GHashTable *) pixhash);
 
2198
                }
 
2199
        }
 
2200
        virtual void SetFont(Font &font);
 
2201
        virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
 
2202
        virtual void SetAverageCharWidth(int width);
 
2203
        virtual void SetVisibleRows(int rows);
 
2204
        virtual int GetVisibleRows() const;
 
2205
        virtual PRectangle GetDesiredRect();
 
2206
        virtual int CaretFromEdge();
 
2207
        virtual void Clear();
 
2208
        virtual void Append(char *s, int type = -1);
 
2209
        virtual int Length();
 
2210
        virtual void Select(int n);
 
2211
        virtual int GetSelection();
 
2212
        virtual int Find(const char *prefix);
 
2213
        virtual void GetValue(int n, char *value, int len);
 
2214
        virtual void RegisterImage(int type, const char *xpm_data);
 
2215
        virtual void ClearRegisteredImages();
 
2216
        virtual void SetDoubleClickAction(CallBackAction action, void *data) {
 
2217
                doubleClickAction = action;
 
2218
                doubleClickActionData = data;
 
2219
        }
 
2220
        virtual void SetList(const char *listText, char separator, char typesep);
 
2221
};
 
2222
 
 
2223
ListBox *ListBox::Allocate() {
 
2224
        ListBoxX *lb = new ListBoxX();
 
2225
        return lb;
 
2226
}
 
2227
 
 
2228
static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
 
2229
        try {
 
2230
                ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
 
2231
                if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
 
2232
                        lb->doubleClickAction(lb->doubleClickActionData);
 
2233
                        return TRUE;
 
2234
                }
 
2235
 
 
2236
        } catch (...) {
 
2237
                // No pointer back to Scintilla to save status
 
2238
        }
 
2239
        return FALSE;
 
2240
}
 
2241
 
 
2242
/* Change the active color to the selected color so the listbox uses the color
 
2243
scheme that it would use if it had the focus. */
 
2244
static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
 
2245
        GtkStyle* style;
 
2246
 
 
2247
        g_return_if_fail(w != NULL);
 
2248
 
 
2249
        /* Copy the selected color to active.  Note that the modify calls will cause
 
2250
        recursive calls to this function after the value is updated and w->style to
 
2251
        be set to a new object */
 
2252
        style = gtk_widget_get_style(w);
 
2253
        if (style == NULL)
 
2254
                return;
 
2255
        if (!gdk_color_equal(&style->base[GTK_STATE_SELECTED], &style->base[GTK_STATE_ACTIVE]))
 
2256
                gtk_widget_modify_base(w, GTK_STATE_ACTIVE, &style->base[GTK_STATE_SELECTED]);
 
2257
 
 
2258
        style = gtk_widget_get_style(w);
 
2259
        if (style == NULL)
 
2260
                return;
 
2261
        if (!gdk_color_equal(&style->text[GTK_STATE_SELECTED], &style->text[GTK_STATE_ACTIVE]))
 
2262
                gtk_widget_modify_text(w, GTK_STATE_ACTIVE, &style->text[GTK_STATE_SELECTED]);
 
2263
}
 
2264
 
 
2265
void ListBoxX::Create(Window &, int, Point, int, bool) {
 
2266
        wid = gtk_window_new(GTK_WINDOW_POPUP);
 
2267
 
 
2268
        GtkWidget *frame = gtk_frame_new(NULL);
 
2269
        gtk_widget_show(frame);
 
2270
        gtk_container_add(GTK_CONTAINER(GetID()), frame);
 
2271
        gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
 
2272
        gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
 
2273
 
 
2274
        scroller = gtk_scrolled_window_new(NULL, NULL);
 
2275
        gtk_container_set_border_width(GTK_CONTAINER(scroller), 0);
 
2276
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
 
2277
                                       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
2278
        gtk_container_add(GTK_CONTAINER(frame), PWidget(scroller));
 
2279
        gtk_widget_show(PWidget(scroller));
 
2280
 
 
2281
        /* Tree and its model */
 
2282
        GtkListStore *store =
 
2283
                gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
 
2284
 
 
2285
        list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
 
2286
        g_signal_connect(G_OBJECT(list), "style-set", G_CALLBACK(StyleSet), NULL);
 
2287
 
 
2288
        GtkTreeSelection *selection =
 
2289
                gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
 
2290
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
 
2291
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
 
2292
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), FALSE);
 
2293
 
 
2294
        /* Columns */
 
2295
        GtkTreeViewColumn *column = gtk_tree_view_column_new();
 
2296
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
 
2297
        gtk_tree_view_column_set_title(column, "Autocomplete");
 
2298
 
 
2299
        pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
 
2300
        gtk_cell_renderer_set_fixed_size(pixbuf_renderer, 0, -1);
 
2301
        gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
 
2302
        gtk_tree_view_column_add_attribute(column, pixbuf_renderer,
 
2303
                                                                                "pixbuf", PIXBUF_COLUMN);
 
2304
 
 
2305
        GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
 
2306
        gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1);
 
2307
        gtk_tree_view_column_pack_start(column, renderer, TRUE);
 
2308
        gtk_tree_view_column_add_attribute(column, renderer,
 
2309
                                                                                "text", TEXT_COLUMN);
 
2310
 
 
2311
        gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
 
2312
        if (g_object_class_find_property(G_OBJECT_GET_CLASS(list), "fixed-height-mode"))
 
2313
                g_object_set(G_OBJECT(list), "fixed-height-mode", TRUE, NULL);
 
2314
 
 
2315
        GtkWidget *wid = PWidget(list); // No code inside the G_OBJECT macro
 
2316
        gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
 
2317
        gtk_widget_show(wid);
 
2318
        g_signal_connect(G_OBJECT(wid), "button_press_event",
 
2319
                           G_CALLBACK(ButtonPress), this);
 
2320
        gtk_widget_realize(PWidget(wid));
 
2321
}
 
2322
 
 
2323
void ListBoxX::SetFont(Font &scint_font) {
 
2324
        // Only do for Pango font as there have been crashes for GDK fonts
 
2325
        if (Created() && PFont(scint_font)->pfd) {
 
2326
                // Current font is Pango font
 
2327
                gtk_widget_modify_font(PWidget(list), PFont(scint_font)->pfd);
 
2328
        }
 
2329
}
 
2330
 
 
2331
void ListBoxX::SetAverageCharWidth(int width) {
 
2332
        aveCharWidth = width;
 
2333
}
 
2334
 
 
2335
void ListBoxX::SetVisibleRows(int rows) {
 
2336
        desiredVisibleRows = rows;
 
2337
}
 
2338
 
 
2339
int ListBoxX::GetVisibleRows() const {
 
2340
        return desiredVisibleRows;
 
2341
}
 
2342
 
 
2343
PRectangle ListBoxX::GetDesiredRect() {
 
2344
        // Before any size allocated pretend its 100 wide so not scrolled
 
2345
        PRectangle rc(0, 0, 100, 100);
 
2346
        if (wid) {
 
2347
                int rows = Length();
 
2348
                if ((rows == 0) || (rows > desiredVisibleRows))
 
2349
                        rows = desiredVisibleRows;
 
2350
 
 
2351
                GtkRequisition req;
 
2352
                int height;
 
2353
 
 
2354
                // First calculate height of the clist for our desired visible
 
2355
                // row count otherwise it tries to expand to the total # of rows
 
2356
                // Get cell height
 
2357
                int row_width=0;
 
2358
                int row_height=0;
 
2359
                GtkTreeViewColumn * column =
 
2360
                        gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
 
2361
                gtk_tree_view_column_cell_get_size(column, NULL,
 
2362
                        NULL, NULL, &row_width, &row_height);
 
2363
                int ythickness = PWidget(list)->style->ythickness;
 
2364
                height = (rows * row_height
 
2365
                          + 2 * (ythickness
 
2366
                                 + GTK_CONTAINER(PWidget(list))->border_width + 1));
 
2367
                gtk_widget_set_size_request(GTK_WIDGET(PWidget(list)), -1, height);
 
2368
 
 
2369
                // Get the size of the scroller because we set usize on the window
 
2370
                gtk_widget_size_request(GTK_WIDGET(scroller), &req);
 
2371
                rc.right = req.width;
 
2372
                rc.bottom = req.height;
 
2373
 
 
2374
                gtk_widget_set_size_request(GTK_WIDGET(list), -1, -1);
 
2375
                int width = maxItemCharacters;
 
2376
                if (width < 12)
 
2377
                        width = 12;
 
2378
                rc.right = width * (aveCharWidth + aveCharWidth / 3);
 
2379
                if (Length() > rows)
 
2380
                        rc.right = rc.right + 16;
 
2381
        }
 
2382
        return rc;
 
2383
}
 
2384
 
 
2385
int ListBoxX::CaretFromEdge() {
 
2386
        gint renderer_width, renderer_height;
 
2387
        gtk_cell_renderer_get_fixed_size(pixbuf_renderer, &renderer_width,
 
2388
                                                &renderer_height);
 
2389
        return 4 + renderer_width;
 
2390
}
 
2391
 
 
2392
void ListBoxX::Clear() {
 
2393
        GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
 
2394
        gtk_list_store_clear(GTK_LIST_STORE(model));
 
2395
        maxItemCharacters = 0;
 
2396
}
 
2397
 
 
2398
static void init_pixmap(ListImage *list_image) {
 
2399
        const char *textForm = list_image->xpm_data;
 
2400
        const char * const * xpm_lineform = reinterpret_cast<const char * const *>(textForm);
 
2401
        const char **xpm_lineformfromtext = 0;
 
2402
        // The XPM data can be either in atext form as will be read from a file
 
2403
        // or in a line form (array of char  *) as will be used for images defined in code.
 
2404
        // Test for text form and convert to line form
 
2405
        if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
 
2406
                // Test done is two parts to avoid possibility of overstepping the memory
 
2407
                // if memcmp implemented strangely. Must be 4 bytes at least at destination.
 
2408
                xpm_lineformfromtext = XPM::LinesFormFromTextForm(textForm);
 
2409
                xpm_lineform = xpm_lineformfromtext;
 
2410
        }
 
2411
 
 
2412
        // Drop any existing pixmap/bitmap as data may have changed
 
2413
        if (list_image->pixbuf)
 
2414
                g_object_unref(list_image->pixbuf);
 
2415
        list_image->pixbuf =
 
2416
                gdk_pixbuf_new_from_xpm_data((const gchar**)xpm_lineform);
 
2417
        delete []xpm_lineformfromtext;
 
2418
}
 
2419
 
 
2420
#define SPACING 5
 
2421
 
 
2422
void ListBoxX::Append(char *s, int type) {
 
2423
        ListImage *list_image = NULL;
 
2424
        if ((type >= 0) && pixhash) {
 
2425
                list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash
 
2426
                             , (gconstpointer) GINT_TO_POINTER(type));
 
2427
        }
 
2428
        GtkTreeIter iter;
 
2429
        GtkListStore *store =
 
2430
                GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
 
2431
        gtk_list_store_append(GTK_LIST_STORE(store), &iter);
 
2432
        if (list_image) {
 
2433
                if (NULL == list_image->pixbuf)
 
2434
                        init_pixmap(list_image);
 
2435
                if (list_image->pixbuf) {
 
2436
                        gtk_list_store_set(GTK_LIST_STORE(store), &iter,
 
2437
                                                                PIXBUF_COLUMN, list_image->pixbuf,
 
2438
                                                                TEXT_COLUMN, s, -1);
 
2439
 
 
2440
                        gint pixbuf_width = gdk_pixbuf_get_width(list_image->pixbuf);
 
2441
                        gint renderer_height, renderer_width;
 
2442
                        gtk_cell_renderer_get_fixed_size(pixbuf_renderer,
 
2443
                                                                &renderer_width, &renderer_height);
 
2444
                        if (pixbuf_width > renderer_width)
 
2445
                                gtk_cell_renderer_set_fixed_size(pixbuf_renderer,
 
2446
                                                                pixbuf_width, -1);
 
2447
                } else {
 
2448
                        gtk_list_store_set(GTK_LIST_STORE(store), &iter,
 
2449
                                                                TEXT_COLUMN, s, -1);
 
2450
                }
 
2451
        } else {
 
2452
                        gtk_list_store_set(GTK_LIST_STORE(store), &iter,
 
2453
                                                                TEXT_COLUMN, s, -1);
 
2454
        }
 
2455
        size_t len = strlen(s);
 
2456
        if (maxItemCharacters < len)
 
2457
                maxItemCharacters = len;
 
2458
}
 
2459
 
 
2460
int ListBoxX::Length() {
 
2461
        if (wid)
 
2462
                return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
 
2463
                                                                                           (GTK_TREE_VIEW(list)), NULL);
 
2464
        return 0;
 
2465
}
 
2466
 
 
2467
void ListBoxX::Select(int n) {
 
2468
        GtkTreeIter iter;
 
2469
        GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
 
2470
        GtkTreeSelection *selection =
 
2471
                gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
 
2472
 
 
2473
        if (n < 0) {
 
2474
                gtk_tree_selection_unselect_all(selection);
 
2475
                return;
 
2476
        }
 
2477
 
 
2478
        bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
 
2479
        if (valid) {
 
2480
                gtk_tree_selection_select_iter(selection, &iter);
 
2481
 
 
2482
                // Move the scrollbar to show the selection.
 
2483
                int total = Length();
 
2484
                GtkAdjustment *adj =
 
2485
                        gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list));
 
2486
                gfloat value = ((gfloat)n / total) * (adj->upper - adj->lower)
 
2487
                                                        + adj->lower - adj->page_size / 2;
 
2488
 
 
2489
                // Get cell height
 
2490
                int row_width;
 
2491
                int row_height;
 
2492
                GtkTreeViewColumn * column =
 
2493
                        gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
 
2494
                gtk_tree_view_column_cell_get_size(column, NULL, NULL,
 
2495
                                                                                        NULL, &row_width, &row_height);
 
2496
 
 
2497
                int rows = Length();
 
2498
                if ((rows == 0) || (rows > desiredVisibleRows))
 
2499
                        rows = desiredVisibleRows;
 
2500
                if (rows & 0x1) {
 
2501
                        // Odd rows to display -- We are now in the middle.
 
2502
                        // Align it so that we don't chop off rows.
 
2503
                        value += (gfloat)row_height / 2.0;
 
2504
                }
 
2505
                // Clamp it.
 
2506
                value = (value < 0)? 0 : value;
 
2507
                value = (value > (adj->upper - adj->page_size))?
 
2508
                                        (adj->upper - adj->page_size) : value;
 
2509
 
 
2510
                // Set it.
 
2511
                gtk_adjustment_set_value(adj, value);
 
2512
        } else {
 
2513
                gtk_tree_selection_unselect_all(selection);
 
2514
        }
 
2515
}
 
2516
 
 
2517
int ListBoxX::GetSelection() {
 
2518
        GtkTreeIter iter;
 
2519
        GtkTreeModel *model;
 
2520
        GtkTreeSelection *selection;
 
2521
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
 
2522
        if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
 
2523
                GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
 
2524
                int *indices = gtk_tree_path_get_indices(path);
 
2525
                // Don't free indices.
 
2526
                if (indices)
 
2527
                        return indices[0];
 
2528
        }
 
2529
        return -1;
 
2530
}
 
2531
 
 
2532
int ListBoxX::Find(const char *prefix) {
 
2533
        GtkTreeIter iter;
 
2534
        GtkTreeModel *model =
 
2535
                gtk_tree_view_get_model(GTK_TREE_VIEW(list));
 
2536
        bool valid = gtk_tree_model_get_iter_first(model, &iter) != FALSE;
 
2537
        int i = 0;
 
2538
        while(valid) {
 
2539
                gchar *s;
 
2540
                gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
 
2541
                if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
 
2542
                        g_free(s);
 
2543
                        return i;
 
2544
                }
 
2545
                g_free(s);
 
2546
                valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
 
2547
                i++;
 
2548
        }
 
2549
        return -1;
 
2550
}
 
2551
 
 
2552
void ListBoxX::GetValue(int n, char *value, int len) {
 
2553
        char *text = NULL;
 
2554
        GtkTreeIter iter;
 
2555
        GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
 
2556
        bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
 
2557
        if (valid) {
 
2558
                gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
 
2559
        }
 
2560
        if (text && len > 0) {
 
2561
                strncpy(value, text, len);
 
2562
                value[len - 1] = '\0';
 
2563
        } else {
 
2564
                value[0] = '\0';
 
2565
        }
 
2566
        g_free(text);
 
2567
}
 
2568
 
 
2569
// g_return_if_fail causes unnecessary compiler warning in release compile.
 
2570
#ifdef _MSC_VER
 
2571
#pragma warning(disable: 4127)
 
2572
#endif
 
2573
 
 
2574
void ListBoxX::RegisterImage(int type, const char *xpm_data) {
 
2575
        g_return_if_fail(xpm_data);
 
2576
 
 
2577
        // Saved and use the saved copy so caller's copy can disappear.
 
2578
        xset.Add(type, xpm_data);
 
2579
        XPM *pxpm = xset.Get(type);
 
2580
        xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
 
2581
 
 
2582
        if (!pixhash) {
 
2583
                pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
 
2584
        }
 
2585
        ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
 
2586
                (gconstpointer) GINT_TO_POINTER(type));
 
2587
        if (list_image) {
 
2588
                // Drop icon already registered
 
2589
                if (list_image->pixbuf)
 
2590
                        g_object_unref(list_image->pixbuf);
 
2591
                list_image->pixbuf = NULL;
 
2592
                list_image->xpm_data = xpm_data;
 
2593
        } else {
 
2594
                list_image = g_new0(ListImage, 1);
 
2595
                list_image->xpm_data = xpm_data;
 
2596
                g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
 
2597
                        (gpointer) list_image);
 
2598
        }
 
2599
}
 
2600
 
 
2601
void ListBoxX::ClearRegisteredImages() {
 
2602
        xset.Clear();
 
2603
}
 
2604
 
 
2605
void ListBoxX::SetList(const char *listText, char separator, char typesep) {
 
2606
        Clear();
 
2607
        int count = strlen(listText) + 1;
 
2608
        char *words = new char[count];
 
2609
        if (words) {
 
2610
                memcpy(words, listText, count);
 
2611
                char *startword = words;
 
2612
                char *numword = NULL;
 
2613
                int i = 0;
 
2614
                for (; words[i]; i++) {
 
2615
                        if (words[i] == separator) {
 
2616
                                words[i] = '\0';
 
2617
                                if (numword)
 
2618
                                        *numword = '\0';
 
2619
                                Append(startword, numword?atoi(numword + 1):-1);
 
2620
                                startword = words + i + 1;
 
2621
                                numword = NULL;
 
2622
                        } else if (words[i] == typesep) {
 
2623
                                numword = words + i;
 
2624
                        }
 
2625
                }
 
2626
                if (startword) {
 
2627
                        if (numword)
 
2628
                                *numword = '\0';
 
2629
                        Append(startword, numword?atoi(numword + 1):-1);
 
2630
                }
 
2631
                delete []words;
 
2632
        }
 
2633
}
 
2634
 
 
2635
Menu::Menu() : mid(0) {}
 
2636
 
 
2637
void Menu::CreatePopUp() {
 
2638
        Destroy();
 
2639
        mid = gtk_menu_new();
 
2640
#if GLIB_CHECK_VERSION(2,10,0)
 
2641
         g_object_ref_sink(G_OBJECT(mid));
 
2642
#else
 
2643
        g_object_ref(G_OBJECT(mid));
 
2644
        gtk_object_sink(GTK_OBJECT(G_OBJECT(mid)));
 
2645
#endif
 
2646
}
 
2647
 
 
2648
void Menu::Destroy() {
 
2649
        if (mid)
 
2650
                g_object_unref(G_OBJECT(mid));
 
2651
        mid = 0;
 
2652
}
 
2653
 
 
2654
static void  MenuPositionFunc(GtkMenu *, gint *x, gint *y, gboolean *, gpointer userData) {
 
2655
        sptr_t intFromPointer = reinterpret_cast<sptr_t>(userData);
 
2656
        *x = intFromPointer & 0xffff;
 
2657
        *y = intFromPointer >> 16;
 
2658
}
 
2659
 
 
2660
void Menu::Show(Point pt, Window &) {
 
2661
        int screenHeight = gdk_screen_height();
 
2662
        int screenWidth = gdk_screen_width();
 
2663
        GtkMenu *widget = reinterpret_cast<GtkMenu *>(mid);
 
2664
        gtk_widget_show_all(GTK_WIDGET(widget));
 
2665
        GtkRequisition requisition;
 
2666
        gtk_widget_size_request(GTK_WIDGET(widget), &requisition);
 
2667
        if ((pt.x + requisition.width) > screenWidth) {
 
2668
                pt.x = screenWidth - requisition.width;
 
2669
        }
 
2670
        if ((pt.y + requisition.height) > screenHeight) {
 
2671
                pt.y = screenHeight - requisition.height;
 
2672
        }
 
2673
        gtk_menu_popup(widget, NULL, NULL, MenuPositionFunc,
 
2674
                reinterpret_cast<void *>((pt.y << 16) | pt.x), 0,
 
2675
                gtk_get_current_event_time());
 
2676
}
 
2677
 
 
2678
ElapsedTime::ElapsedTime() {
 
2679
        GTimeVal curTime;
 
2680
        g_get_current_time(&curTime);
 
2681
        bigBit = curTime.tv_sec;
 
2682
        littleBit = curTime.tv_usec;
 
2683
}
 
2684
 
 
2685
class DynamicLibraryImpl : public DynamicLibrary {
 
2686
protected:
 
2687
        GModule* m;
 
2688
public:
 
2689
        DynamicLibraryImpl(const char *modulePath) {
 
2690
                m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
 
2691
        }
 
2692
 
 
2693
        virtual ~DynamicLibraryImpl() {
 
2694
                if (m != NULL)
 
2695
                        g_module_close(m);
 
2696
        }
 
2697
 
 
2698
        // Use g_module_symbol to get a pointer to the relevant function.
 
2699
        virtual Function FindFunction(const char *name) {
 
2700
                if (m != NULL) {
 
2701
                        gpointer fn_address = NULL;
 
2702
                        gboolean status = g_module_symbol(m, name, &fn_address);
 
2703
                        if (status)
 
2704
                                return static_cast<Function>(fn_address);
 
2705
                        else
 
2706
                                return NULL;
 
2707
                } else
 
2708
                        return NULL;
 
2709
        }
 
2710
 
 
2711
        virtual bool IsValid() {
 
2712
                return m != NULL;
 
2713
        }
 
2714
};
 
2715
 
 
2716
DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
 
2717
        return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
 
2718
}
 
2719
 
 
2720
double ElapsedTime::Duration(bool reset) {
 
2721
        GTimeVal curTime;
 
2722
        g_get_current_time(&curTime);
 
2723
        long endBigBit = curTime.tv_sec;
 
2724
        long endLittleBit = curTime.tv_usec;
 
2725
        double result = 1000000.0 * (endBigBit - bigBit);
 
2726
        result += endLittleBit - littleBit;
 
2727
        result /= 1000000.0;
 
2728
        if (reset) {
 
2729
                bigBit = endBigBit;
 
2730
                littleBit = endLittleBit;
 
2731
        }
 
2732
        return result;
 
2733
}
 
2734
 
 
2735
ColourDesired Platform::Chrome() {
 
2736
        return ColourDesired(0xe0, 0xe0, 0xe0);
 
2737
}
 
2738
 
 
2739
ColourDesired Platform::ChromeHighlight() {
 
2740
        return ColourDesired(0xff, 0xff, 0xff);
 
2741
}
 
2742
 
 
2743
const char *Platform::DefaultFont() {
 
2744
#ifdef G_OS_WIN32
 
2745
        return "Lucida Console";
 
2746
#else
 
2747
        return "!Sans";
 
2748
#endif
 
2749
}
 
2750
 
 
2751
int Platform::DefaultFontSize() {
 
2752
#ifdef G_OS_WIN32
 
2753
        return 10;
 
2754
#else
 
2755
        return 12;
 
2756
#endif
 
2757
}
 
2758
 
 
2759
unsigned int Platform::DoubleClickTime() {
 
2760
        return 500;     // Half a second
 
2761
}
 
2762
 
 
2763
bool Platform::MouseButtonBounce() {
 
2764
        return true;
 
2765
}
 
2766
 
 
2767
void Platform::DebugDisplay(const char *s) {
 
2768
        fprintf(stderr, "%s", s);
 
2769
}
 
2770
 
 
2771
bool Platform::IsKeyDown(int) {
 
2772
        // TODO: discover state of keys in GTK+/X
 
2773
        return false;
 
2774
}
 
2775
 
 
2776
long Platform::SendScintilla(
 
2777
    WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
 
2778
        return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
 
2779
}
 
2780
 
 
2781
long Platform::SendScintillaPointer(
 
2782
    WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
 
2783
        return scintilla_send_message(SCINTILLA(w), msg, wParam,
 
2784
                                      reinterpret_cast<sptr_t>(lParam));
 
2785
}
 
2786
 
 
2787
bool Platform::IsDBCSLeadByte(int codePage, char ch) {
 
2788
        // Byte ranges found in Wikipedia articles with relevant search strings in each case
 
2789
        unsigned char uch = static_cast<unsigned char>(ch);
 
2790
        switch (codePage) {
 
2791
                case 932:
 
2792
                        // Shift_jis
 
2793
                        return ((uch >= 0x81) && (uch <= 0x9F)) ||
 
2794
                                ((uch >= 0xE0) && (uch <= 0xEF));
 
2795
                case 936:
 
2796
                        // GBK
 
2797
                        return (uch >= 0x81) && (uch <= 0xFE);
 
2798
                case 950:
 
2799
                        // Big5
 
2800
                        return (uch >= 0x81) && (uch <= 0xFE);
 
2801
                // Korean EUC-KR may be code page 949.
 
2802
        }
 
2803
        return false;
 
2804
}
 
2805
 
 
2806
int Platform::DBCSCharLength(int codePage, const char *s) {
 
2807
        if (codePage == 932 || codePage == 936 || codePage == 950) {
 
2808
                return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1;
 
2809
        } else {
 
2810
                int bytes = mblen(s, MB_CUR_MAX);
 
2811
                if (bytes >= 1)
 
2812
                        return bytes;
 
2813
                else
 
2814
                        return 1;
 
2815
        }
 
2816
}
 
2817
 
 
2818
int Platform::DBCSCharMaxLength() {
 
2819
        return MB_CUR_MAX;
 
2820
        //return 2;
 
2821
}
 
2822
 
 
2823
// These are utility functions not really tied to a platform
 
2824
 
 
2825
int Platform::Minimum(int a, int b) {
 
2826
        if (a < b)
 
2827
                return a;
 
2828
        else
 
2829
                return b;
 
2830
}
 
2831
 
 
2832
int Platform::Maximum(int a, int b) {
 
2833
        if (a > b)
 
2834
                return a;
 
2835
        else
 
2836
                return b;
 
2837
}
 
2838
 
 
2839
//#define TRACE
 
2840
 
 
2841
#ifdef TRACE
 
2842
void Platform::DebugPrintf(const char *format, ...) {
 
2843
        char buffer[2000];
 
2844
        va_list pArguments;
 
2845
        va_start(pArguments, format);
 
2846
        vsprintf(buffer, format, pArguments);
 
2847
        va_end(pArguments);
 
2848
        Platform::DebugDisplay(buffer);
 
2849
}
 
2850
#else
 
2851
void Platform::DebugPrintf(const char *, ...) {}
 
2852
 
 
2853
#endif
 
2854
 
 
2855
// Not supported for GTK+
 
2856
static bool assertionPopUps = true;
 
2857
 
 
2858
bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
 
2859
        bool ret = assertionPopUps;
 
2860
        assertionPopUps = assertionPopUps_;
 
2861
        return ret;
 
2862
}
 
2863
 
 
2864
void Platform::Assert(const char *c, const char *file, int line) {
 
2865
        char buffer[2000];
 
2866
        sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
 
2867
        strcat(buffer, "\r\n");
 
2868
        Platform::DebugDisplay(buffer);
 
2869
        abort();
 
2870
}
 
2871
 
 
2872
int Platform::Clamp(int val, int minVal, int maxVal) {
 
2873
        if (val > maxVal)
 
2874
                val = maxVal;
 
2875
        if (val < minVal)
 
2876
                val = minVal;
 
2877
        return val;
 
2878
}
 
2879
 
 
2880
void Platform_Initialise() {
 
2881
        FontMutexAllocate();
 
2882
}
 
2883
 
 
2884
void Platform_Finalise() {
 
2885
        FontMutexFree();
 
2886
}