summaryrefslogtreecommitdiffstats
path: root/Lib/wave.py
blob: 60c0b98319ffe0fb0dde4a8b935ce1cec6f6870b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
"""Stuff to parse WAVE files.

Usage.

Reading WAVE files:
      f = wave.open(file, 'r')
where file is either the name of a file or an open file pointer.
The open file pointer must have methods read(), seek(), and close().
When the setpos() and rewind() methods are not used, the seek()
method is not  necessary.

This returns an instance of a class with the following public methods:
      getnchannels()  -- returns number of audio channels (1 for
                         mono, 2 for stereo)
      getsampwidth()  -- returns sample width in bytes
      getframerate()  -- returns sampling frequency
      getnframes()    -- returns number of audio frames
      getcomptype()   -- returns compression type ('NONE' for linear samples)
      getcompname()   -- returns human-readable version of
                         compression type ('not compressed' linear samples)
      getparams()     -- returns a tuple consisting of all of the
                         above in the above order
      getmarkers()    -- returns None (for compatibility with the
                         aifc module)
      getmark(id)     -- raises an error since the mark does not
                         exist (for compatibility with the aifc module)
      readframes(n)   -- returns at most n frames of audio
      rewind()        -- rewind to the beginning of the audio stream
      setpos(pos)     -- seek to the specified position
      tell()          -- return the current position
      close()         -- close the instance (make it unusable)
The position returned by tell() and the position given to setpos()
are compatible and have nothing to do with the actual position in the
file.
The close() method is called automatically when the class instance
is destroyed.

Writing WAVE files:
      f = wave.open(file, 'w')
where file is either the name of a file or an open file pointer.
The open file pointer must have methods write(), tell(), seek(), and
close().

This returns an instance of a class with the following public methods:
      setnchannels(n) -- set the number of channels
      setsampwidth(n) -- set the sample width
      setframerate(n) -- set the frame rate
      setnframes(n)   -- set the number of frames
      setcomptype(type, name)
                      -- set the compression type and the
                         human-readable compression type
      setparams(tuple)
                      -- set all parameters at once
      tell()          -- return current position in output file
      writeframesraw(data)
                      -- write audio frames without pathing up the
                         file header
      writeframes(data)
                      -- write audio frames and patch up the file header
      close()         -- patch up the file header and close the
                         output file
You should set the parameters before the first writeframesraw or
writeframes.  The total number of frames does not need to be set,
but when it is set to the correct value, the header does not have to
be patched up.
It is best to first set all parameters, perhaps possibly the
compression type, and then write audio frames using writeframesraw.
When all frames have been written, either call writeframes('') or
close() to patch up the sizes in the header.
The close() method is called automatically when the class instance
is destroyed.
"""

import __builtin__

Error = 'wave.Error'

WAVE_FORMAT_PCM = 0x0001

_array_fmts = None, 'b', 'h', None, 'l'

# Determine endian-ness
import struct
if struct.pack("h", 1) == "\000\001":
    big_endian = 1
else:
    big_endian = 0

from chunk import Chunk

class Wave_read:
    """Variables used in this class:

    These variables are available to the user though appropriate
    methods of this class:
    _file -- the open file with methods read(), close(), and seek()
              set through the __init__() method
    _nchannels -- the number of audio channels
              available through the getnchannels() method
    _nframes -- the number of audio frames
              available through the getnframes() method
    _sampwidth -- the number of bytes per audio sample
              available through the getsampwidth() method
    _framerate -- the sampling frequency
              available through the getframerate() method
    _comptype -- the AIFF-C compression type ('NONE' if AIFF)
              available through the getcomptype() method
    _compname -- the human-readable AIFF-C compression type
              available through the getcomptype() method
    _soundpos -- the position in the audio stream
              available through the tell() method, set through the
              setpos() method

    These variables are used internally only:
    _fmt_chunk_read -- 1 iff the FMT chunk has been read
    _data_seek_needed -- 1 iff positioned correctly in audio
              file for readframes()
    _data_chunk -- instantiation of a chunk class for the DATA chunk
    _framesize -- size of one frame in the file
    """

    def initfp(self, file):
        self._convert = None
        self._soundpos = 0
        self._file = Chunk(file, bigendian = 0)
        if self._file.getname() != 'RIFF':
            raise Error, 'file does not start with RIFF id'
        if self._file.read(4) != 'WAVE':
            raise Error, 'not a WAVE file'
        self._fmt_chunk_read = 0
        self._data_chunk = None
        while 1:
            self._data_seek_needed = 1
            try:
                chunk = Chunk(self._file, bigendian = 0)
            except EOFError:
                break
            chunkname = chunk.getname()
            if chunkname == 'fmt ':
                self._read_fmt_chunk(chunk)
                self._fmt_chunk_read = 1
            elif chunkname == 'data':
                if not self._fmt_chunk_read:
                    raise Error, 'data chunk before fmt chunk'
                self._data_chunk = chunk
                self._nframes = chunk.chunksize / self._framesize
                self._data_seek_needed = 0
                break
            chunk.skip()
        if not self._fmt_chunk_read or not self._data_chunk:
            raise Error, 'fmt chunk and/or data chunk missing'

    def __init__(self, f):
        if type(f) == type(''):
            f = __builtin__.open(f, 'rb')
        # else, assume it is an open file object already
        self.initfp(f)

    #
    # User visible methods.
    #
    def getfp(self):
        return self._file

    def rewind(self):
        self._data_seek_needed = 1
        self._soundpos = 0

    def close(self):
        self._file = None

    def tell(self):
        return self._soundpos

    def getnchannels(self):
        return self._nchannels

    def getnframes(self):
        return self._nframes

    def getsampwidth(self):
        return self._sampwidth

    def getframerate(self):
        return self._framerate

    def getcomptype(self):
        return self._comptype

    def getcompname(self):
        return self._compname

    def getparams(self):
        return self.getnchannels(), self.getsampwidth(), \
               self.getframerate(), self.getnframes(), \
               self.getcomptype(), self.getcompname()

    def getmarkers(self):
        return None

    def getmark(self, id):
        raise Error, 'no marks'

    def setpos(self, pos):
        if pos < 0 or pos > self._nframes:
            raise Error, 'position not in range'
        self._soundpos = pos
        self._data_seek_needed = 1

    def readframes(self, nframes):
        if self._data_seek_needed:
            self._data_chunk.seek(0, 0)
            pos = self._soundpos * self._framesize
            if pos:
                self._data_chunk.seek(pos, 0)
            self._data_seek_needed = 0
        if nframes == 0:
            return ''
        if self._sampwidth > 1 and big_endian:
            # unfortunately the fromfile() method does not take
            # something that only looks like a file object, so
            # we have to reach into the innards of the chunk object
            import array
            chunk = self._data_chunk
            data = array.array(_array_fmts[self._sampwidth])
            nitems = nframes * self._nchannels
            if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
                nitems = (chunk.chunksize - chunk.size_read) / self._sampwidth
            data.fromfile(chunk.file.file, nitems)
            # "tell" data chunk how much was read
            chunk.size_read = chunk.size_read + nitems * self._sampwidth
            # do the same for the outermost chunk
            chunk = chunk.file
            chunk.size_read = chunk.size_read + nitems * self._sampwidth
            data.byteswap()
            data = data.tostring()
        else:
            data = self._data_chunk.read(nframes * self._framesize)
        if self._convert and data:
            data = self._convert(data)
        self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth)
        return data

    #
    # Internal methods.
    #

    def _read_fmt_chunk(self, chunk):
        wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<hhllh', chunk.read(14))
        if wFormatTag == WAVE_FORMAT_PCM:
            sampwidth = struct.unpack('<h', chunk.read(2))[0]
            self._sampwidth = (sampwidth + 7) / 8
        else:
            raise Error, 'unknown format: ' + `wFormatTag`
        self._framesize = self._nchannels * self._sampwidth
        self._comptype = 'NONE'
        self._compname = 'not compressed'

class Wave_write:
    """Variables used in this class:

    These variables are user settable through appropriate methods
    of this class:
    _file -- the open file with methods write(), close(), tell(), seek()
              set through the __init__() method
    _comptype -- the AIFF-C compression type ('NONE' in AIFF)
              set through the setcomptype() or setparams() method
    _compname -- the human-readable AIFF-C compression type
              set through the setcomptype() or setparams() method
    _nchannels -- the number of audio channels
              set through the setnchannels() or setparams() method
    _sampwidth -- the number of bytes per audio sample
              set through the setsampwidth() or setparams() method
    _framerate -- the sampling frequency
              set through the setframerate() or setparams() method
    _nframes -- the number of audio frames written to the header
              set through the setnframes() or setparams() method

    These variables are used internally only:
    _datalength -- the size of the audio samples written to the header
    _nframeswritten -- the number of frames actually written
    _datawritten -- the size of the audio samples actually written
    """

    def __init__(self, f):
        if type(f) == type(''):
            f = __builtin__.open(f, 'wb')
        self.initfp(f)

    def initfp(self, file):
        self._file = file
        self._convert = None
        self._nchannels = 0
        self._sampwidth = 0
        self._framerate = 0
        self._nframes = 0
        self._nframeswritten = 0
        self._datawritten = 0
        self._datalength = 0

    def __del__(self):
        if self._file:
            self.close()

    #
    # User visible methods.
    #
    def setnchannels(self, nchannels):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        if nchannels < 1:
            raise Error, 'bad # of channels'
        self._nchannels = nchannels

    def getnchannels(self):
        if not self._nchannels:
            raise Error, 'number of channels not set'
        return self._nchannels

    def setsampwidth(self, sampwidth):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        if sampwidth < 1 or sampwidth > 4:
            raise Error, 'bad sample width'
        self._sampwidth = sampwidth

    def getsampwidth(self):
        if not self._sampwidth:
            raise Error, 'sample width not set'
        return self._sampwidth

    def setframerate(self, framerate):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        if framerate <= 0:
            raise Error, 'bad frame rate'
        self._framerate = framerate

    def getframerate(self):
        if not self._framerate:
            raise Error, 'frame rate not set'
        return self._framerate

    def setnframes(self, nframes):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        self._nframes = nframes

    def getnframes(self):
        return self._nframeswritten

    def setcomptype(self, comptype, compname):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        if comptype not in ('NONE',):
            raise Error, 'unsupported compression type'
        self._comptype = comptype
        self._compname = compname

    def getcomptype(self):
        return self._comptype

    def getcompname(self):
        return self._compname

    def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)):
        if self._datawritten:
            raise Error, 'cannot change parameters after starting to write'
        self.setnchannels(nchannels)
        self.setsampwidth(sampwidth)
        self.setframerate(framerate)
        self.setnframes(nframes)
        self.setcomptype(comptype, compname)

    def getparams(self):
        if not self._nchannels or not self._sampwidth or not self._framerate:
            raise Error, 'not all parameters set'
        return self._nchannels, self._sampwidth, self._framerate, \
              self._nframes, self._comptype, self._compname

    def setmark(self, id, pos, name):
        raise Error, 'setmark() not supported'

    def getmark(self, id):
        raise Error, 'no marks'

    def getmarkers(self):
        return None
                
    def tell(self):
        return self._nframeswritten

    def writeframesraw(self, data):
        self._ensure_header_written(len(data))
        nframes = len(data) / (self._sampwidth * self._nchannels)
        if self._convert:
            data = self._convert(data)
        if self._sampwidth > 1 and big_endian:
            import array
            data = array.array(_array_fmts[self._sampwidth], data)
            data.byteswap()
            data.tofile(self._file)
            self._datawritten = self._datawritten + len(data) * self._sampwidth
        else:
            self._file.write(data)
            self._datawritten = self._datawritten + len(data)
        self._nframeswritten = self._nframeswritten + nframes

    def writeframes(self, data):
        self.writeframesraw(data)
        if self._datalength != self._datawritten:
            self._patchheader()

    def close(self):
        self._ensure_header_written(0)
        if self._datalength != self._datawritten:
            self._patchheader()
        self._file.flush()
        self._file = None

    #
    # Internal methods.
    #

    def _ensure_header_written(self, datasize):
        if not self._datawritten:
            if not self._nchannels:
                raise Error, '# channels not specified'
            if not self._sampwidth:
                raise Error, 'sample width not specified'
            if not self._framerate:
                raise Error, 'sampling rate not specified'
            self._write_header(datasize)

    def _write_header(self, initlength):
        self._file.write('RIFF')
        if not self._nframes:
            self._nframes = initlength / (self._nchannels * self._sampwidth)
        self._datalength = self._nframes * self._nchannels * self._sampwidth
        self._form_length_pos = self._file.tell()
        self._file.write(struct.pack('<lsslhhllhhs',
            36 + self._datalength, 'WAVE', 'fmt ', 16,
            WAVE_FORMAT_PCM, self._nchannels, self._framerate,
            self._nchannels * self._framerate * self._sampwidth,
            self._nchannels * self._sampwidth,
            self._sampwidth * 8, 'data'))
        self._data_length_pos = self._file.tell()
        self._file.write(struct.pack('<l', self._datalength))

    def _patchheader(self):
        if self._datawritten == self._datalength:
            return
        curpos = self._file.tell()
        self._file.seek(self._form_length_pos, 0)
        self._file.write(struct.pack('<l', 36 + self._datawritten))
        self._file.seek(self._data_length_pos, 0)
        self._file.write(struct.pack('<l', self._datawritten))
        self._file.seek(curpos, 0)
        self._datalength = self._datawritten

def open(f, mode=None):
    if mode is None:
        if hasattr(f, 'mode'):
            mode = f.mode
        else:
            mode = 'rb'
    if mode in ('r', 'rb'):
        return Wave_read(f)
    elif mode in ('w', 'wb'):
        return Wave_write(f)
    else:
        raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"

openfp = open # B/W compatibility
t">; if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) { if (isEnabled() && underMouse() && !isChecked() && !isDown()) opt.state |= QStyle::State_Raised; if (isChecked()) opt.state |= QStyle::State_On; if (isDown()) opt.state |= QStyle::State_Sunken; style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this); } opt.icon = icon(); opt.subControls = 0; opt.activeSubControls = 0; opt.features = QStyleOptionToolButton::None; opt.arrowType = Qt::NoArrow; int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this); opt.iconSize = QSize(size, size); style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, this); } /****************************************************************************** ** QDockWidgetLayout */ QDockWidgetLayout::QDockWidgetLayout(QWidget *parent) : QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0) { } QDockWidgetLayout::~QDockWidgetLayout() { qDeleteAll(item_list); } bool QDockWidgetLayout::nativeWindowDeco() const { return nativeWindowDeco(parentWidget()->isWindow()); } bool QDockWidgetLayout::nativeWindowDeco(bool floating) const { #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WINCE) Q_UNUSED(floating); return false; #else return floating && item_list[QDockWidgetLayout::TitleBar] == 0; #endif } void QDockWidgetLayout::addItem(QLayoutItem*) { qWarning() << "QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()"; return; } QLayoutItem *QDockWidgetLayout::itemAt(int index) const { int cnt = 0; foreach (QLayoutItem *item, item_list) { if (item == 0) continue; if (index == cnt++) return item; } return 0; } QLayoutItem *QDockWidgetLayout::takeAt(int index) { int j = 0; for (int i = 0; i < item_list.count(); ++i) { QLayoutItem *item = item_list.at(i); if (item == 0) continue; if (index == j) { item_list[i] = 0; invalidate(); return item; } ++j; } return 0; } int QDockWidgetLayout::count() const { int result = 0; foreach (QLayoutItem *item, item_list) { if (item != 0) ++result; } return result; } QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const { QSize result = content; if (verticalTitleBar) { result.setHeight(qMax(result.height(), minimumTitleWidth())); result.setWidth(qMax(content.width(), 0)); } else { result.setHeight(qMax(result.height(), 0)); result.setWidth(qMax(content.width(), minimumTitleWidth())); } QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget()); const bool nativeDeco = nativeWindowDeco(floating); int fw = floating && !nativeDeco ? w->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, w) : 0; const int th = titleHeight(); if (!nativeDeco) { if (verticalTitleBar) result += QSize(th + 2*fw, 2*fw); else result += QSize(2*fw, th + 2*fw); } result.setHeight(qMin(result.height(), (int) QWIDGETSIZE_MAX)); result.setWidth(qMin(result.width(), (int) QWIDGETSIZE_MAX)); if (content.width() < 0) result.setWidth(-1); if (content.height() < 0) result.setHeight(-1); int left, top, right, bottom; w->getContentsMargins(&left, &top, &right, &bottom); //we need to substract the contents margin (it will be added by the caller) QSize min = w->minimumSize() - QSize(left + right, top + bottom); QSize max = w->maximumSize() - QSize(left + right, top + bottom); /* A floating dockwidget will automatically get its minimumSize set to the layout's minimum size + deco. We're *not* interested in this, we only take minimumSize() into account if the user set it herself. Otherwise we end up expanding the result of a calculation for a non-floating dock widget to a floating dock widget's minimum size + window decorations. */ uint explicitMin = 0; uint explicitMax = 0; if (w->d_func()->extra != 0) { explicitMin = w->d_func()->extra->explicitMinSize; explicitMax = w->d_func()->extra->explicitMaxSize; } if (!(explicitMin & Qt::Horizontal) || min.width() == 0) min.setWidth(-1); if (!(explicitMin & Qt::Vertical) || min.height() == 0) min.setHeight(-1); if (!(explicitMax & Qt::Horizontal)) max.setWidth(QWIDGETSIZE_MAX); if (!(explicitMax & Qt::Vertical)) max.setHeight(QWIDGETSIZE_MAX); return result.boundedTo(max).expandedTo(min); } QSize QDockWidgetLayout::sizeHint() const { QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget()); QSize content(-1, -1); if (item_list[Content] != 0) content = item_list[Content]->sizeHint(); return sizeFromContent(content, w->isFloating()); } QSize QDockWidgetLayout::maximumSize() const { if (item_list[Content] != 0) { const QSize content = item_list[Content]->maximumSize(); return sizeFromContent(content, parentWidget()->isWindow()); } else { return parentWidget()->maximumSize(); } } QSize QDockWidgetLayout::minimumSize() const { QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget()); QSize content(0, 0); if (item_list[Content] != 0) content = item_list[Content]->minimumSize(); return sizeFromContent(content, w->isFloating()); } QWidget *QDockWidgetLayout::widgetForRole(Role r) const { QLayoutItem *item = item_list.at(r); return item == 0 ? 0 : item->widget(); } QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const { return item_list.at(r); } void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w) { QWidget *old = widgetForRole(r); if (old != 0) { old->hide(); removeWidget(old); } if (w != 0) { addChildWidget(w); item_list[r] = new QWidgetItemV2(w); w->show(); } else { item_list[r] = 0; } invalidate(); } static inline int pick(bool vertical, const QSize &size) { return vertical ? size.height() : size.width(); } static inline int perp(bool vertical, const QSize &size) { return vertical ? size.width() : size.height(); } int QDockWidgetLayout::minimumTitleWidth() const { QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget()); if (QWidget *title = widgetForRole(TitleBar)) return pick(verticalTitleBar, title->minimumSizeHint()); QSize closeSize(0, 0); QSize floatSize(0, 0); if (hasFeature(q, QDockWidget::DockWidgetClosable)) { if (QLayoutItem *item = item_list[CloseButton]) closeSize = item->widget()->sizeHint(); } if (hasFeature(q, QDockWidget::DockWidgetFloatable)) { if (QLayoutItem *item = item_list[FloatButton]) floatSize = item->widget()->sizeHint(); } int titleHeight = this->titleHeight(); int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q); return pick(verticalTitleBar, closeSize) + pick(verticalTitleBar, floatSize) + titleHeight + 2*fw + 3*mw; } int QDockWidgetLayout::titleHeight() const { QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget()); if (QWidget *title = widgetForRole(TitleBar)) return perp(verticalTitleBar, title->sizeHint()); QSize closeSize(0, 0); QSize floatSize(0, 0); if (QLayoutItem *item = item_list[CloseButton]) closeSize = item->widget()->sizeHint(); if (QLayoutItem *item = item_list[FloatButton]) floatSize = item->widget()->sizeHint(); int buttonHeight = qMax(perp(verticalTitleBar, closeSize), perp(verticalTitleBar, floatSize)); QFontMetrics titleFontMetrics = q->fontMetrics(); #ifdef Q_WS_MAC if (qobject_cast<QMacStyle *>(q->style())) { //### this breaks on proxy styles. (But is this code still called?) QFont font = qt_app_fonts_hash()->value("QToolButton", q->font()); titleFontMetrics = QFontMetrics(font); } #endif int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); return qMax(buttonHeight + 2, titleFontMetrics.lineSpacing() + 2*mw); } void QDockWidgetLayout::setGeometry(const QRect &geometry) { QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget()); bool nativeDeco = nativeWindowDeco(); int fw = q->isFloating() && !nativeDeco ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; if (nativeDeco) { if (QLayoutItem *item = item_list[Content]) item->setGeometry(geometry); } else { int titleHeight = this->titleHeight(); if (verticalTitleBar) { _titleArea = QRect(QPoint(fw, fw), QSize(titleHeight, geometry.height() - (fw * 2))); } else { _titleArea = QRect(QPoint(fw, fw), QSize(geometry.width() - (fw * 2), titleHeight)); } if (QLayoutItem *item = item_list[TitleBar]) { item->setGeometry(_titleArea); } else { QStyleOptionDockWidgetV2 opt; q->initStyleOption(&opt); if (QLayoutItem *item = item_list[CloseButton]) { if (!item->isEmpty()) { QRect r = q->style() ->subElementRect(QStyle::SE_DockWidgetCloseButton, &opt, q); if (!r.isNull()) item->setGeometry(r); } } if (QLayoutItem *item = item_list[FloatButton]) { if (!item->isEmpty()) { QRect r = q->style() ->subElementRect(QStyle::SE_DockWidgetFloatButton, &opt, q); if (!r.isNull()) item->setGeometry(r); } } } if (QLayoutItem *item = item_list[Content]) { QRect r = geometry; if (verticalTitleBar) { r.setLeft(_titleArea.right() + 1); r.adjust(0, fw, -fw, -fw); } else { r.setTop(_titleArea.bottom() + 1); r.adjust(fw, 0, -fw, -fw); } item->setGeometry(r); } } } void QDockWidgetLayout::setVerticalTitleBar(bool b) { if (b == verticalTitleBar) return; verticalTitleBar = b; invalidate(); parentWidget()->update(); } /****************************************************************************** ** QDockWidgetItem */ QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget) : QWidgetItem(dockWidget) { } QSize QDockWidgetItem::minimumSize() const { QSize widgetMin(0, 0); if (QLayoutItem *item = dockWidgetChildItem()) widgetMin = item->minimumSize(); return dockWidgetLayout()->sizeFromContent(widgetMin, false); } QSize QDockWidgetItem::maximumSize() const { if (QLayoutItem *item = dockWidgetChildItem()) { return dockWidgetLayout()->sizeFromContent(item->maximumSize(), false); } else { return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); } } QSize QDockWidgetItem::sizeHint() const { if (QLayoutItem *item = dockWidgetChildItem()) { return dockWidgetLayout()->sizeFromContent(item->sizeHint(), false); } else { return QWidgetItem::sizeHint(); } } /****************************************************************************** ** QDockWidgetPrivate */ void QDockWidgetPrivate::init() { Q_Q(QDockWidget); QDockWidgetLayout *layout = new QDockWidgetLayout(q); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); QAbstractButton *button = new QDockWidgetTitleButton(q); button->setObjectName(QLatin1String("qt_dockwidget_floatbutton")); QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel())); layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button); button = new QDockWidgetTitleButton(q); button->setObjectName(QLatin1String("qt_dockwidget_closebutton")); QObject::connect(button, SIGNAL(clicked()), q, SLOT(close())); layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button); resizer = new QWidgetResizeHandler(q); resizer->setMovingEnabled(false); resizer->setActive(false); #ifndef QT_NO_ACTION toggleViewAction = new QAction(q); toggleViewAction->setCheckable(true); fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q); toggleViewAction->setText(fixedWindowTitle); QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(_q_toggleView(bool))); #endif updateButtons(); } /*! Initialize \a option with the values from this QDockWidget. This method is useful for subclasses when they need a QStyleOptionDockWidget, but don't want to fill in all the information themselves. \sa QStyleOption::initFrom() */ void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const { Q_D(const QDockWidget); if (!option) return; QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout()); option->initFrom(this); option->rect = dwlayout->titleArea(); option->title = d->fixedWindowTitle; option->closable = hasFeature(this, QDockWidget::DockWidgetClosable); option->movable = hasFeature(this, QDockWidget::DockWidgetMovable); option->floatable = hasFeature(this, QDockWidget::DockWidgetFloatable); QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(layout()); QStyleOptionDockWidgetV2 *v2 = qstyleoption_cast<QStyleOptionDockWidgetV2*>(option); if (v2 != 0) v2->verticalTitleBar = l->verticalTitleBar; } void QDockWidgetPrivate::_q_toggleView(bool b) { Q_Q(QDockWidget); if (b == q->isHidden()) { if (b) q->show(); else q->close(); } } void QDockWidgetPrivate::updateButtons() { Q_Q(QDockWidget); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout()); QStyleOptionDockWidget opt; q->initStyleOption(&opt); bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0; bool nativeDeco = layout->nativeWindowDeco(); bool hideButtons = nativeDeco || customTitleBar; bool canClose = hasFeature(q, QDockWidget::DockWidgetClosable); bool canFloat = hasFeature(q, QDockWidget::DockWidgetFloatable); QAbstractButton *button = qobject_cast<QAbstractButton*>(layout->widgetForRole(QDockWidgetLayout::FloatButton)); button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q)); button->setVisible(canFloat && !hideButtons); button = qobject_cast <QAbstractButton*>(layout->widgetForRole(QDockWidgetLayout::CloseButton)); button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q)); button->setVisible(canClose && !hideButtons); q->setAttribute(Qt::WA_ContentsPropagated, (canFloat || canClose) && !hideButtons); layout->invalidate(); } void QDockWidgetPrivate::_q_toggleTopLevel() { Q_Q(QDockWidget); q->setFloating(!q->isFloating()); } void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca) { Q_Q(QDockWidget); if (state != 0) return; QMainWindow *win = qobject_cast<QMainWindow*>(q->parentWidget()); Q_ASSERT(win != 0); QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout()); Q_ASSERT(layout != 0); if (layout->layoutState.indexOf(q).isEmpty()) //The dock widget has not been added into the main window return; if (layout->pluggingWidget != 0) // the main window is animating a docking operation return; state = new QDockWidgetPrivate::DragState; state->pressPos = pos; state->dragging = false; state->widgetItem = 0; state->ownWidgetItem = false; state->nca = nca; state->ctrlDrag = false; } void QDockWidgetPrivate::startDrag() { Q_Q(QDockWidget); if (state == 0 || state->dragging) return; QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); Q_ASSERT(layout != 0); state->widgetItem = layout->unplug(q); if (state->widgetItem == 0) { /* I have a QMainWindow parent, but I was never inserted with QMainWindow::addDockWidget, so the QMainWindowLayout has no widget item for me. :( I have to create it myself, and then delete it if I don't get dropped into a dock area. */ state->widgetItem = new QDockWidgetItem(q); state->ownWidgetItem = true; } if (state->ctrlDrag) layout->restore(); state->dragging = true; } void QDockWidgetPrivate::endDrag(bool abort) { Q_Q(QDockWidget); Q_ASSERT(state != 0); q->releaseMouse(); if (state->dragging) { QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); Q_ASSERT(layout != 0); if (abort || !layout->plug(state->widgetItem)) { if (hasFeature(q, QDockWidget::DockWidgetFloatable)) { if (state->ownWidgetItem) delete state->widgetItem; layout->restore(); #ifdef Q_WS_X11 // get rid of the X11BypassWindowManager window flag and activate the resizer Qt::WindowFlags flags = q->windowFlags(); flags &= ~Qt::X11BypassWindowManagerHint; q->setWindowFlags(flags); resizer->setActive(QWidgetResizeHandler::Resize, true); q->show(); #else QDockWidgetLayout *myLayout = qobject_cast<QDockWidgetLayout*>(q->layout()); resizer->setActive(QWidgetResizeHandler::Resize, myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0); #endif undockedGeometry = q->geometry(); q->activateWindow(); } else { layout->revert(state->widgetItem); } } } delete state; state = 0; } bool QDockWidgetPrivate::isAnimating() const { Q_Q(const QDockWidget); QMainWindow *mainWin = qobject_cast<QMainWindow*>(q->parentWidget()); if (mainWin == 0) return false; QMainWindowLayout *mainWinLayout = qobject_cast<QMainWindowLayout*>(mainWin->layout()); if (mainWinLayout == 0) return false; return (void*)mainWinLayout->pluggingWidget == (void*)q; } bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event) { #if !defined(QT_NO_MAINWINDOW) Q_Q(QDockWidget); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout()); if (!layout->nativeWindowDeco()) { QRect titleArea = layout->titleArea(); if (event->button() != Qt::LeftButton || !titleArea.contains(event->pos()) || // check if the tool window is movable... do nothing if it // is not (but allow moving if the window is floating) (!hasFeature(q, QDockWidget::DockWidgetMovable) && !q->isFloating()) || qobject_cast<QMainWindow*>(q->parentWidget()) == 0 || isAnimating() || state != 0) { return false; } initDrag(event->pos(), false); if (state) state->ctrlDrag = hasFeature(q, QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier; return true; } #endif // !defined(QT_NO_MAINWINDOW) return false; } bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event) { Q_Q(QDockWidget); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout()); if (!layout->nativeWindowDeco()) { QRect titleArea = layout->titleArea(); if (event->button() == Qt::LeftButton && titleArea.contains(event->pos()) && hasFeature(q, QDockWidget::DockWidgetFloatable)) { _q_toggleTopLevel(); return true; } } return false; } bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event) { bool ret = false; #if !defined(QT_NO_MAINWINDOW) Q_Q(QDockWidget); if (!state) return ret; QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(q->layout()); QMainWindowLayout *mwlayout = qobject_cast<QMainWindowLayout*>(q->parentWidget()->layout()); if (!dwlayout->nativeWindowDeco()) { if (!state->dragging && mwlayout->pluggingWidget == 0 && (event->pos() - state->pressPos).manhattanLength() > QApplication::startDragDistance()) { startDrag(); #ifdef Q_OS_WIN grabMouseWhileInWindow(); #else q->grabMouse(); #endif ret = true; } } if (state->dragging && !state->nca) { QPoint pos = event->globalPos() - state->pressPos; q->move(pos); if (!state->ctrlDrag) mwlayout->hover(state->widgetItem, event->globalPos()); ret = true; } #endif // !defined(QT_NO_MAINWINDOW) return ret; } bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event) { #if !defined(QT_NO_MAINWINDOW) if (event->button() == Qt::LeftButton && state && !state->nca) { endDrag(); return true; //filter out the event } #endif // !defined(QT_NO_MAINWINDOW) return false; } void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event) { Q_Q(QDockWidget); int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q); QRect geo = q->geometry(); QRect titleRect = q->frameGeometry(); #ifdef Q_WS_MAC if ((features & QDockWidget::DockWidgetVerticalTitleBar)) { titleRect.setTop(geo.top()); titleRect.setBottom(geo.bottom()); titleRect.setRight(geo.left() - 1); } else #endif { titleRect.setLeft(geo.left()); titleRect.setRight(geo.right()); titleRect.setBottom(geo.top() - 1); titleRect.adjust(0, fw, 0, 0); } switch (event->type()) { case QEvent::NonClientAreaMouseButtonPress: if (!titleRect.contains(event->globalPos())) break; if (state != 0) break; if (qobject_cast<QMainWindow*>(q->parentWidget()) == 0) break; if (isAnimating()) break; initDrag(event->pos(), true); if (state == 0) break; #ifdef Q_OS_WIN // On Windows, NCA mouse events don't contain modifier info state->ctrlDrag = GetKeyState(VK_CONTROL) & 0x8000; #else state->ctrlDrag = event->modifiers() & Qt::ControlModifier; #endif startDrag(); break; case QEvent::NonClientAreaMouseMove: if (state == 0 || !state->dragging) break; if (state->nca) { endDrag(); } #ifdef Q_OS_MAC else { // workaround for lack of mouse-grab on Mac QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); Q_ASSERT(layout != 0); q->move(event->globalPos() - state->pressPos); if (!state->ctrlDrag) layout->hover(state->widgetItem, event->globalPos()); } #endif break; case QEvent::NonClientAreaMouseButtonRelease: #ifdef Q_OS_MAC if (state) endDrag(); #endif break; case QEvent::NonClientAreaMouseButtonDblClick: _q_toggleTopLevel(); break; default: break; } } void QDockWidgetPrivate::moveEvent(QMoveEvent *event) { Q_Q(QDockWidget); if (state == 0 || !state->dragging || !state->nca || !q->isWindow()) return; // When the native window frame is being dragged, all we get is these mouse // move events. if (state->ctrlDrag) return; QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); Q_ASSERT(layout != 0); QPoint globalMousePos = event->pos() + state->pressPos; layout->hover(state->widgetItem, globalMousePos); } void QDockWidgetPrivate::unplug(const QRect &rect) { Q_Q(QDockWidget); QRect r = rect; r.moveTopLeft(q->mapToGlobal(QPoint(0, 0))); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout()); if (layout->nativeWindowDeco(true)) r.adjust(0, layout->titleHeight(), 0, 0); setWindowState(true, true, r); } void QDockWidgetPrivate::plug(const QRect &rect) { setWindowState(false, false, rect); } void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect &rect) { Q_Q(QDockWidget); bool wasFloating = q->isFloating(); bool hidden = q->isHidden(); if (q->isVisible()) q->hide(); Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget; QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout()); const bool nativeDeco = layout->nativeWindowDeco(floating); if (nativeDeco) { flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint; if (hasFeature(q, QDockWidget::DockWidgetClosable)) flags |= Qt::WindowCloseButtonHint; } else { flags |= Qt::FramelessWindowHint; } if (unplug) flags |= Qt::X11BypassWindowManagerHint; q->setWindowFlags(flags); #if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) if (floating && nativeDeco && (q->features() & QDockWidget::DockWidgetVerticalTitleBar)) { ChangeWindowAttributes(HIViewGetWindow(HIViewRef(q->winId())), kWindowSideTitlebarAttribute, 0); } #endif if (!rect.isNull()) q->setGeometry(rect); updateButtons(); if (!hidden) q->show(); if (floating != wasFloating) { emit q->topLevelChanged(floating); if (!floating && q->parentWidget()) { QMainWindowLayout *mwlayout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); if (mwlayout) emit q->dockLocationChanged(mwlayout->dockWidgetArea(q)); } } resizer->setActive(QWidgetResizeHandler::Resize, !unplug && floating && !nativeDeco); } /*! \class QDockWidget \brief The QDockWidget class provides a widget that can be docked inside a QMainWindow or floated as a top-level window on the desktop. \ingroup application QDockWidget provides the concept of dock widgets, also know as tool palettes or utility windows. Dock windows are secondary windows placed in the \e {dock widget area} around the \l{QMainWindow::centralWidget()}{central widget} in a QMainWindow. \image mainwindow-docks.png Dock windows can be moved inside their current area, moved into new areas and floated (e.g., undocked) by the end-user. The QDockWidget API allows the programmer to restrict the dock widgets ability to move, float and close, as well as the areas in which they can be placed. \section1 Appearance A QDockWidget consists of a title bar and the content area. The title bar displays the dock widgets \link QWidget::windowTitle() window title\endlink, a \e float button and a \e close button. Depending on the state of the QDockWidget, the \e float and \e close buttons may be either disabled or not shown at all. The visual appearance of the title bar and buttons is dependent on the \l{QStyle}{style} in use. A QDockWidget acts as a wrapper for its child widget, set with setWidget(). Custom size hints, minimum and maximum sizes and size policies should be implemented in the child widget. QDockWidget will respect them, adjusting its own constraints to include the frame and title. Size constraints should not be set on the QDockWidget itself, because they change depending on whether it is docked; a docked QDockWidget has no frame and a smaller title bar. \sa QMainWindow, {Dock Widgets Example} */ /*! \enum QDockWidget::DockWidgetFeature \value DockWidgetClosable The dock widget can be closed. On some systems the dock widget always has a close button when it's floating (for example on MacOS 10.5). \value DockWidgetMovable The dock widget can be moved between docks by the user. \value DockWidgetFloatable The dock widget can be detached from the main window, and floated as an independent window. \value DockWidgetVerticalTitleBar The dock widget displays a vertical title bar on its left side. This can be used to increase the amount of vertical space in a QMainWindow. \value AllDockWidgetFeatures (Deprecated) The dock widget can be closed, moved, and floated. Since new features might be added in future releases, the look and behavior of dock widgets might change if you use this flag. Please specify individual flags instead. \value NoDockWidgetFeatures The dock widget cannot be closed, moved, or floated. \omitvalue DockWidgetFeatureMask \omitvalue Reserved */ /*! \property QDockWidget::windowTitle \internal By default, this property contains an empty string. */ /*! Constructs a QDockWidget with parent \a parent and window flags \a flags. The dock widget will be placed in the left dock widget area. */ QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags) : QWidget(*new QDockWidgetPrivate, parent, flags) { Q_D(QDockWidget); d->init(); } /*! Constructs a QDockWidget with parent \a parent and window flags \a flags. The dock widget will be placed in the left dock widget area. The window title is set to \a title. This title is used when the QDockWidget is docked and undocked. It is also used in the context menu provided by QMainWindow. \sa setWindowTitle() */ QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags) : QWidget(*new QDockWidgetPrivate, parent, flags) { Q_D(QDockWidget); d->init(); setWindowTitle(title); } /*! Destroys the dock widget. */ QDockWidget::~QDockWidget() { } /*! Returns the widget for the dock widget. This function returns zero if the widget has not been set. \sa setWidget() */ QWidget *QDockWidget::widget() const { QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); return layout->widgetForRole(QDockWidgetLayout::Content); } /*! Sets the widget for the dock widget to \a widget. If the dock widget is visible when \a widget is added, you must \l{QWidget::}{show()} it explicitly. Note that you must add the layout of the \a widget before you call this function; if not, the \a widget will not be visible. \sa widget() */ void QDockWidget::setWidget(QWidget *widget) { QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); layout->setWidgetForRole(QDockWidgetLayout::Content, widget); } /*! \property QDockWidget::features \brief whether the dock widget is movable, closable, and floatable By default, this property is set to a combination of DockWidgetClosable, DockWidgetMovable and DockWidgetFloatable. \sa DockWidgetFeature */ void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features) { Q_D(QDockWidget); features &= DockWidgetFeatureMask; if (d->features == features) return; d->features = features; QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar); d->updateButtons(); d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable); emit featuresChanged(d->features); update(); } QDockWidget::DockWidgetFeatures QDockWidget::features() const { Q_D(const QDockWidget); return d->features; } /*! \property QDockWidget::floating \brief whether the dock widget is floating A floating dock widget is presented to the user as an independent window "on top" of its parent QMainWindow, instead of being docked in the QMainWindow. By default, this property is true. \sa isWindow() */ void QDockWidget::setFloating(bool floating) { Q_D(QDockWidget); // the initial click of a double-click may have started a drag... if (d->state != 0) d->endDrag(true); QRect r = d->undockedGeometry; d->setWindowState(floating, false, floating ? r : QRect()); if (floating && r.isNull()) { QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); QRect titleArea = layout->titleArea(); int h = layout->verticalTitleBar ? titleArea.width() : titleArea.height(); QPoint p = mapToGlobal(QPoint(h, h)); move(p); } } /*! \property QDockWidget::allowedAreas \brief areas where the dock widget may be placed The default is Qt::AllDockWidgetAreas. \sa Qt::DockWidgetArea */ void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas) { Q_D(QDockWidget); areas &= Qt::DockWidgetArea_Mask; if (areas == d->allowedAreas) return; d->allowedAreas = areas; emit allowedAreasChanged(d->allowedAreas); } Qt::DockWidgetAreas QDockWidget::allowedAreas() const { Q_D(const QDockWidget); return d->allowedAreas; } /*! \fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const Returns true if this dock widget can be placed in the given \a area; otherwise returns false. */ /*! \reimp */ void QDockWidget::changeEvent(QEvent *event) { Q_D(QDockWidget); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); switch (event->type()) { case QEvent::ModifiedChange: case QEvent::WindowTitleChange: update(layout->titleArea()); #ifndef QT_NO_ACTION d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this); d->toggleViewAction->setText(d->fixedWindowTitle); #endif #ifndef QT_NO_TABBAR { QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget()); if (QMainWindowLayout *winLayout = (win ? qobject_cast<QMainWindowLayout*>(win->layout()) : 0)) if (QDockAreaLayoutInfo *info = (winLayout ? winLayout->layoutState.dockAreaLayout.info(this) : 0)) info->updateTabBar(); } #endif // QT_NO_TABBAR break; default: break; } QWidget::changeEvent(event); } /*! \reimp */ void QDockWidget::closeEvent(QCloseEvent *event) { Q_D(QDockWidget); if (d->state) d->endDrag(true); QWidget::closeEvent(event); } /*! \reimp */ void QDockWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0; bool nativeDeco = layout->nativeWindowDeco(); if (!nativeDeco && !customTitleBar) { QStylePainter p(this); // ### Add PixelMetric to change spacers, so style may show border // when not floating. if (isFloating()) { QStyleOptionFrame framOpt; framOpt.init(this); p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt); } // Title must be painted after the frame, since the areas overlap, and // the title may wish to extend out to all sides (eg. XP style) QStyleOptionDockWidgetV2 titleOpt; initStyleOption(&titleOpt); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } } /*! \reimp */ bool QDockWidget::event(QEvent *event) { Q_D(QDockWidget); QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget()); QMainWindowLayout *layout = 0; if (win != 0) layout = qobject_cast<QMainWindowLayout*>(win->layout()); switch (event->type()) { #ifndef QT_NO_ACTION case QEvent::Hide: if (layout != 0) layout->keepSize(this); d->toggleViewAction->setChecked(false); emit visibilityChanged(false); break; case QEvent::Show: d->toggleViewAction->setChecked(true); emit visibilityChanged(true); break; #endif case QEvent::ApplicationLayoutDirectionChange: case QEvent::LayoutDirectionChange: case QEvent::StyleChange: case QEvent::ParentChange: d->updateButtons(); break; case QEvent::ZOrderChange: { bool onTop = false; if (win != 0) { const QObjectList &siblings = win->children(); onTop = siblings.count() > 0 && siblings.last() == (QObject*)this; } if (!isFloating() && layout != 0 && onTop) layout->raise(this); break; } case QEvent::WindowActivate: case QEvent::WindowDeactivate: update(qobject_cast<QDockWidgetLayout *>(this->layout())->titleArea()); break; case QEvent::ContextMenu: if (d->state) { event->accept(); return true; } break; // return true after calling the handler since we don't want // them to be passed onto the default handlers case QEvent::MouseButtonPress: if (d->mousePressEvent(static_cast<QMouseEvent *>(event))) return true; break; case QEvent::MouseButtonDblClick: if (d->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event))) return true; break; case QEvent::MouseMove: if (d->mouseMoveEvent(static_cast<QMouseEvent *>(event))) return true; break; #ifdef Q_OS_WIN case QEvent::Leave: if (d->state != 0 && d->state->dragging && !d->state->nca) { // This is a workaround for loosing the mouse on Vista. QPoint pos = QCursor::pos(); QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton, QApplication::mouseButtons(), QApplication::keyboardModifiers()); d->mouseMoveEvent(&fake); } break; #endif case QEvent::MouseButtonRelease: if (d->mouseReleaseEvent(static_cast<QMouseEvent *>(event))) return true; break; case QEvent::NonClientAreaMouseMove: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: d->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(event)); return true; case QEvent::Move: d->moveEvent(static_cast<QMoveEvent*>(event)); break; case QEvent::Resize: // if the mainwindow is plugging us, we don't want to update undocked geometry if (isFloating() && layout != 0 && layout->pluggingWidget != this) d->undockedGeometry = geometry(); break; default: break; } return QWidget::event(event); } #ifndef QT_NO_ACTION /*! Returns a checkable action that can be used to show or close this dock widget. The action's text is set to the dock widget's window title. \sa QAction::text QWidget::windowTitle */ QAction * QDockWidget::toggleViewAction() const { Q_D(const QDockWidget); return d->toggleViewAction; } #endif // QT_NO_ACTION /*! \fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features) This signal is emitted when the \l features property changes. The \a features parameter gives the new value of the property. */ /*! \fn void QDockWidget::topLevelChanged(bool topLevel) This signal is emitted when the \l floating property changes. The \a topLevel parameter is true if the dock widget is now floating; otherwise it is false. \sa isWindow() */ /*! \fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas) This signal is emitted when the \l allowedAreas property changes. The \a allowedAreas parameter gives the new value of the property. */ /*! \fn void QDockWidget::visibilityChanged(bool visible) \since 4.3 This signal is emitted when the dock widget becomes \a visible (or invisible). This happens when the widget is hidden or shown, as well as when it is docked in a tabbed dock area and its tab becomes selected or unselected. */ /*! \fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area) \since 4.3 This signal is emitted when the dock widget is moved to another dock \a area, or is moved to a different location in its current dock area. This happens when the dock widget is moved programmatically or is dragged to a new location by the user. */ /*! \since 4.3 Sets an arbitrary \a widget as the dock widget's title bar. If \a widget is 0, the title bar widget is removed, but not deleted. If a title bar widget is set, QDockWidget will not use native window decorations when it is floated. Here are some tips for implementing custom title bars: \list \i Mouse events that are not explicitly handled by the title bar widget must be ignored by calling QMouseEvent::ignore(). These events then propagate to the QDockWidget parent, which handles them in the usual manner, moving when the title bar is dragged, docking and undocking when it is double-clicked, etc. \i When DockWidgetVerticalTitleBar is set on QDockWidget, the title bar widget is repositioned accordingly. In resizeEvent(), the title bar should check what orientation it should assume: \snippet doc/src/snippets/code/src_gui_widgets_qdockwidget.cpp 0 \i The title bar widget must have a valid QWidget::sizeHint() and QWidget::minimumSizeHint(). These functions should take into account the current orientation of the title bar. \endlist Using qobject_cast as shown above, the title bar widget has full access to its parent QDockWidget. Hence it can perform such operations as docking and hiding in response to user actions. \sa titleBarWidget() DockWidgetVerticalTitleBar */ void QDockWidget::setTitleBarWidget(QWidget *widget) { Q_D(QDockWidget); QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); layout->setWidgetForRole(QDockWidgetLayout::TitleBar, widget); d->updateButtons(); if (isWindow()) { //this ensures the native decoration is drawn d->setWindowState(true /*floating*/, true /*unplug*/); } } /*! \since 4.3 Returns the custom title bar widget set on the QDockWidget, or 0 if no custom title bar has been set. \sa setTitleBarWidget() */ QWidget *QDockWidget::titleBarWidget() const { QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout()); return layout->widgetForRole(QDockWidgetLayout::TitleBar); } QT_END_NAMESPACE #include "qdockwidget.moc" #include "moc_qdockwidget.cpp" #endif // QT_NO_DOCKWIDGET